dolibarr 21.0.0-beta
commoninvoice.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
3 * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
4 * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
5 * Copyright (C) 2023 Nick Fragoulis
6 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
8 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
30require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
32
36abstract class CommonInvoice extends CommonObject
37{
38 use CommonIncoterm;
39
43 public $title;
44
48 public $type = self::TYPE_STANDARD;
49
53 public $subtype;
54
60 public $fk_soc;
64 public $socid;
65
69 public $paye;
70
76 public $date;
77
81 public $date_lim_reglement;
82
86 public $cond_reglement_id; // Id in llx_c_paiement
90 public $cond_reglement_code; // Code in llx_c_paiement
94 public $cond_reglement_label;
98 public $cond_reglement_doc;
99
103 public $mode_reglement_id;
107 public $mode_reglement_code;
108
112 public $mode_reglement;
113
117 public $revenuestamp;
118
122 public $totalpaid; // duplicate with sumpayed
126 public $totaldeposits; // duplicate with sumdeposit
130 public $totalcreditnotes; // duplicate with sumcreditnote
131
135 public $sumpayed;
139 public $sumpayed_multicurrency;
143 public $sumdeposit;
147 public $sumdeposit_multicurrency;
151 public $sumcreditnote;
155 public $sumcreditnote_multicurrency;
159 public $remaintopay;
160
164 public $stripechargedone;
165
169 public $stripechargeerror;
170
175 public $description;
176
182 public $ref_client;
183
187 public $situation_cycle_ref;
188
194 public $close_code;
195
200 public $close_note;
201
202
207 public $postactionmessages;
208
209
213 const TYPE_STANDARD = 0;
214
219
224
228 const TYPE_DEPOSIT = 3;
229
234 const TYPE_PROFORMA = 4;
235
239 const TYPE_SITUATION = 5;
240
244 const STATUS_DRAFT = 0;
245
250
258 const STATUS_CLOSED = 2;
259
268
269
270
278 public function getRemainToPay($multicurrency = 0)
279 {
280 $alreadypaid = 0.0;
281 $alreadypaid += $this->getSommePaiement($multicurrency);
282 $alreadypaid += $this->getSumDepositsUsed($multicurrency);
283 $alreadypaid += $this->getSumCreditNotesUsed($multicurrency);
284
285 $remaintopay = price2num($this->total_ttc - $alreadypaid, 'MT');
286 if ($this->status == self::STATUS_CLOSED && $this->close_code == 'discount_vat') { // If invoice closed with discount for anticipated payment
287 $remaintopay = 0.0;
288 }
289 return $remaintopay;
290 }
291
300 public function getSommePaiement($multicurrency = 0)
301 {
302 $table = 'paiement_facture';
303 $field = 'fk_facture';
304 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
305 $table = 'paiementfourn_facturefourn';
306 $field = 'fk_facturefourn';
307 }
308
309 $sql = "SELECT sum(amount) as amount, sum(multicurrency_amount) as multicurrency_amount";
310 $sql .= " FROM ".$this->db->prefix().$table;
311 $sql .= " WHERE ".$field." = ".((int) $this->id);
312
313 dol_syslog(get_class($this)."::getSommePaiement", LOG_DEBUG);
314
315 $resql = $this->db->query($sql);
316 if ($resql) {
317 $obj = $this->db->fetch_object($resql);
318
319 $this->db->free($resql);
320
321 if ($obj) {
322 if ($multicurrency < 0) {
323 $this->sumpayed = $obj->amount;
324 $this->sumpayed_multicurrency = $obj->multicurrency_amount;
325 return array('alreadypaid' => (float) $obj->amount, 'alreadypaid_multicurrency' => (float) $obj->multicurrency_amount);
326 } elseif ($multicurrency) {
327 $this->sumpayed_multicurrency = $obj->multicurrency_amount;
328 return (float) $obj->multicurrency_amount;
329 } else {
330 $this->sumpayed = $obj->amount;
331 return (float) $obj->amount;
332 }
333 } else {
334 return 0;
335 }
336 } else {
337 $this->error = $this->db->lasterror();
338 return -1;
339 }
340 }
341
351 public function getSumDepositsUsed($multicurrency = 0)
352 {
353 /*if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
354 // FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS was never supported for purchase invoice, so we can return 0 with no need of SQL for this case.
355 return 0.0;
356 }*/
357
358 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
359
360 $discountstatic = new DiscountAbsolute($this->db);
361 $result = $discountstatic->getSumDepositsUsed($this, $multicurrency);
362
363 if ($result >= 0) {
364 if ($multicurrency) {
365 $this->sumdeposit_multicurrency = $result;
366 } else {
367 $this->sumdeposit = $result;
368 }
369
370 return $result;
371 } else {
372 $this->error = $discountstatic->error;
373 return -1;
374 }
375 }
376
384 public function getSumCreditNotesUsed($multicurrency = 0)
385 {
386 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
387
388 $discountstatic = new DiscountAbsolute($this->db);
389 $result = $discountstatic->getSumCreditNotesUsed($this, $multicurrency);
390 if ($result >= 0) {
391 if ($multicurrency) {
392 $this->sumcreditnote_multicurrency = $result;
393 } else {
394 $this->sumcreditnote = $result;
395 }
396
397 return $result;
398 } else {
399 $this->error = $discountstatic->error;
400 return -1;
401 }
402 }
403
410 public function getSumFromThisCreditNotesNotUsed($multicurrency = 0)
411 {
412 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
413
414 $discountstatic = new DiscountAbsolute($this->db);
415 $result = $discountstatic->getSumFromThisCreditNotesNotUsed($this, $multicurrency);
416 if ($result >= 0) {
417 return $result;
418 } else {
419 $this->error = $discountstatic->error;
420 return -1;
421 }
422 }
423
430 {
431 $idarray = array();
432
433 $sql = "SELECT rowid";
434 $sql .= " FROM ".$this->db->prefix().$this->table_element;
435 $sql .= " WHERE fk_facture_source = ".((int) $this->id);
436 $sql .= " AND type = 2";
437 $resql = $this->db->query($sql);
438 if ($resql) {
439 $num = $this->db->num_rows($resql);
440 $i = 0;
441 while ($i < $num) {
442 $row = $this->db->fetch_row($resql);
443 $idarray[] = $row[0];
444 $i++;
445 }
446 } else {
447 dol_print_error($this->db);
448 }
449 return $idarray;
450 }
451
458 public function getIdReplacingInvoice($option = '')
459 {
460 $sql = "SELECT rowid";
461 $sql .= " FROM ".$this->db->prefix().$this->table_element;
462 $sql .= " WHERE fk_facture_source = ".((int) $this->id);
463 $sql .= " AND type < 2";
464 if ($option == 'validated') {
465 $sql .= ' AND fk_statut = 1';
466 }
467 // PROTECTION BAD DATA
468 // In case the database is corrupted and there is a valid replectement invoice
469 // and another no, priority is given to the valid one.
470 // Should not happen (unless concurrent access and 2 people have created a
471 // replacement invoice for the same invoice at the same time)
472 $sql .= " ORDER BY fk_statut DESC";
473
474 $resql = $this->db->query($sql);
475 if ($resql) {
476 $obj = $this->db->fetch_object($resql);
477 if ($obj) {
478 // If there is any
479 return $obj->rowid;
480 } else {
481 // If no invoice replaces it
482 return 0;
483 }
484 } else {
485 return -1;
486 }
487 }
488
499 public function getListOfPayments($filtertype = '', $multicurrency = 0, $mode = 0)
500 {
501 $retarray = array();
502 $this->error = ''; // By default no error, list can be empty.
503
504 $table = 'paiement_facture';
505 $table2 = 'paiement';
506 $field = 'fk_facture';
507 $field2 = 'fk_paiement';
508 $field3 = ', p.ref_ext';
509 $field4 = ', p.fk_bank'; // Bank line id
510 $sharedentity = 'facture';
511 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
512 $table = 'paiementfourn_facturefourn';
513 $table2 = 'paiementfourn';
514 $field = 'fk_facturefourn';
515 $field2 = 'fk_paiementfourn';
516 $field3 = '';
517 $sharedentity = 'facture_fourn';
518 }
519
520 // List of payments
521 if (empty($mode) || $mode == 1) {
522 $sql = "SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code".$field3 . $field4;
523 $sql .= " FROM ".$this->db->prefix().$table." as pf, ".$this->db->prefix().$table2." as p, ".$this->db->prefix()."c_paiement as t";
524 $sql .= " WHERE pf.".$field." = ".((int) $this->id);
525 $sql .= " AND pf.".$field2." = p.rowid";
526 $sql .= ' AND p.fk_paiement = t.id';
527 $sql .= ' AND p.entity IN ('.getEntity($sharedentity).')';
528 if ($filtertype) {
529 $sql .= " AND t.code='PRE'";
530 }
531
532 dol_syslog(get_class($this)."::getListOfPayments filtertype=".$filtertype." multicurrency=".$multicurrency." mode=".$mode, LOG_DEBUG);
533
534 $resql = $this->db->query($sql);
535 if ($resql) {
536 $num = $this->db->num_rows($resql);
537 $i = 0;
538 while ($i < $num) {
539 $obj = $this->db->fetch_object($resql);
540 if ($multicurrency) {
541 $tmp = array('amount' => $obj->multicurrency_amount, 'type' => $obj->code, 'typeline' => 'payment', 'date' => $obj->datep, 'num' => $obj->num, 'ref' => $obj->ref);
542 } else {
543 $tmp = array('amount' => $obj->amount, 'type' => $obj->code, 'typeline' => 'payment', 'date' => $obj->datep, 'num' => $obj->num, 'ref' => $obj->ref);
544 }
545 if (!empty($field3)) {
546 $tmp['ref_ext'] = $obj->ref_ext;
547 }
548 if (!empty($field4)) {
549 $tmp['fk_bank_line'] = $obj->fk_bank;
550 }
551 $retarray[] = $tmp;
552 $i++;
553 }
554 $this->db->free($resql);
555 } else {
556 $this->error = $this->db->lasterror();
557 dol_print_error($this->db);
558 return array();
559 }
560 }
561
562 // Look for credit notes and discounts and deposits
563 if (empty($mode) || $mode == 2) {
564 $sql = '';
565 if ($this->element == 'facture' || $this->element == 'invoice') {
566 $sql = "SELECT rc.amount_ttc as amount, rc.multicurrency_amount_ttc as multicurrency_amount, rc.datec as date, f.ref as ref, rc.description as type";
567 $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture as f';
568 $sql .= ' WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = '.((int) $this->id);
569 $sql .= ' AND (f.type = 2 OR f.type = 0 OR f.type = 3)'; // Find discount coming from credit note or excess received or deposits (payments from deposits are always null except if FACTURE_DEPOSITS_ARE_JUST_PAYMENTS is set)
570 } elseif ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
571 $sql = "SELECT rc.amount_ttc as amount, rc.multicurrency_amount_ttc as multicurrency_amount, rc.datec as date, f.ref as ref, rc.description as type";
572 $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture_fourn as f';
573 $sql .= ' WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = '.((int) $this->id);
574 $sql .= ' AND (f.type = 2 OR f.type = 0 OR f.type = 3)'; // Find discount coming from credit note or excess received or deposits (payments from deposits are always null except if FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS is set)
575 }
576
577 if ($sql) {
578 $resql = $this->db->query($sql);
579 if ($resql) {
580 $num = $this->db->num_rows($resql);
581 $i = 0;
582 while ($i < $num) {
583 $obj = $this->db->fetch_object($resql);
584 if ($multicurrency) {
585 $retarray[] = array('amount' => $obj->multicurrency_amount, 'type' => $obj->type, 'typeline' => 'discount', 'date' => $obj->date, 'num' => '0', 'ref' => $obj->ref);
586 } else {
587 $retarray[] = array('amount' => $obj->amount, 'type' => $obj->type, 'typeline' => 'discount', 'date' => $obj->date, 'num' => '', 'ref' => $obj->ref);
588 }
589 $i++;
590 }
591 } else {
592 $this->error = $this->db->lasterror();
593 dol_print_error($this->db);
594 return array();
595 }
596 $this->db->free($resql);
597 } else {
598 $this->error = $this->db->lasterror();
599 dol_print_error($this->db);
600 return array();
601 }
602 }
603
604 return $retarray;
605 }
606
607
608 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
622 public function is_erasable()
623 {
624 // phpcs:enable
625
626 // We check if invoice is a temporary number (PROVxxxx)
627 $tmppart = substr($this->ref, 1, 4);
628
629 if ($this->status == self::STATUS_DRAFT && $tmppart === 'PROV') { // If draft invoice and ref not yet defined
630 return 1;
631 }
632
633 if (getDolGlobalString('INVOICE_CAN_NEVER_BE_REMOVED')) {
634 return 0;
635 }
636
637 // If not a draft invoice and not temporary invoice
638 if ($tmppart !== 'PROV') {
639 $ventilExportCompta = $this->getVentilExportCompta();
640 if ($ventilExportCompta != 0) {
641 return -1;
642 }
643
644 // Get last number of validated invoice
645 if ($this->element != 'invoice_supplier') {
646 if (empty($this->thirdparty)) {
647 $this->fetch_thirdparty(); // We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag).
648 }
649 $maxref = $this->getNextNumRef($this->thirdparty, 'last');
650
651 // If there is no invoice into the reset range and not already dispatched, we can delete
652 // If invoice to delete is last one and not already dispatched, we can delete
653 if (!getDolGlobalString('INVOICE_CAN_ALWAYS_BE_REMOVED') && $maxref != '' && $maxref != $this->ref) {
654 return -2;
655 }
656
657 // TODO If there is payment in bookkeeping, check payment is not dispatched in accounting
658 // ...
659
660 if ($this->situation_cycle_ref && method_exists($this, 'is_last_in_cycle')) {
661 $last = $this->is_last_in_cycle();
662 if (!$last) {
663 return -3;
664 }
665 }
666 }
667 }
668
669 // Test if there is at least one payment. If yes, refuse to delete.
670 if (!getDolGlobalString('INVOICE_CAN_ALWAYS_BE_REMOVED') && $this->getSommePaiement() > 0) {
671 return -4;
672 }
673
674 return 2;
675 }
676
683 public function getVentilExportCompta()
684 {
685 $alreadydispatched = 0;
686
687 $type = 'customer_invoice';
688 if ($this->element == 'invoice_supplier') {
689 $type = 'supplier_invoice';
690 }
691
692 $sql = " SELECT COUNT(ab.rowid) as nb FROM ".$this->db->prefix()."accounting_bookkeeping as ab";
693 $sql .= " WHERE ab.doc_type='".$this->db->escape($type)."' AND ab.fk_doc = ".((int) $this->id);
694
695 $resql = $this->db->query($sql);
696 if ($resql) {
697 $obj = $this->db->fetch_object($resql);
698 if ($obj) {
699 $alreadydispatched = $obj->nb;
700 }
701 } else {
702 $this->error = $this->db->lasterror();
703 return -1;
704 }
705
706 if ($alreadydispatched) {
707 return 1;
708 }
709 return 0;
710 }
711
719 public function getNextNumRef($soc, $mode = 'next')
720 {
721 // TODO Must be implemented into main class
722 return '';
723 }
724
731 public function getLibType($withbadge = 0)
732 {
733 global $langs;
734
735 $labellong = "Unknown";
736 $labelshort = "Unknown";
737 if ($this->type == CommonInvoice::TYPE_STANDARD) {
738 $labellong = "InvoiceStandard";
739 $labelshort = "InvoiceStandardShort";
740 } elseif ($this->type == CommonInvoice::TYPE_REPLACEMENT) {
741 $labellong = "InvoiceReplacement";
742 $labelshort = "InvoiceReplacementShort";
743 } elseif ($this->type == CommonInvoice::TYPE_CREDIT_NOTE) {
744 $labellong = "InvoiceAvoir";
745 $labelshort = "CreditNote";
746 } elseif ($this->type == CommonInvoice::TYPE_DEPOSIT) {
747 $labellong = "InvoiceDeposit";
748 $labelshort = "Deposit";
749 } elseif ($this->type == CommonInvoice::TYPE_PROFORMA) { // @phan-suppress-current-line PhanDeprecatedClassConstant
750 $labellong = "InvoiceProForma"; // Not used.
751 $labelshort = "ProForma";
752 } elseif ($this->type == CommonInvoice::TYPE_SITUATION) {
753 $labellong = "InvoiceSituation";
754 $labelshort = "Situation";
755 }
756
757 $out = '';
758 if ($withbadge) {
759 $out .= '<span class="badgeneutral" title="'.dol_escape_htmltag($langs->trans($labellong)).'">';
760 }
761 $out .= $langs->trans($withbadge == 2 ? $labelshort : $labellong);
762 if ($withbadge) {
763 $out .= '</span>';
764 }
765 return $out;
766 }
767
774 public function getSubtypeLabel($table = '')
775 {
776 $subtypeLabel = '';
777 if ($table === 'facture' || $table === 'facture_fourn') {
778 $sql = "SELECT s.label FROM " . $this->db->prefix() . $table . " AS f";
779 $sql .= " INNER JOIN " . $this->db->prefix() . "c_invoice_subtype AS s ON f.subtype = s.rowid";
780 $sql .= " WHERE f.ref = '".$this->db->escape($this->ref)."'";
781 } elseif ($table === 'facture_rec' || $table === 'facture_fourn_rec') {
782 $sql = "SELECT s.label FROM " . $this->db->prefix() . $table . " AS f";
783 $sql .= " INNER JOIN " . $this->db->prefix() . "c_invoice_subtype AS s ON f.subtype = s.rowid";
784 $sql .= " WHERE f.titre = '".$this->db->escape($this->title)."'";
785 } else {
786 return -1;
787 }
788
789 $resql = $this->db->query($sql);
790 if ($resql) {
791 while ($obj = $this->db->fetch_object($resql)) {
792 $subtypeLabel = $obj->label;
793 }
794 } else {
795 dol_print_error($this->db);
796 return -1;
797 }
798
799 return $subtypeLabel;
800 }
801
808 public function getArrayOfInvoiceSubtypes($mode = 0)
809 {
810 global $mysoc;
811
812 $effs = array();
813
814 $sql = "SELECT rowid, code, label as label";
815 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
816 $sql .= " WHERE active = 1 AND fk_country = ".((int) $mysoc->country_id)." AND entity IN(".getEntity('c_invoice_subtype').")";
817 $sql .= " ORDER by rowid, code";
818
819 dol_syslog(get_class($this) . '::getArrayOfInvoiceSubtypes', LOG_DEBUG);
820 $resql = $this->db->query($sql);
821 if ($resql) {
822 $num = $this->db->num_rows($resql);
823 $i = 0;
824
825 while ($i < $num) {
826 $objp = $this->db->fetch_object($resql);
827 if (!$mode) {
828 $key = $objp->rowid;
829 $effs[$key] = $objp->label;
830 } else {
831 $key = $objp->code;
832 $effs[$key] = $objp->rowid;
833 }
834
835 $i++;
836 }
837 $this->db->free($resql);
838 }
839
840 return $effs;
841 }
842
850 public function getLibStatut($mode = 0, $alreadypaid = -1)
851 {
852 return $this->LibStatut($this->paye, $this->status, $mode, $alreadypaid, $this->type);
853 }
854
855 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
866 public function LibStatut($paye, $status, $mode = 0, $alreadypaid = -1, $type = -1)
867 {
868 // phpcs:enable
869 global $langs, $hookmanager;
870 $langs->load('bills');
871
872 if ($type == -1) {
873 $type = $this->type;
874 }
875
876 $statusType = 'status0';
877 $prefix = 'Short';
878 if (!$paye) {
879 if ($status == 0) {
880 $labelStatus = $langs->transnoentitiesnoconv('BillStatusDraft');
881 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusDraft');
882 } elseif (($status == 3 || $status == 2) && $alreadypaid <= 0) {
883 if ($status == 3) {
884 $labelStatus = $langs->transnoentitiesnoconv('BillStatusCanceled');
885 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusCanceled');
886 } else {
887 $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedUnpaid');
888 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedUnpaid');
889 }
890 $statusType = 'status5';
891 } elseif (($status == 3 || $status == 2) && $alreadypaid > 0) {
892 $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedPaidPartially');
893 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedPaidPartially');
894 $statusType = 'status9';
895 } elseif ($alreadypaid == 0) {
896 $labelStatus = $langs->transnoentitiesnoconv('BillStatusNotPaid');
897 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusNotPaid');
898 $statusType = 'status1';
899 } else {
900 $labelStatus = $langs->transnoentitiesnoconv('BillStatusStarted');
901 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusStarted');
902 $statusType = 'status3';
903 }
904 } else {
905 $statusType = 'status6';
906
907 if ($type == self::TYPE_CREDIT_NOTE) {
908 $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaidBackOrConverted'); // credit note
909 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaidBackOrConverted'); // credit note
910 } elseif ($type == self::TYPE_DEPOSIT) {
911 $labelStatus = $langs->transnoentitiesnoconv('BillStatusConverted'); // deposit invoice
912 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusConverted'); // deposit invoice
913 } else {
914 $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaid');
915 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaid');
916 }
917 }
918
919 $parameters = array(
920 'status' => $status,
921 'mode' => $mode,
922 'paye' => $paye,
923 'alreadypaid' => $alreadypaid,
924 'type' => $type
925 );
926
927 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
928
929 if ($reshook > 0) {
930 return $hookmanager->resPrint;
931 }
932
933
934
935 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
936 }
937
938 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
946 public function calculate_date_lim_reglement($cond_reglement = 0)
947 {
948 // phpcs:enable
949 if (!$cond_reglement) {
950 $cond_reglement = $this->cond_reglement_code;
951 }
952 if (!$cond_reglement) {
953 $cond_reglement = $this->cond_reglement_id;
954 }
955 if (!$cond_reglement) {
956 return $this->date;
957 }
958
959 $cdr_nbjour = 0;
960 $cdr_type = 0;
961 $cdr_decalage = 0;
962
963 $sqltemp = "SELECT c.type_cdr, c.nbjour, c.decalage";
964 $sqltemp .= " FROM ".$this->db->prefix()."c_payment_term as c";
965 if (is_numeric($cond_reglement)) {
966 $sqltemp .= " WHERE c.rowid=".((int) $cond_reglement);
967 } else {
968 $sqltemp .= " WHERE c.entity IN (".getEntity('c_payment_term').")";
969 $sqltemp .= " AND c.code = '".$this->db->escape($cond_reglement)."'";
970 }
971
972 dol_syslog(get_class($this).'::calculate_date_lim_reglement', LOG_DEBUG);
973 $resqltemp = $this->db->query($sqltemp);
974 if ($resqltemp) {
975 if ($this->db->num_rows($resqltemp)) {
976 $obj = $this->db->fetch_object($resqltemp);
977 $cdr_nbjour = $obj->nbjour;
978 $cdr_type = $obj->type_cdr;
979 $cdr_decalage = $obj->decalage;
980 }
981 } else {
982 $this->error = $this->db->error();
983 return -1;
984 }
985 $this->db->free($resqltemp);
986
987 /* Definition de la date limit */
988
989 // 0 : adding the number of days
990 if ($cdr_type == 0) {
991 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
992
993 $datelim += ($cdr_decalage * 3600 * 24);
994 } elseif ($cdr_type == 1) {
995 // 1 : application of the "end of the month" rule
996 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
997
998 $mois = date('m', $datelim);
999 $annee = date('Y', $datelim);
1000 if ($mois == 12) {
1001 $mois = 1;
1002 $annee += 1;
1003 } else {
1004 $mois += 1;
1005 }
1006 // We move at the beginning of the next month, and we take a day off
1007 $datelim = dol_mktime(12, 0, 0, $mois, 1, $annee);
1008 $datelim -= (3600 * 24);
1009
1010 $datelim += ($cdr_decalage * 3600 * 24);
1011 } elseif ($cdr_type == 2 && !empty($cdr_decalage)) {
1012 // 2 : application of the rule, the N of the current or next month
1013 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1014 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
1015
1016 $date_piece = dol_mktime(0, 0, 0, (int) date('m', $datelim), (int) date('d', $datelim), (int) date('Y', $datelim)); // Sans les heures minutes et secondes
1017 $date_lim_current = dol_mktime(0, 0, 0, (int) date('m', $datelim), (int) $cdr_decalage, (int) date('Y', $datelim)); // Sans les heures minutes et secondes
1018 $date_lim_next = dol_time_plus_duree((int) $date_lim_current, 1, 'm'); // Add 1 month
1019
1020 $diff = $date_piece - $date_lim_current;
1021
1022 if ($diff < 0) {
1023 $datelim = $date_lim_current;
1024 } else {
1025 $datelim = $date_lim_next;
1026 }
1027 } else {
1028 return 'Bad value for type_cdr in database for record cond_reglement = '.$cond_reglement;
1029 }
1030
1031 return $datelim;
1032 }
1033
1034 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1047 public function demande_prelevement(User $fuser, float $amount = 0, string $type = 'direct-debit', string $sourcetype = 'facture', int $checkduplicateamongall = 0, int $ribId = 0)
1048 {
1049 // phpcs:enable
1050 global $conf;
1051
1052 $error = 0;
1053
1054 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
1055
1056 if ($this->status > self::STATUS_DRAFT && $this->paye == 0) {
1057 require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
1058 $bac = new CompanyBankAccount($this->db);
1059 $bac->fetch($ribId, '', $this->socid);
1060
1061 $sql = "SELECT count(rowid) as nb";
1062 $sql .= " FROM ".$this->db->prefix()."prelevement_demande";
1063 if ($type == 'bank-transfer') {
1064 $sql .= " WHERE fk_facture_fourn = ".((int) $this->id);
1065 } else {
1066 $sql .= " WHERE fk_facture = ".((int) $this->id);
1067 }
1068 $sql .= " AND type = 'ban'"; // To exclude record done for some online payments
1069 if (empty($checkduplicateamongall)) {
1070 $sql .= " AND traite = 0";
1071 }
1072
1073 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
1074
1075 $resql = $this->db->query($sql);
1076 if ($resql) {
1077 $obj = $this->db->fetch_object($resql);
1078 if ($obj && $obj->nb == 0) { // If no request found yet
1079 $now = dol_now();
1080
1081 $totalpaid = $this->getSommePaiement();
1082 $totalcreditnotes = $this->getSumCreditNotesUsed();
1083 $totaldeposits = $this->getSumDepositsUsed();
1084 //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits;
1085
1086 // We can also use bcadd to avoid pb with floating points
1087 // For example print 239.2 - 229.3 - 9.9; does not return 0.
1088 //$resteapayer=bcadd($this->total_ttc,$totalpaid,$conf->global->MAIN_MAX_DECIMALS_TOT);
1089 //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
1090 if (empty($amount)) {
1091 $amount = price2num($this->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1092 }
1093
1094 if (is_numeric($amount) && $amount != 0) {
1095 $sql = 'INSERT INTO '.$this->db->prefix().'prelevement_demande(';
1096 if ($type == 'bank-transfer') {
1097 $sql .= 'fk_facture_fourn, ';
1098 } else {
1099 $sql .= 'fk_facture, ';
1100 }
1101 $sql .= ' amount, date_demande, fk_user_demande, code_banque, code_guichet, number, cle_rib, sourcetype, type, entity';
1102 if (empty($bac->id)) {
1103 $sql .= ')';
1104 } else {
1105 $sql .= ', fk_societe_rib)';
1106 }
1107 $sql .= " VALUES (".((int) $this->id);
1108 $sql .= ", ".((float) price2num($amount));
1109 $sql .= ", '".$this->db->idate($now)."'";
1110 $sql .= ", ".((int) $fuser->id);
1111 $sql .= ", '".$this->db->escape($bac->code_banque)."'";
1112 $sql .= ", '".$this->db->escape($bac->code_guichet)."'";
1113 $sql .= ", '".$this->db->escape($bac->number)."'";
1114 $sql .= ", '".$this->db->escape($bac->cle_rib)."'";
1115 $sql .= ", '".$this->db->escape($sourcetype)."'";
1116 $sql .= ", 'ban'";
1117 $sql .= ", ".((int) $conf->entity);
1118 if (!empty($bac->id)) {
1119 $sql .= ", '".$this->db->escape($bac->id)."'";
1120 }
1121 $sql .= ")";
1122
1123 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
1124 $resql = $this->db->query($sql);
1125 if (!$resql) {
1126 $this->error = $this->db->lasterror();
1127 dol_syslog(get_class($this).'::demandeprelevement Erreur');
1128 $error++;
1129 }
1130 } else {
1131 $this->error = 'WithdrawRequestErrorNilAmount';
1132 dol_syslog(get_class($this).'::demandeprelevement WithdrawRequestErrorNilAmount');
1133 $error++;
1134 }
1135
1136 if (!$error) {
1137 // Force payment mode of invoice to withdraw
1138 $payment_mode_id = dol_getIdFromCode($this->db, ($type == 'bank-transfer' ? 'VIR' : 'PRE'), 'c_paiement', 'code', 'id', 1);
1139 if ($payment_mode_id > 0) {
1140 $result = $this->setPaymentMethods($payment_mode_id);
1141 }
1142 }
1143
1144 if ($error) {
1145 return -1;
1146 }
1147 return 1;
1148 } else {
1149 $this->error = "A request already exists";
1150 dol_syslog(get_class($this).'::demandeprelevement Can t create a request to generate a direct debit, a request already exists.');
1151 return 0;
1152 }
1153 } else {
1154 $this->error = $this->db->error();
1155 dol_syslog(get_class($this).'::demandeprelevement Error -2');
1156 return -2;
1157 }
1158 } else {
1159 $this->error = "Status of invoice does not allow this";
1160 dol_syslog(get_class($this)."::demandeprelevement ".$this->error." $this->status, $this->paye, $this->mode_reglement_id");
1161 return -3;
1162 }
1163 }
1164
1165
1177 public function makeStripeCardRequest($fuser, $id, $sourcetype = 'facture')
1178 {
1179 // TODO See in sellyoursaas
1180 return 0;
1181 }
1182
1195 public function makeStripeSepaRequest($fuser, $did, $type = 'direct-debit', $sourcetype = 'facture', $service = '', $forcestripe = '')
1196 {
1197 global $conf, $user, $langs;
1198
1199 if ($type != 'bank-transfer' && $type != 'credit-transfer' && !getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) {
1200 return 0;
1201 }
1202 if ($type != 'direct-debit' && !getDolGlobalString('STRIPE_SEPA_CREDIT_TRANSFER')) {
1203 return 0;
1204 }
1205 // Set a default value for service if not provided
1206 if (empty($service)) {
1207 $service = 'StripeTest';
1208 if (getDolGlobalString('STRIPE_LIVE') && !GETPOST('forcesandbox', 'alpha')) {
1209 $service = 'StripeLive';
1210 }
1211 }
1212
1213 $error = 0;
1214
1215 dol_syslog(get_class($this)."::makeStripeSepaRequest start did=".$did." type=".$type." service=".$service." sourcetype=".$sourcetype." forcestripe=".$forcestripe, LOG_DEBUG);
1216
1217 if ($this->status > self::STATUS_DRAFT && $this->paye == 0) {
1218 // Get the default payment mode for BAN payment of the third party
1219 require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
1220 $bac = new CompanyBankAccount($this->db); // Table societe_rib
1221 $result = $bac->fetch(0, '', $this->socid, 1, 'ban');
1222 if ($result <= 0 || empty($bac->id)) {
1223 $this->error = $langs->trans("ThirdpartyHasNoDefaultBanAccount");
1224 $this->errors[] = $this->error;
1225 dol_syslog(get_class($this)."::makeStripeSepaRequest ".$this->error);
1226 return -1;
1227 }
1228
1229 // Load the pending payment request to process (with rowid=$did)
1230 $sql = "SELECT rowid, date_demande, amount, fk_facture, fk_facture_fourn, fk_salary, fk_prelevement_bons";
1231 $sql .= " FROM ".$this->db->prefix()."prelevement_demande";
1232 $sql .= " WHERE rowid = ".((int) $did);
1233 if ($type != 'bank-transfer' && $type != 'credit-transfer') {
1234 $sql .= " AND fk_facture = ".((int) $this->id); // Add a protection to not pay another invoice than current one
1235 }
1236 if ($type != 'direct-debit') {
1237 if ($sourcetype == 'salary') {
1238 $sql .= " AND fk_salary = ".((int) $this->id); // Add a protection to not pay another salary than current one
1239 } else {
1240 $sql .= " AND fk_facture_fourn = ".((int) $this->id); // Add a protection to not pay another invoice than current one
1241 }
1242 }
1243 $sql .= " AND traite = 0"; // To not process payment request that were already converted into a direct debit or credit transfer order (Note: fk_prelevement_bons is also empty when traite = 0)
1244
1245 dol_syslog(get_class($this)."::makeStripeSepaRequest load requests to process", LOG_DEBUG);
1246 $resql = $this->db->query($sql);
1247 if ($resql) {
1248 $obj = $this->db->fetch_object($resql);
1249 if (!$obj) {
1250 dol_print_error($this->db, 'CantFindRequestWithId');
1251 return -2;
1252 }
1253
1254 // amount to pay
1255 $amount = $obj->amount;
1256
1257 if (is_numeric($amount) && $amount != 0) {
1258 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
1259 $companypaymentmode = new CompanyPaymentMode($this->db); // table societe_rib
1260 $companypaymentmode->fetch($bac->id);
1261
1262 $this->stripechargedone = 0;
1263 $this->stripechargeerror = 0;
1264
1265 $now = dol_now();
1266
1267 $currency = $conf->currency;
1268
1269 $errorforinvoice = 0; // We reset the $errorforinvoice at each invoice loop
1270
1271 $this->fetch_thirdparty();
1272
1273 dol_syslog("makeStripeSepaRequest Process payment request amount=".$amount." thirdparty_id=" . $this->thirdparty->id . ", thirdparty_name=" . $this->thirdparty->name . " ban id=" . $bac->id, LOG_DEBUG);
1274
1275 //$alreadypayed = $this->getSommePaiement();
1276 //$amount_credit_notes_included = $this->getSumCreditNotesUsed();
1277 //$amounttopay = $this->total_ttc - $alreadypayed - $amount_credit_notes_included;
1278 $amounttopay = $amount;
1279
1280 // Correct the amount according to unit of currency
1281 // See https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support
1282 $arrayzerounitcurrency = ['BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'];
1283 $amountstripe = $amounttopay;
1284 if (!in_array($currency, $arrayzerounitcurrency)) {
1285 $amountstripe *= 100;
1286 }
1287
1288 $fk_bank_account = getDolGlobalInt('STRIPE_BANK_ACCOUNT_FOR_PAYMENTS'); // Bank account used for SEPA direct debit or credit transfer. Must be the Stripe account in Dolibarr.
1289 if (!($fk_bank_account > 0)) {
1290 $error++;
1291 $errorforinvoice++;
1292 dol_syslog("makeStripeSepaRequest Error no bank account defined for Stripe payments", LOG_ERR);
1293 $this->errors[] = "Error bank account for Stripe payments not defined into Stripe module";
1294 }
1295
1296 $this->db->begin();
1297
1298 // Create a prelevement_bon
1299 require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php';
1300 $bon = new BonPrelevement($this->db);
1301 if (!$error) {
1302 if (empty($obj->fk_prelevement_bons)) {
1303 // This creates a record into llx_prelevement_bons and updates link with llx_prelevement_demande
1304 $nbinvoices = $bon->create(0, 0, 'real', 'ALL', 0, 0, $type, $did, $fk_bank_account);
1305 if ($nbinvoices <= 0) {
1306 $error++;
1307 $errorforinvoice++;
1308 dol_syslog("makeStripeSepaRequest Error on BonPrelevement creation", LOG_ERR);
1309 $this->errors[] = "Error on BonPrelevement creation";
1310 }
1311 /*
1312 if (!$error) {
1313 // Update the direct debit payment request of the processed request to save the id of the prelevement_bon
1314 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
1315 $sql .= " fk_prelevement_bons = ".((int) $bon->id);
1316 $sql .= " WHERE rowid = ".((int) $did);
1317
1318 $result = $this->db->query($sql);
1319 if ($result < 0) {
1320 $error++;
1321 $this->errors[] = "Error on updating fk_prelevement_bons to ".$bon->id;
1322 }
1323 }
1324 */
1325 } else {
1326 $error++;
1327 $errorforinvoice++;
1328 dol_syslog("makeStripeSepaRequest Error Line already part of a bank payment order", LOG_ERR);
1329 $this->errors[] = "The line is already included into a bank payment order. Delete the bank payment order first.";
1330 }
1331 }
1332
1333 $paymentintent = null;
1334 if (!$error) {
1335 if ($amountstripe > 0) {
1336 try {
1337 global $savstripearrayofkeysbyenv;
1338 global $stripearrayofkeysbyenv;
1339 $servicestatus = 0;
1340 if ($service == 'StripeLive') {
1341 $servicestatus = 1;
1342 }
1343
1344 //var_dump($companypaymentmode);
1345 dol_syslog("makeStripeSepaRequest We will try to pay with companypaymentmodeid=" . $companypaymentmode->id . " stripe_card_ref=" . $companypaymentmode->stripe_card_ref . " mode=" . $companypaymentmode->status, LOG_DEBUG);
1346
1347 $thirdparty = new Societe($this->db);
1348 $resultthirdparty = $thirdparty->fetch($this->socid);
1349
1350 include_once DOL_DOCUMENT_ROOT . '/stripe/class/stripe.class.php'; // This include the include of htdocs/stripe/config.php
1351 // So it inits or erases the $stripearrayofkeysbyenv
1352 $stripe = new Stripe($this->db);
1353
1354 if (empty($savstripearrayofkeysbyenv)) {
1355 $savstripearrayofkeysbyenv = $stripearrayofkeysbyenv;
1356 }
1357 dol_syslog("makeStripeSepaRequest Current Stripe environment is " . $stripearrayofkeysbyenv[$servicestatus]['publishable_key']);
1358 dol_syslog("makeStripeSepaRequest Current Saved Stripe environment is ".$savstripearrayofkeysbyenv[$servicestatus]['publishable_key']);
1359
1360 $foundalternativestripeaccount = '';
1361
1362 // Force stripe to another value (by default this value is empty)
1363 if (! empty($forcestripe)) {
1364 dol_syslog("makeStripeSepaRequest A dedicated stripe account was forced, so we switch to it.");
1365
1366 $tmparray = explode('@', $forcestripe);
1367 if (! empty($tmparray[1])) {
1368 $tmparray2 = explode(':', $tmparray[1]);
1369 if (! empty($tmparray2[1])) {
1370 $stripearrayofkeysbyenv[$servicestatus]["publishable_key"] = $tmparray2[0];
1371 $stripearrayofkeysbyenv[$servicestatus]["secret_key"] = $tmparray2[1];
1372
1373 $stripearrayofkeys = $stripearrayofkeysbyenv[$servicestatus];
1374 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1375
1376 $foundalternativestripeaccount = $tmparray[0]; // Store the customer id
1377
1378 dol_syslog("makeStripeSepaRequest We use now customer=".$foundalternativestripeaccount." publishable_key=".$stripearrayofkeys['publishable_key'], LOG_DEBUG);
1379 }
1380 }
1381
1382 if (! $foundalternativestripeaccount) {
1383 $stripearrayofkeysbyenv = $savstripearrayofkeysbyenv;
1384
1385 $stripearrayofkeys = $savstripearrayofkeysbyenv[$servicestatus];
1386 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1387 dol_syslog("makeStripeSepaRequest We found a bad value for Stripe Account for thirdparty id=".$thirdparty->id.", so we ignore it and keep using the global one, so ".$stripearrayofkeys['publishable_key'], LOG_WARNING);
1388 }
1389 } else {
1390 $stripearrayofkeysbyenv = $savstripearrayofkeysbyenv;
1391
1392 $stripearrayofkeys = $savstripearrayofkeysbyenv[$servicestatus];
1393 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1394 dol_syslog("makeStripeSepaRequest No dedicated Stripe Account requested, so we use global one, so ".$stripearrayofkeys['publishable_key'], LOG_DEBUG);
1395 }
1396
1397 $stripeacc = $stripe->getStripeAccount($service, $this->socid); // Get Stripe OAuth connect account if it exists (no network access here)
1398
1399 if ($foundalternativestripeaccount) {
1400 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
1401 $customer = \Stripe\Customer::retrieve(array('id' => "$foundalternativestripeaccount", 'expand[]' => 'sources'));
1402 } else {
1403 $customer = \Stripe\Customer::retrieve(array('id' => "$foundalternativestripeaccount", 'expand[]' => 'sources'), array("stripe_account" => $stripeacc));
1404 }
1405 } else {
1406 $customer = $stripe->customerStripe($thirdparty, $stripeacc, $servicestatus, 0);
1407 if (empty($customer) && ! empty($stripe->error)) {
1408 $this->errors[] = $stripe->error;
1409 }
1410 /*if (!empty($customer) && empty($customer->sources)) {
1411 $customer = null;
1412 $this->errors[] = '\Stripe\Customer::retrieve did not returned the sources';
1413 }*/
1414 }
1415
1416 // $nbhoursbetweentries = (empty($conf->global->SELLYOURSAAS_NBHOURSBETWEENTRIES) ? 49 : $conf->global->SELLYOURSAAS_NBHOURSBETWEENTRIES); // Must have more that 48 hours + 1 between each try (so 1 try every 3 daily batch)
1417 // $nbdaysbeforeendoftries = (empty($conf->global->SELLYOURSAAS_NBDAYSBEFOREENDOFTRIES) ? 35 : $conf->global->SELLYOURSAAS_NBDAYSBEFOREENDOFTRIES);
1418 $postactionmessages = [];
1419
1420 if ($resultthirdparty > 0 && !empty($customer)) {
1421 if (!$error) { // Payment was not canceled
1422 $stripecard = null;
1423 if ($companypaymentmode->type == 'ban') {
1424 // Check into societe_rib if a payment mode for Stripe and ban payment exists
1425 // To make a Stripe SEPA payment request, we must have the payment mode source already saved into societe_rib and retrieved with ->sepaStripe
1426 // The payment mode source is created when we create the bank account on Stripe with paymentmodes.php?action=create
1427 $stripecard = $stripe->sepaStripe($customer, $companypaymentmode, $stripeacc, $servicestatus, 0);
1428 } else {
1429 $error++;
1430 $this->error = 'The payment mode type is not "ban"';
1431 }
1432
1433 if ($stripecard) { // Can be src_... (for sepa) or pm_... (new card mode). Note that card_... (old card mode) should not happen here.
1434 $FULLTAG = 'DID='.$did.'-INV=' . $this->id . '-CUS=' . $thirdparty->id;
1435 $description = 'Stripe payment from makeStripeSepaRequest: ' . $FULLTAG . ' did='.$did.' ref=' . $this->ref;
1436
1437 $stripefailurecode = '';
1438 $stripefailuremessage = '';
1439 $stripefailuredeclinecode = '';
1440
1441 // Using new SCA method
1442 dol_syslog("* Create payment on SEPA " . $stripecard->id . ", amounttopay=" . $amounttopay . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG);
1443
1444 // Create payment intent and charge payment (confirmnow = true)
1445 $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $this, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1, 1, $did);
1446
1447 $charge = new stdClass();
1448
1449 if ($paymentintent->status === 'succeeded' || $paymentintent->status === 'processing') {
1450 $charge->status = 'ok';
1451 $charge->id = $paymentintent->id;
1452 $charge->customer = $customer->id;
1453 } elseif ($paymentintent->status === 'requires_action') {
1454 //paymentintent->status may be => 'requires_action' (no error in such a case)
1455 dol_syslog(var_export($paymentintent, true), LOG_DEBUG);
1456
1457 $charge->status = 'failed';
1458 $charge->customer = $customer->id;
1459 $charge->failure_code = $stripe->code;
1460 $charge->failure_message = $stripe->error;
1461 $charge->failure_declinecode = $stripe->declinecode;
1462 $stripefailurecode = $stripe->code;
1463 $stripefailuremessage = 'Action required. Contact the support at ';// . $conf->global->SELLYOURSAAS_MAIN_EMAIL;
1464 $stripefailuredeclinecode = $stripe->declinecode;
1465 } else {
1466 dol_syslog(var_export($paymentintent, true), LOG_DEBUG);
1467
1468 $charge->status = 'failed';
1469 $charge->customer = $customer->id;
1470 $charge->failure_code = $stripe->code;
1471 $charge->failure_message = $stripe->error;
1472 $charge->failure_declinecode = $stripe->declinecode;
1473 $stripefailurecode = $stripe->code;
1474 $stripefailuremessage = $stripe->error;
1475 $stripefailuredeclinecode = $stripe->declinecode;
1476 }
1477
1478 //var_dump("stripefailurecode=".$stripefailurecode." stripefailuremessage=".$stripefailuremessage." stripefailuredeclinecode=".$stripefailuredeclinecode);
1479 //exit;
1480
1481
1482 // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...)
1483 if (empty($charge) || $charge->status == 'failed') {
1484 dol_syslog('Failed to charge payment mode ' . $stripecard->id . ' stripefailurecode=' . $stripefailurecode . ' stripefailuremessage=' . $stripefailuremessage . ' stripefailuredeclinecode=' . $stripefailuredeclinecode, LOG_WARNING);
1485
1486 // Save a stripe payment was in error
1487 $this->stripechargeerror++;
1488
1489 $error++;
1490 $errorforinvoice++;
1491 $errmsg = $langs->trans("FailedToChargeCard");
1492 if (!empty($charge)) {
1493 if ($stripefailuredeclinecode == 'authentication_required') {
1494 $errauthenticationmessage = $langs->trans("ErrSCAAuthentication");
1495 $errmsg = $errauthenticationmessage;
1496 } elseif (in_array($stripefailuredeclinecode, ['insufficient_funds', 'generic_decline'])) {
1497 $errmsg .= ': ' . $charge->failure_code;
1498 $errmsg .= ($charge->failure_message ? ' - ' : '') . ' ' . $charge->failure_message;
1499 if (empty($stripefailurecode)) {
1500 $stripefailurecode = $charge->failure_code;
1501 }
1502 if (empty($stripefailuremessage)) {
1503 $stripefailuremessage = $charge->failure_message;
1504 }
1505 } else {
1506 $errmsg .= ': failure_code=' . $charge->failure_code;
1507 $errmsg .= ($charge->failure_message ? ' - ' : '') . ' failure_message=' . $charge->failure_message;
1508 if (empty($stripefailurecode)) {
1509 $stripefailurecode = $charge->failure_code;
1510 }
1511 if (empty($stripefailuremessage)) {
1512 $stripefailuremessage = $charge->failure_message;
1513 }
1514 }
1515 } else {
1516 $errmsg .= ': ' . $stripefailurecode . ' - ' . $stripefailuremessage;
1517 $errmsg .= ($stripefailuredeclinecode ? ' - ' . $stripefailuredeclinecode : '');
1518 }
1519
1520 $description = 'Stripe payment ERROR from makeStripeSepaRequest: ' . $FULLTAG;
1521 $postactionmessages[] = $errmsg . ' (' . $stripearrayofkeys['publishable_key'] . ')';
1522 $this->errors[] = $errmsg;
1523 } else {
1524 dol_syslog('Successfuly request '.$type.' '.$stripecard->id);
1525
1526 $postactionmessages[] = 'Success to request '.$type.' (' . $charge->id . ' with ' . $stripearrayofkeys['publishable_key'] . ')';
1527
1528 // Save a stripe payment was done in real life so later we will be able to force a commit on recorded payments
1529 // even if in batch mode (method doTakePaymentStripe), we will always make all action in one transaction with a forced commit.
1530 $this->stripechargedone++;
1531
1532 // Default description used for label of event. Will be overwrite by another value later.
1533 $description = 'Stripe payment request OK (' . $charge->id . ') from makeStripeSepaRequest: ' . $FULLTAG;
1534 }
1535
1536 $object = $this;
1537
1538 // Track an event
1539 if (empty($charge) || $charge->status == 'failed') {
1540 $actioncode = 'PAYMENT_STRIPE_KO';
1541 $extraparams = $stripefailurecode;
1542 $extraparams .= (($extraparams && $stripefailuremessage) ? ' - ' : '') . $stripefailuremessage;
1543 $extraparams .= (($extraparams && $stripefailuredeclinecode) ? ' - ' : '') . $stripefailuredeclinecode;
1544 } else {
1545 $actioncode = 'PAYMENT_STRIPE_OK';
1546 $extraparams = '';
1547 }
1548 } else {
1549 $error++;
1550 $errorforinvoice++;
1551 dol_syslog("No ban payment method found for this stripe customer " . $customer->id, LOG_WARNING);
1552 $this->errors[] = 'Failed to get direct debit payment method for stripe customer = ' . $customer->id;
1553
1554 $description = 'Failed to find or use the payment mode - no ban defined for the thirdparty account';
1555 $stripefailurecode = 'BADPAYMENTMODE';
1556 $stripefailuremessage = 'Failed to find or use the payment mode - no ban defined for the thirdparty account';
1557 $postactionmessages[] = $description . ' (' . $stripearrayofkeys['publishable_key'] . ')';
1558
1559 $object = $this;
1560
1561 $actioncode = 'PAYMENT_STRIPE_KO';
1562 $extraparams = '';
1563 }
1564 } else {
1565 // If error because payment was canceled for a logical reason, we do nothing (no event added)
1566 $description = '';
1567 $stripefailurecode = '';
1568 $stripefailuremessage = '';
1569
1570 $object = $this;
1571
1572 $actioncode = '';
1573 $extraparams = '';
1574 }
1575 } else { // Else of the if ($resultthirdparty > 0 && ! empty($customer)) {
1576 if ($resultthirdparty <= 0) {
1577 dol_syslog('SellYourSaasUtils Failed to load customer for thirdparty_id = ' . $thirdparty->id, LOG_WARNING);
1578 $this->errors[] = 'Failed to load Stripe account for thirdparty_id = ' . $thirdparty->id;
1579 } else { // $customer stripe not found
1580 dol_syslog('SellYourSaasUtils Failed to get Stripe customer id for thirdparty_id = ' . $thirdparty->id . " in mode " . $servicestatus . " in Stripe env " . $stripearrayofkeysbyenv[$servicestatus]['publishable_key'], LOG_WARNING);
1581 $this->errors[] = 'Failed to get Stripe account id for thirdparty_id = ' . $thirdparty->id . " in mode " . $servicestatus . " in Stripe env " . $stripearrayofkeysbyenv[$servicestatus]['publishable_key'];
1582 }
1583 $error++;
1584 $errorforinvoice++;
1585
1586 $description = 'Failed to find or use your payment mode (no payment mode for this customer id)';
1587 $stripefailurecode = 'BADPAYMENTMODE';
1588 $stripefailuremessage = 'Failed to find or use your payment mode (no payment mode for this customer id)';
1589 $postactionmessages = [];
1590
1591 $object = $this;
1592
1593 $actioncode = 'PAYMENT_STRIPE_KO';
1594 $extraparams = '';
1595 }
1596
1597 if ($description) {
1598 dol_syslog("* Record event for credit transfer or direct debit request result - " . $description);
1599 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1600
1601 // Insert record of payment (success or error)
1602 $actioncomm = new ActionComm($this->db);
1603
1604 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
1605 $actioncomm->code = 'AC_' . $actioncode;
1606 $actioncomm->label = $description;
1607 $actioncomm->note_private = implode(",\n", $postactionmessages);
1608 $actioncomm->fk_project = $this->fk_project;
1609 $actioncomm->datep = $now;
1610 $actioncomm->datef = $now;
1611 $actioncomm->percentage = -1; // Not applicable
1612 $actioncomm->socid = $thirdparty->id;
1613 $actioncomm->contactid = 0;
1614 $actioncomm->authorid = $user->id; // User saving action
1615 $actioncomm->userownerid = $user->id; // Owner of action
1616 // Fields when action is a real email (content is already into note)
1617 /*$actioncomm->email_msgid = $object->email_msgid;
1618 $actioncomm->email_from = $object->email_from;
1619 $actioncomm->email_sender= $object->email_sender;
1620 $actioncomm->email_to = $object->email_to;
1621 $actioncomm->email_tocc = $object->email_tocc;
1622 $actioncomm->email_tobcc = $object->email_tobcc;
1623 $actioncomm->email_subject = $object->email_subject;
1624 $actioncomm->errors_to = $object->errors_to;*/
1625 $actioncomm->fk_element = $this->id;
1626 $actioncomm->elementtype = $this->element;
1627 $actioncomm->extraparams = dol_trunc($extraparams, 250);
1628
1629 $actioncomm->create($user);
1630 }
1631
1632 $this->description = $description;
1633 $this->postactionmessages = $postactionmessages;
1634 } catch (Exception $e) {
1635 $error++;
1636 $errorforinvoice++;
1637 dol_syslog('Error ' . $e->getMessage(), LOG_ERR);
1638 $this->errors[] = 'Error ' . $e->getMessage();
1639 }
1640 } else { // If remain to pay is null
1641 $error++;
1642 $errorforinvoice++;
1643 dol_syslog("Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?", LOG_WARNING);
1644 $this->errors[] = "Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?";
1645 }
1646 }
1647
1648 // Set status of the order to "Transferred" with method 'api'
1649 if (!$error && !$errorforinvoice) {
1650 $result = $bon->set_infotrans($user, $now, 3);
1651 if ($result < 0) {
1652 $error++;
1653 $errorforinvoice++;
1654 dol_syslog("Error on BonPrelevement creation", LOG_ERR);
1655 $this->errors[] = "Error on BonPrelevement creation";
1656 }
1657 }
1658
1659 if (!$error && !$errorforinvoice && $paymentintent !== null) {
1660 // Update the direct debit payment request of the processed invoice to save the id of the prelevement_bon
1661 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
1662 $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."',";
1663 $sql .= " ext_payment_site = '".$this->db->escape($service)."'";
1664 $sql .= " WHERE rowid = ".((int) $did);
1665
1666 dol_syslog(get_class($this)."::makeStripeSepaRequest update to save stripe paymentintent ids", LOG_DEBUG);
1667 $resql = $this->db->query($sql);
1668 if (!$resql) {
1669 $this->error = $this->db->lasterror();
1670 dol_syslog(get_class($this).'::makeStripeSepaRequest Erreur');
1671 $error++;
1672 }
1673 }
1674
1675 if (!$error && !$errorforinvoice) {
1676 $this->db->commit();
1677 } else {
1678 $this->db->rollback();
1679 }
1680 } else {
1681 $this->error = 'WithdrawRequestErrorNilAmount';
1682 dol_syslog(get_class($this).'::makeStripeSepaRequest WithdrawRequestErrorNilAmount');
1683 $error++;
1684 }
1685
1686 /*
1687 if (!$error) {
1688 // Force payment mode of the invoice to withdraw
1689 $payment_mode_id = dol_getIdFromCode($this->db, ($type == 'bank-transfer' ? 'VIR' : 'PRE'), 'c_paiement', 'code', 'id', 1);
1690 if ($payment_mode_id > 0) {
1691 $result = $this->setPaymentMethods($payment_mode_id);
1692 }
1693 }*/
1694
1695 if ($error) {
1696 return -1;
1697 }
1698 return 1;
1699 } else {
1700 $this->error = $this->db->error();
1701 dol_syslog(get_class($this).'::makeStripeSepaRequest Erreur -2');
1702 return -2;
1703 }
1704 } else {
1705 $this->error = "Status of invoice does not allow this";
1706 dol_syslog(get_class($this)."::makeStripeSepaRequest ".$this->error." ".$this->status." ,".$this->paye.", ".$this->mode_reglement_id, LOG_WARNING);
1707 return -3;
1708 }
1709 }
1710
1711 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1719 public function demande_prelevement_delete($fuser, $did)
1720 {
1721 // phpcs:enable
1722 $sql = 'DELETE FROM '.$this->db->prefix().'prelevement_demande';
1723 $sql .= ' WHERE rowid = '.((int) $did);
1724 $sql .= ' AND traite = 0';
1725 if ($this->db->query($sql)) {
1726 return 0;
1727 } else {
1728 $this->error = $this->db->lasterror();
1729 dol_syslog(get_class($this).'::demande_prelevement_delete Error '.$this->error);
1730 return -1;
1731 }
1732 }
1733
1739 public function buildEPCQrCodeString()
1740 {
1741 global $mysoc;
1742
1743 // Convert total_ttc to a string with 2 decimal places
1744 $totalTTCString = number_format($this->total_ttc, 2, '.', '');
1745
1746 // Initialize an array to hold the lines of the QR code
1747 $lines = array();
1748
1749 // Add the standard elements to the QR code
1750 $lines = [
1751 'BCD', // Service Tag (optional)
1752 '002', // Version (optional)
1753 '1', // Character set (optional)
1754 'SCT', // Identification (optional)
1755 ];
1756
1757 // Add the bank account information
1758 include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
1759 $bankAccount = new Account($this->db);
1760 if ($this->fk_account > 0) {
1761 $bankAccount->fetch($this->fk_account);
1762 $lines[] = $bankAccount->bic; //BIC (required)
1763 if (!empty($bankAccount->owner_name)) {
1764 $lines[] = $bankAccount->owner_name; //Owner of the bank account, if present (required)
1765 } else {
1766 $lines[] = $mysoc->name; //Name (required)
1767 }
1768 $lines[] = $bankAccount->iban; //IBAN (required)
1769 } else {
1770 $lines[] = ""; //BIC (required)
1771 $lines[] = $mysoc->name; //Name (required)
1772 $lines[] = ""; //IBAN (required)
1773 }
1774
1775 // Add the amount and reference
1776 $lines[] = 'EUR' . $totalTTCString; // Amount (optional)
1777 $lines[] = ''; // Purpose (optional)
1778 $lines[] = ''; // Payment reference (optional)
1779 $lines[] = $this->ref; // Remittance Information (optional)
1780
1781 // Join the lines with newline characters and return the result
1782 return implode("\n", $lines);
1783 }
1789 public function buildZATCAQRString()
1790 {
1791 global $conf, $mysoc;
1792
1793 $tmplang = new Translate('', $conf);
1794 $tmplang->setDefaultLang('en_US');
1795 $tmplang->load("main");
1796
1797 $datestring = dol_print_date($this->date, 'dayhourrfc');
1798 //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
1799 //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
1800 $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
1801 $pricetaxstring = price2num($this->total_tva, 2, 1);
1802
1803 /*
1804 $name = implode(unpack("H*", $this->thirdparty->name));
1805 $vatnumber = implode(unpack("H*", $this->thirdparty->tva_intra));
1806 $date = implode(unpack("H*", $datestring));
1807 $pricewithtax = implode(unpack("H*", price2num($pricewithtaxstring, 2)));
1808 $pricetax = implode(unpack("H*", $pricetaxstring));
1809
1810 //var_dump(strlen($this->thirdparty->name));
1811 //var_dump(str_pad(dechex('9'), 2, '0', STR_PAD_LEFT));
1812 //var_dump($this->thirdparty->name);
1813 //var_dump(implode(unpack("H*", $this->thirdparty->name)));
1814 //var_dump(price($this->total_tva, 0, $tmplang, 0, -1, 2));
1815
1816 $s = '01'.str_pad(dechex(strlen($this->thirdparty->name)), 2, '0', STR_PAD_LEFT).$name;
1817 $s .= '02'.str_pad(dechex(strlen($this->thirdparty->tva_intra)), 2, '0', STR_PAD_LEFT).$vatnumber;
1818 $s .= '03'.str_pad(dechex(strlen($datestring)), 2, '0', STR_PAD_LEFT).$date;
1819 $s .= '04'.str_pad(dechex(strlen($pricewithtaxstring)), 2, '0', STR_PAD_LEFT).$pricewithtax;
1820 $s .= '05'.str_pad(dechex(strlen($pricetaxstring)), 2, '0', STR_PAD_LEFT).$pricetax;
1821 $s .= ''; // Hash of xml invoice
1822 $s .= ''; // ecda signature
1823 $s .= ''; // ecda public key
1824 $s .= ''; // ecda signature of public key stamp
1825 */
1826 $mysocname = $mysoc->name ?? '';
1827 // Using TLV format
1828 $s = pack('C1', 1).pack('C1', strlen($mysocname)).$mysocname;
1829 $s .= pack('C1', 2).pack('C1', strlen($mysoc->tva_intra)).$mysoc->tva_intra;
1830 $s .= pack('C1', 3).pack('C1', strlen($datestring)).$datestring;
1831 $s .= pack('C1', 4).pack('C1', strlen($pricewithtaxstring)).$pricewithtaxstring;
1832 $s .= pack('C1', 5).pack('C1', strlen($pricetaxstring)).$pricetaxstring;
1833 $s .= ''; // Hash of xml invoice
1834 $s .= ''; // ecda signature
1835 $s .= ''; // ecda public key
1836 $s .= ''; // ecda signature of public key stamp
1837
1838 $s = base64_encode($s);
1839
1840 return $s;
1841 }
1842
1843
1850 {
1851 global $conf, $mysoc;
1852
1853 $tmplang = new Translate('', $conf);
1854 $tmplang->setDefaultLang('en_US');
1855 $tmplang->load("main");
1856
1857 $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
1858 $pricetaxstring = price2num($this->total_tva, 2, 1);
1859
1860 $complementaryinfo = '';
1861 /*
1862 Example: //S1/10/10201409/11/190512/20/1400.000-53/30/106017086/31/180508/32/7.7/40/2:10;0:30
1863 /10/ Numéro de facture – 10201409
1864 /11/ Date de facture – 12.05.2019
1865 /20/ Référence client – 1400.000-53
1866 /30/ Numéro IDE pour la TVA – CHE-106.017.086 TVA
1867 /31/ Date de la prestation pour la comptabilisation de la TVA – 08.05.2018
1868 /32/ Taux de TVA sur le montant total de la facture – 7.7%
1869 /40/ Conditions – 2% d’escompte à 10 jours, paiement net à 30 jours
1870 */
1871 $datestring = dol_print_date($this->date, '%y%m%d');
1872 //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
1873 //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
1874 $complementaryinfo = '//S1/10/'.str_replace('/', '', $this->ref).'/11/'.$datestring;
1875 if ($this->ref_client) {
1876 $complementaryinfo .= '/20/'.$this->ref_client;
1877 }
1878 if ($this->thirdparty->tva_intra) {
1879 $complementaryinfo .= '/30/'.$this->thirdparty->tva_intra;
1880 }
1881
1882 include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
1883 $bankaccount = new Account($this->db);
1884
1885 // Header
1886 $s = '';
1887 $s .= "SPC\n";
1888 $s .= "0200\n";
1889 $s .= "1\n";
1890 // Info Seller ("Compte / Payable à")
1891 if ($this->fk_account > 0) {
1892 // Bank BAN if country is LI or CH. TODO Add a test to check than IBAN start with CH or LI
1893 $bankaccount->fetch($this->fk_account);
1894 $s .= $bankaccount->iban."\n";
1895 } else {
1896 $s .= "\n";
1897 }
1898 if ($bankaccount->id > 0 && getDolGlobalString('PDF_SWISS_QRCODE_USE_OWNER_OF_ACCOUNT_AS_CREDITOR')) {
1899 // If a bank account is provided and we ask to use it as creditor, we use the bank address
1900 // TODO In a future, we may always use this address, and if name/address/zip/town/country differs from $mysoc, we can use the address of $mysoc into the final seller field ?
1901 $s .= "S\n";
1902 $s .= dol_trunc($bankaccount->owner_name, 70, 'right', 'UTF-8', 1)."\n";
1903 $addresslinearray = explode("\n", $bankaccount->owner_address);
1904 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1905 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1906 /*$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
1907 $s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
1908 $s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";*/
1909 } else {
1910 $s .= "S\n";
1911 $s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
1912 $addresslinearray = explode("\n", $mysoc->address);
1913 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1914 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1915 $s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
1916 $s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
1917 $s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
1918 }
1919 // Final seller (Ultimate seller) ("Créancier final" = "En faveur de")
1920 $s .= "\n";
1921 $s .= "\n";
1922 $s .= "\n";
1923 $s .= "\n";
1924 $s .= "\n";
1925 $s .= "\n";
1926 $s .= "\n";
1927 // Amount of payment (to do?)
1928 $s .= price($pricewithtaxstring, 0, 'none', 0, 0, 2)."\n";
1929 $s .= ($this->multicurrency_code ? $this->multicurrency_code : $conf->currency)."\n";
1930 // Buyer
1931 $s .= "S\n";
1932 $s .= dol_trunc($this->thirdparty->name, 70, 'right', 'UTF-8', 1)."\n";
1933 $addresslinearray = explode("\n", $this->thirdparty->address);
1934 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1935 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1936 $s .= dol_trunc($this->thirdparty->zip, 16, 'right', 'UTF-8', 1)."\n";
1937 $s .= dol_trunc($this->thirdparty->town, 35, 'right', 'UTF-8', 1)."\n";
1938 $s .= dol_trunc($this->thirdparty->country_code, 2, 'right', 'UTF-8', 1)."\n";
1939 // ID of payment
1940 $s .= "NON\n"; // NON or QRR
1941 $s .= "\n"; // QR Code reference if previous field is QRR
1942 // Free text
1943 if ($complementaryinfo) {
1944 $s .= $complementaryinfo."\n";
1945 } else {
1946 $s .= "\n";
1947 }
1948 $s .= "EPD\n";
1949 // More text, complementary info
1950 if ($complementaryinfo) {
1951 $s .= $complementaryinfo."\n";
1952 }
1953 $s .= "\n";
1954 //var_dump($s);exit;
1955 return $s;
1956 }
1957}
1958
1959
1960
1961require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
1962
1967{
1973 public $label;
1974
1980 public $ref; // Product ref (deprecated)
1986 public $libelle; // Product label (deprecated)
1987
1992 public $product_type = 0;
1993
1998 public $product_ref;
1999
2004 public $product_label;
2005
2010 public $product_desc;
2011
2016 public $qty;
2017
2022 public $subprice;
2023
2029 public $price;
2030
2035 public $fk_product;
2036
2041 public $vat_src_code;
2042
2047 public $tva_tx;
2048
2053 public $localtax1_tx;
2054
2059 public $localtax2_tx;
2060
2066 public $localtax1_type;
2067
2073 public $localtax2_type;
2074
2079 public $remise_percent;
2080
2086 public $remise;
2087
2092 public $total_ht;
2093
2098 public $total_tva;
2099
2104 public $total_localtax1;
2105
2110 public $total_localtax2;
2111
2116 public $total_ttc;
2117
2121 public $date_start_fill; // If set to 1, when invoice is created from a template invoice, it will also auto set the field date_start at creation
2125 public $date_end_fill; // If set to 1, when invoice is created from a template invoice, it will also auto set the field date_end at creation
2126
2130 public $buy_price_ht;
2135 public $buyprice;
2140 public $pa_ht;
2141
2145 public $marge_tx;
2149 public $marque_tx;
2150
2157 public $info_bits = 0;
2158
2167 public $special_code = 0;
2168
2173 public $fk_user_author;
2174
2179 public $fk_user_modif;
2180
2184 public $fk_accounting_account;
2185}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
$object ref
Definition info.php:89
Class to manage bank accounts.
Class to manage agenda events (actions)
Class to manage withdrawal receipts.
Superclass for invoice classes.
const TYPE_CREDIT_NOTE
Credit note invoice.
const STATUS_CLOSED
Classified paid.
getSumCreditNotesUsed($multicurrency=0)
Return amount (with tax) of all credit notes invoices + excess received used by invoice.
getRemainToPay($multicurrency=0)
Return remain amount to pay.
buildZATCAQRString()
Build string for ZATCA QR Code (Arabi Saudia)
makeStripeCardRequest($fuser, $id, $sourcetype='facture')
Create a payment with Stripe card Must take amount using Stripe and record an event into llx_actionco...
const TYPE_STANDARD
Standard invoice.
buildEPCQrCodeString()
Build string for EPC QR Code.
makeStripeSepaRequest($fuser, $did, $type='direct-debit', $sourcetype='facture', $service='', $forcestripe='')
Create a direct debit order into prelevement_bons for a given prelevement_request,...
getSubtypeLabel($table='')
Return label of invoice subtype.
demande_prelevement_delete($fuser, $did)
Remove a direct debit request or a credit transfer request.
getVentilExportCompta()
Return if an invoice was transferred into accountnancy.
const TYPE_PROFORMA
Proforma invoice.
buildSwitzerlandQRString()
Build string for QR-Bill (Switzerland)
const STATUS_VALIDATED
Validated (need to be paid)
getListOfPayments($filtertype='', $multicurrency=0, $mode=0)
Return list of payments.
const TYPE_SITUATION
Situation invoice.
getSumDepositsUsed($multicurrency=0)
Return amount (with tax) of all deposits invoices used by invoice.
getSommePaiement($multicurrency=0)
Return amount of payments already done.
demande_prelevement(User $fuser, float $amount=0, string $type='direct-debit', string $sourcetype='facture', int $checkduplicateamongall=0, int $ribId=0)
Create a withdrawal request for a direct debit order or a credit transfer order.
const TYPE_DEPOSIT
Deposit invoice.
LibStatut($paye, $status, $mode=0, $alreadypaid=-1, $type=-1)
Return label of a status.
const STATUS_ABANDONED
Classified abandoned and no payment done.
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date.
const TYPE_REPLACEMENT
Replacement invoice.
getSumFromThisCreditNotesNotUsed($multicurrency=0)
Return amount (with tax) of all converted amount for this credit note.
getListIdAvoirFromInvoice()
Returns array of credit note ids from the invoice.
const STATUS_DRAFT
Draft status.
getIdReplacingInvoice($option='')
Returns the id of the invoice that replaces it.
getLibType($withbadge=0)
Return label of type of invoice.
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -> yes (1) I...
getArrayOfInvoiceSubtypes($mode=0)
Retrieve a list of invoice subtype labels or codes.
getLibStatut($mode=0, $alreadypaid=-1)
Return label of object status.
getNextNumRef($soc, $mode='next')
Return next reference of invoice not already used (or last reference)
Parent class of all other business classes for details of elements (invoices, contracts,...
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
setPaymentMethods($id)
Change the payments methods.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage bank accounts description of third parties.
Class for CompanyPaymentMode.
Class to manage absolute discounts.
Class to manage third parties objects (customers, suppliers, prospects...)
Stripe class @TODO No reason to extends CommonObject.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:149