dolibarr 20.0.0
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 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
30require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
31
35abstract class CommonInvoice extends CommonObject
36{
38
42 public $title;
43
47 public $type = self::TYPE_STANDARD;
48
52 public $subtype;
53
59 public $fk_soc;
63 public $socid;
64
65 public $paye;
66
72 public $date;
73
77 public $date_lim_reglement;
78
79 public $cond_reglement_id; // Id in llx_c_paiement
80 public $cond_reglement_code; // Code in llx_c_paiement
81 public $cond_reglement_label;
82 public $cond_reglement_doc; // Code in llx_c_paiement
83
84 public $mode_reglement_id;
85 public $mode_reglement_code; // Code in llx_c_paiement
86
90 public $mode_reglement;
91
95 public $revenuestamp;
96
97 public $totalpaid; // duplicate with sumpayed
98 public $totaldeposits; // duplicate with sumdeposit
99 public $totalcreditnotes; // duplicate with sumcreditnote
100
101 public $sumpayed;
102 public $sumpayed_multicurrency;
103 public $sumdeposit;
104 public $sumdeposit_multicurrency;
105 public $sumcreditnote;
106 public $sumcreditnote_multicurrency;
107 public $remaintopay;
108
112 public $stripechargedone;
113
117 public $stripechargeerror;
118
123 public $description;
124
130 public $ref_client;
131
135 public $situation_cycle_ref;
136
142 public $close_code;
143
148 public $close_note;
149
150
155 public $postactionmessages;
156
157
161 const TYPE_STANDARD = 0;
162
167
172
176 const TYPE_DEPOSIT = 3;
177
182 const TYPE_PROFORMA = 4;
183
187 const TYPE_SITUATION = 5;
188
192 const STATUS_DRAFT = 0;
193
198
206 const STATUS_CLOSED = 2;
207
216
217
218
226 public function getRemainToPay($multicurrency = 0)
227 {
228 $alreadypaid = 0.0;
229 $alreadypaid += $this->getSommePaiement($multicurrency);
230 $alreadypaid += $this->getSumDepositsUsed($multicurrency);
231 $alreadypaid += $this->getSumCreditNotesUsed($multicurrency);
232
233 $remaintopay = price2num($this->total_ttc - $alreadypaid, 'MT');
234 if ($this->statut == self::STATUS_CLOSED && $this->close_code == 'discount_vat') { // If invoice closed with discount for anticipated payment
235 $remaintopay = 0.0;
236 }
237 return $remaintopay;
238 }
239
248 public function getSommePaiement($multicurrency = 0)
249 {
250 $table = 'paiement_facture';
251 $field = 'fk_facture';
252 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
253 $table = 'paiementfourn_facturefourn';
254 $field = 'fk_facturefourn';
255 }
256
257 $sql = "SELECT sum(amount) as amount, sum(multicurrency_amount) as multicurrency_amount";
258 $sql .= " FROM ".$this->db->prefix().$table;
259 $sql .= " WHERE ".$field." = ".((int) $this->id);
260
261 dol_syslog(get_class($this)."::getSommePaiement", LOG_DEBUG);
262
263 $resql = $this->db->query($sql);
264 if ($resql) {
265 $obj = $this->db->fetch_object($resql);
266
267 $this->db->free($resql);
268
269 if ($obj) {
270 if ($multicurrency < 0) {
271 $this->sumpayed = $obj->amount;
272 $this->sumpayed_multicurrency = $obj->multicurrency_amount;
273 return array('alreadypaid' => (float) $obj->amount, 'alreadypaid_multicurrency' => (float) $obj->multicurrency_amount);
274 } elseif ($multicurrency) {
275 $this->sumpayed_multicurrency = $obj->multicurrency_amount;
276 return (float) $obj->multicurrency_amount;
277 } else {
278 $this->sumpayed = $obj->amount;
279 return (float) $obj->amount;
280 }
281 } else {
282 return 0;
283 }
284 } else {
285 $this->error = $this->db->lasterror();
286 return -1;
287 }
288 }
289
299 public function getSumDepositsUsed($multicurrency = 0)
300 {
301 /*if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
302 // 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.
303 return 0.0;
304 }*/
305
306 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
307
308 $discountstatic = new DiscountAbsolute($this->db);
309 $result = $discountstatic->getSumDepositsUsed($this, $multicurrency);
310
311 if ($result >= 0) {
312 if ($multicurrency) {
313 $this->sumdeposit_multicurrency = $result;
314 } else {
315 $this->sumdeposit = $result;
316 }
317
318 return $result;
319 } else {
320 $this->error = $discountstatic->error;
321 return -1;
322 }
323 }
324
332 public function getSumCreditNotesUsed($multicurrency = 0)
333 {
334 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
335
336 $discountstatic = new DiscountAbsolute($this->db);
337 $result = $discountstatic->getSumCreditNotesUsed($this, $multicurrency);
338 if ($result >= 0) {
339 if ($multicurrency) {
340 $this->sumcreditnote_multicurrency = $result;
341 } else {
342 $this->sumcreditnote = $result;
343 }
344
345 return $result;
346 } else {
347 $this->error = $discountstatic->error;
348 return -1;
349 }
350 }
351
358 public function getSumFromThisCreditNotesNotUsed($multicurrency = 0)
359 {
360 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
361
362 $discountstatic = new DiscountAbsolute($this->db);
363 $result = $discountstatic->getSumFromThisCreditNotesNotUsed($this, $multicurrency);
364 if ($result >= 0) {
365 return $result;
366 } else {
367 $this->error = $discountstatic->error;
368 return -1;
369 }
370 }
371
378 {
379 $idarray = array();
380
381 $sql = "SELECT rowid";
382 $sql .= " FROM ".$this->db->prefix().$this->table_element;
383 $sql .= " WHERE fk_facture_source = ".((int) $this->id);
384 $sql .= " AND type = 2";
385 $resql = $this->db->query($sql);
386 if ($resql) {
387 $num = $this->db->num_rows($resql);
388 $i = 0;
389 while ($i < $num) {
390 $row = $this->db->fetch_row($resql);
391 $idarray[] = $row[0];
392 $i++;
393 }
394 } else {
395 dol_print_error($this->db);
396 }
397 return $idarray;
398 }
399
406 public function getIdReplacingInvoice($option = '')
407 {
408 $sql = "SELECT rowid";
409 $sql .= " FROM ".$this->db->prefix().$this->table_element;
410 $sql .= " WHERE fk_facture_source = ".((int) $this->id);
411 $sql .= " AND type < 2";
412 if ($option == 'validated') {
413 $sql .= ' AND fk_statut = 1';
414 }
415 // PROTECTION BAD DATA
416 // In case the database is corrupted and there is a valid replectement invoice
417 // and another no, priority is given to the valid one.
418 // Should not happen (unless concurrent access and 2 people have created a
419 // replacement invoice for the same invoice at the same time)
420 $sql .= " ORDER BY fk_statut DESC";
421
422 $resql = $this->db->query($sql);
423 if ($resql) {
424 $obj = $this->db->fetch_object($resql);
425 if ($obj) {
426 // If there is any
427 return $obj->rowid;
428 } else {
429 // If no invoice replaces it
430 return 0;
431 }
432 } else {
433 return -1;
434 }
435 }
436
446 public function getListOfPayments($filtertype = '', $multicurrency = 0)
447 {
448 $retarray = array();
449 // By default no error, list can be empty.
450 $this->error = '';
451
452 $table = 'paiement_facture';
453 $table2 = 'paiement';
454 $field = 'fk_facture';
455 $field2 = 'fk_paiement';
456 $field3 = ', p.ref_ext';
457 $field4 = ', p.fk_bank'; // Bank line id
458 $sharedentity = 'facture';
459 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
460 $table = 'paiementfourn_facturefourn';
461 $table2 = 'paiementfourn';
462 $field = 'fk_facturefourn';
463 $field2 = 'fk_paiementfourn';
464 $field3 = '';
465 $sharedentity = 'facture_fourn';
466 }
467
468 $sql = "SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code".$field3 . $field4;
469 $sql .= " FROM ".$this->db->prefix().$table." as pf, ".$this->db->prefix().$table2." as p, ".$this->db->prefix()."c_paiement as t";
470 $sql .= " WHERE pf.".$field." = ".((int) $this->id);
471 $sql .= " AND pf.".$field2." = p.rowid";
472 $sql .= ' AND p.fk_paiement = t.id';
473 $sql .= ' AND p.entity IN ('.getEntity($sharedentity).')';
474 if ($filtertype) {
475 $sql .= " AND t.code='PRE'";
476 }
477
478 dol_syslog(get_class($this)."::getListOfPayments", LOG_DEBUG);
479 $resql = $this->db->query($sql);
480 if ($resql) {
481 $num = $this->db->num_rows($resql);
482 $i = 0;
483 while ($i < $num) {
484 $obj = $this->db->fetch_object($resql);
485 $tmp = array('amount' => $obj->amount, 'type' => $obj->code, 'date' => $obj->datep, 'num' => $obj->num, 'ref' => $obj->ref);
486 if (!empty($field3)) {
487 $tmp['ref_ext'] = $obj->ref_ext;
488 }
489 if (!empty($field4)) {
490 $tmp['fk_bank_line'] = $obj->fk_bank;
491 }
492 $retarray[] = $tmp;
493 $i++;
494 }
495 $this->db->free($resql);
496
497 //look for credit notes and discounts and deposits
498 $sql = '';
499 if ($this->element == 'facture' || $this->element == 'invoice') {
500 $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";
501 $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture as f';
502 $sql .= ' WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = '.((int) $this->id);
503 $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)
504 } elseif ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
505 $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";
506 $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture_fourn as f';
507 $sql .= ' WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = '.((int) $this->id);
508 $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)
509 }
510
511 if ($sql) {
512 $resql = $this->db->query($sql);
513 if ($resql) {
514 $num = $this->db->num_rows($resql);
515 $i = 0;
516 while ($i < $num) {
517 $obj = $this->db->fetch_object($resql);
518 if ($multicurrency) {
519 $retarray[] = array('amount' => $obj->multicurrency_amount, 'type' => $obj->type, 'date' => $obj->date, 'num' => '0', 'ref' => $obj->ref);
520 } else {
521 $retarray[] = array('amount' => $obj->amount, 'type' => $obj->type, 'date' => $obj->date, 'num' => '', 'ref' => $obj->ref);
522 }
523 $i++;
524 }
525 } else {
526 $this->error = $this->db->lasterror();
527 dol_print_error($this->db);
528 return array();
529 }
530 $this->db->free($resql);
531 }
532
533 return $retarray;
534 } else {
535 $this->error = $this->db->lasterror();
536 dol_print_error($this->db);
537 return array();
538 }
539 }
540
541
542 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
556 public function is_erasable()
557 {
558 // phpcs:enable
559
560 // We check if invoice is a temporary number (PROVxxxx)
561 $tmppart = substr($this->ref, 1, 4);
562
563 if ($this->statut == self::STATUS_DRAFT && $tmppart === 'PROV') { // If draft invoice and ref not yet defined
564 return 1;
565 }
566
567 if (getDolGlobalString('INVOICE_CAN_NEVER_BE_REMOVED')) {
568 return 0;
569 }
570
571 // If not a draft invoice and not temporary invoice
572 if ($tmppart !== 'PROV') {
573 $ventilExportCompta = $this->getVentilExportCompta();
574 if ($ventilExportCompta != 0) {
575 return -1;
576 }
577
578 // Get last number of validated invoice
579 if ($this->element != 'invoice_supplier') {
580 if (empty($this->thirdparty)) {
581 $this->fetch_thirdparty(); // We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag).
582 }
583 $maxref = $this->getNextNumRef($this->thirdparty, 'last');
584
585 // If there is no invoice into the reset range and not already dispatched, we can delete
586 // If invoice to delete is last one and not already dispatched, we can delete
587 if (!getDolGlobalString('INVOICE_CAN_ALWAYS_BE_REMOVED') && $maxref != '' && $maxref != $this->ref) {
588 return -2;
589 }
590
591 // TODO If there is payment in bookkeeping, check payment is not dispatched in accounting
592 // ...
593
594 if ($this->situation_cycle_ref && method_exists($this, 'is_last_in_cycle')) {
595 $last = $this->is_last_in_cycle();
596 if (!$last) {
597 return -3;
598 }
599 }
600 }
601 }
602
603 // Test if there is at least one payment. If yes, refuse to delete.
604 if (!getDolGlobalString('INVOICE_CAN_ALWAYS_BE_REMOVED') && $this->getSommePaiement() > 0) {
605 return -4;
606 }
607
608 return 2;
609 }
610
616 public function getVentilExportCompta()
617 {
618 $alreadydispatched = 0;
619
620 $type = 'customer_invoice';
621 if ($this->element == 'invoice_supplier') {
622 $type = 'supplier_invoice';
623 }
624
625 $sql = " SELECT COUNT(ab.rowid) as nb FROM ".$this->db->prefix()."accounting_bookkeeping as ab WHERE ab.doc_type='".$this->db->escape($type)."' AND ab.fk_doc = ".((int) $this->id);
626 $resql = $this->db->query($sql);
627 if ($resql) {
628 $obj = $this->db->fetch_object($resql);
629 if ($obj) {
630 $alreadydispatched = $obj->nb;
631 }
632 } else {
633 $this->error = $this->db->lasterror();
634 return -1;
635 }
636
637 if ($alreadydispatched) {
638 return 1;
639 }
640 return 0;
641 }
642
650 public function getNextNumRef($soc, $mode = 'next')
651 {
652 // TODO Must be implemented into main class
653 return '';
654 }
655
662 public function getLibType($withbadge = 0)
663 {
664 global $langs;
665
666 $labellong = "Unknown";
667 if ($this->type == CommonInvoice::TYPE_STANDARD) {
668 $labellong = "InvoiceStandard";
669 $labelshort = "InvoiceStandardShort";
670 } elseif ($this->type == CommonInvoice::TYPE_REPLACEMENT) {
671 $labellong = "InvoiceReplacement";
672 $labelshort = "InvoiceReplacementShort";
673 } elseif ($this->type == CommonInvoice::TYPE_CREDIT_NOTE) {
674 $labellong = "InvoiceAvoir";
675 $labelshort = "CreditNote";
676 } elseif ($this->type == CommonInvoice::TYPE_DEPOSIT) {
677 $labellong = "InvoiceDeposit";
678 $labelshort = "Deposit";
679 } elseif ($this->type == CommonInvoice::TYPE_PROFORMA) {
680 $labellong = "InvoiceProForma"; // Not used.
681 $labelshort = "ProForma";
682 } elseif ($this->type == CommonInvoice::TYPE_SITUATION) {
683 $labellong = "InvoiceSituation";
684 $labelshort = "Situation";
685 }
686
687 $out = '';
688 if ($withbadge) {
689 $out .= '<span class="badgeneutral" title="'.dol_escape_htmltag($langs->trans($labellong)).'">';
690 }
691 $out .= $langs->trans($withbadge == 2 ? $labelshort : $labellong);
692 if ($withbadge) {
693 $out .= '</span>';
694 }
695 return $out;
696 }
697
704 public function getSubtypeLabel($table = '')
705 {
706 $subtypeLabel = '';
707 if ($table === 'facture' || $table === 'facture_fourn') {
708 $sql = "SELECT s.label FROM " . $this->db->prefix() . $table . " AS f";
709 $sql .= " INNER JOIN " . $this->db->prefix() . "c_invoice_subtype AS s ON f.subtype = s.rowid";
710 $sql .= " WHERE f.ref = '".$this->db->escape($this->ref)."'";
711 } elseif ($table === 'facture_rec' || $table === 'facture_fourn_rec') {
712 $sql = "SELECT s.label FROM " . $this->db->prefix() . $table . " AS f";
713 $sql .= " INNER JOIN " . $this->db->prefix() . "c_invoice_subtype AS s ON f.subtype = s.rowid";
714 $sql .= " WHERE f.titre = '".$this->db->escape($this->title)."'";
715 } else {
716 return -1;
717 }
718
719 $resql = $this->db->query($sql);
720 if ($resql) {
721 while ($obj = $this->db->fetch_object($resql)) {
722 $subtypeLabel = $obj->label;
723 }
724 } else {
725 dol_print_error($this->db);
726 return -1;
727 }
728
729 return $subtypeLabel;
730 }
731
738 public function getArrayOfInvoiceSubtypes($mode = 0)
739 {
740 global $mysoc;
741
742 $effs = array();
743
744 $sql = "SELECT rowid, code, label as label";
745 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
746 $sql .= " WHERE active = 1 AND fk_country = ".((int) $mysoc->country_id)." AND entity IN(".getEntity('c_invoice_subtype').")";
747 $sql .= " ORDER by rowid, code";
748
749 dol_syslog(get_class($this) . '::getArrayOfInvoiceSubtypes', LOG_DEBUG);
750 $resql = $this->db->query($sql);
751 if ($resql) {
752 $num = $this->db->num_rows($resql);
753 $i = 0;
754
755 while ($i < $num) {
756 $objp = $this->db->fetch_object($resql);
757 if (!$mode) {
758 $key = $objp->rowid;
759 $effs[$key] = $objp->label;
760 } else {
761 $key = $objp->code;
762 $effs[$key] = $objp->rowid;
763 }
764
765 $i++;
766 }
767 $this->db->free($resql);
768 }
769
770 return $effs;
771 }
772
780 public function getLibStatut($mode = 0, $alreadypaid = -1)
781 {
782 return $this->LibStatut($this->paye, $this->statut, $mode, $alreadypaid, $this->type);
783 }
784
785 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
796 public function LibStatut($paye, $status, $mode = 0, $alreadypaid = -1, $type = -1)
797 {
798 // phpcs:enable
799 global $langs, $hookmanager;
800 $langs->load('bills');
801
802 if ($type == -1) {
803 $type = $this->type;
804 }
805
806 $statusType = 'status0';
807 $prefix = 'Short';
808 if (!$paye) {
809 if ($status == 0) {
810 $labelStatus = $langs->transnoentitiesnoconv('BillStatusDraft');
811 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusDraft');
812 } elseif (($status == 3 || $status == 2) && $alreadypaid <= 0) {
813 if ($status == 3) {
814 $labelStatus = $langs->transnoentitiesnoconv('BillStatusCanceled');
815 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusCanceled');
816 } else {
817 $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedUnpaid');
818 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedUnpaid');
819 }
820 $statusType = 'status5';
821 } elseif (($status == 3 || $status == 2) && $alreadypaid > 0) {
822 $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedPaidPartially');
823 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedPaidPartially');
824 $statusType = 'status9';
825 } elseif ($alreadypaid == 0) {
826 $labelStatus = $langs->transnoentitiesnoconv('BillStatusNotPaid');
827 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusNotPaid');
828 $statusType = 'status1';
829 } else {
830 $labelStatus = $langs->transnoentitiesnoconv('BillStatusStarted');
831 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusStarted');
832 $statusType = 'status3';
833 }
834 } else {
835 $statusType = 'status6';
836
837 if ($type == self::TYPE_CREDIT_NOTE) {
838 $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaidBackOrConverted'); // credit note
839 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaidBackOrConverted'); // credit note
840 } elseif ($type == self::TYPE_DEPOSIT) {
841 $labelStatus = $langs->transnoentitiesnoconv('BillStatusConverted'); // deposit invoice
842 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusConverted'); // deposit invoice
843 } else {
844 $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaid');
845 $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaid');
846 }
847 }
848
849 $parameters = array(
850 'status' => $status,
851 'mode' => $mode,
852 'paye' => $paye,
853 'alreadypaid' => $alreadypaid,
854 'type' => $type
855 );
856
857 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
858
859 if ($reshook > 0) {
860 return $hookmanager->resPrint;
861 }
862
863
864
865 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
866 }
867
868 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
876 public function calculate_date_lim_reglement($cond_reglement = 0)
877 {
878 // phpcs:enable
879 if (!$cond_reglement) {
880 $cond_reglement = $this->cond_reglement_code;
881 }
882 if (!$cond_reglement) {
883 $cond_reglement = $this->cond_reglement_id;
884 }
885 if (!$cond_reglement) {
886 return $this->date;
887 }
888
889 $cdr_nbjour = 0;
890 $cdr_type = 0;
891 $cdr_decalage = 0;
892
893 $sqltemp = "SELECT c.type_cdr, c.nbjour, c.decalage";
894 $sqltemp .= " FROM ".$this->db->prefix()."c_payment_term as c";
895 if (is_numeric($cond_reglement)) {
896 $sqltemp .= " WHERE c.rowid=".((int) $cond_reglement);
897 } else {
898 $sqltemp .= " WHERE c.entity IN (".getEntity('c_payment_term').")";
899 $sqltemp .= " AND c.code = '".$this->db->escape($cond_reglement)."'";
900 }
901
902 dol_syslog(get_class($this).'::calculate_date_lim_reglement', LOG_DEBUG);
903 $resqltemp = $this->db->query($sqltemp);
904 if ($resqltemp) {
905 if ($this->db->num_rows($resqltemp)) {
906 $obj = $this->db->fetch_object($resqltemp);
907 $cdr_nbjour = $obj->nbjour;
908 $cdr_type = $obj->type_cdr;
909 $cdr_decalage = $obj->decalage;
910 }
911 } else {
912 $this->error = $this->db->error();
913 return -1;
914 }
915 $this->db->free($resqltemp);
916
917 /* Definition de la date limit */
918
919 // 0 : adding the number of days
920 if ($cdr_type == 0) {
921 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
922
923 $datelim += ($cdr_decalage * 3600 * 24);
924 } elseif ($cdr_type == 1) {
925 // 1 : application of the "end of the month" rule
926 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
927
928 $mois = date('m', $datelim);
929 $annee = date('Y', $datelim);
930 if ($mois == 12) {
931 $mois = 1;
932 $annee += 1;
933 } else {
934 $mois += 1;
935 }
936 // We move at the beginning of the next month, and we take a day off
937 $datelim = dol_mktime(12, 0, 0, $mois, 1, $annee);
938 $datelim -= (3600 * 24);
939
940 $datelim += ($cdr_decalage * 3600 * 24);
941 } elseif ($cdr_type == 2 && !empty($cdr_decalage)) {
942 // 2 : application of the rule, the N of the current or next month
943 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
944 $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
945
946 $date_piece = dol_mktime(0, 0, 0, date('m', $datelim), date('d', $datelim), date('Y', $datelim)); // Sans les heures minutes et secondes
947 $date_lim_current = dol_mktime(0, 0, 0, date('m', $datelim), $cdr_decalage, date('Y', $datelim)); // Sans les heures minutes et secondes
948 $date_lim_next = dol_time_plus_duree($date_lim_current, 1, 'm'); // Add 1 month
949
950 $diff = $date_piece - $date_lim_current;
951
952 if ($diff < 0) {
953 $datelim = $date_lim_current;
954 } else {
955 $datelim = $date_lim_next;
956 }
957 } else {
958 return 'Bad value for type_cdr in database for record cond_reglement = '.$cond_reglement;
959 }
960
961 return $datelim;
962 }
963
964 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
976 public function demande_prelevement($fuser, $amount = 0, $type = 'direct-debit', $sourcetype = 'facture', $checkduplicateamongall = 0)
977 {
978 // phpcs:enable
979 global $conf;
980
981 $error = 0;
982
983 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
984
985 if ($this->status > self::STATUS_DRAFT && $this->paye == 0) {
986 require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
987 $bac = new CompanyBankAccount($this->db);
988 $bac->fetch(0, '', $this->socid);
989
990 $sql = "SELECT count(rowid) as nb";
991 $sql .= " FROM ".$this->db->prefix()."prelevement_demande";
992 if ($type == 'bank-transfer') {
993 $sql .= " WHERE fk_facture_fourn = ".((int) $this->id);
994 } else {
995 $sql .= " WHERE fk_facture = ".((int) $this->id);
996 }
997 $sql .= " AND type = 'ban'"; // To exclude record done for some online payments
998 if (empty($checkduplicateamongall)) {
999 $sql .= " AND traite = 0";
1000 }
1001
1002 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
1003
1004 $resql = $this->db->query($sql);
1005 if ($resql) {
1006 $obj = $this->db->fetch_object($resql);
1007 if ($obj && $obj->nb == 0) { // If no request found yet
1008 $now = dol_now();
1009
1010 $totalpaid = $this->getSommePaiement();
1011 $totalcreditnotes = $this->getSumCreditNotesUsed();
1012 $totaldeposits = $this->getSumDepositsUsed();
1013 //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits;
1014
1015 // We can also use bcadd to avoid pb with floating points
1016 // For example print 239.2 - 229.3 - 9.9; does not return 0.
1017 //$resteapayer=bcadd($this->total_ttc,$totalpaid,$conf->global->MAIN_MAX_DECIMALS_TOT);
1018 //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
1019 if (empty($amount)) {
1020 $amount = price2num($this->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1021 }
1022
1023 if (is_numeric($amount) && $amount != 0) {
1024 $sql = 'INSERT INTO '.$this->db->prefix().'prelevement_demande(';
1025 if ($type == 'bank-transfer') {
1026 $sql .= 'fk_facture_fourn, ';
1027 } else {
1028 $sql .= 'fk_facture, ';
1029 }
1030 $sql .= ' amount, date_demande, fk_user_demande, code_banque, code_guichet, number, cle_rib, sourcetype, type, entity)';
1031 $sql .= " VALUES (".((int) $this->id);
1032 $sql .= ", ".((float) price2num($amount));
1033 $sql .= ", '".$this->db->idate($now)."'";
1034 $sql .= ", ".((int) $fuser->id);
1035 $sql .= ", '".$this->db->escape($bac->code_banque)."'";
1036 $sql .= ", '".$this->db->escape($bac->code_guichet)."'";
1037 $sql .= ", '".$this->db->escape($bac->number)."'";
1038 $sql .= ", '".$this->db->escape($bac->cle_rib)."'";
1039 $sql .= ", '".$this->db->escape($sourcetype)."'";
1040 $sql .= ", 'ban'";
1041 $sql .= ", ".((int) $conf->entity);
1042 $sql .= ")";
1043
1044 dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
1045 $resql = $this->db->query($sql);
1046 if (!$resql) {
1047 $this->error = $this->db->lasterror();
1048 dol_syslog(get_class($this).'::demandeprelevement Erreur');
1049 $error++;
1050 }
1051 } else {
1052 $this->error = 'WithdrawRequestErrorNilAmount';
1053 dol_syslog(get_class($this).'::demandeprelevement WithdrawRequestErrorNilAmount');
1054 $error++;
1055 }
1056
1057 if (!$error) {
1058 // Force payment mode of invoice to withdraw
1059 $payment_mode_id = dol_getIdFromCode($this->db, ($type == 'bank-transfer' ? 'VIR' : 'PRE'), 'c_paiement', 'code', 'id', 1);
1060 if ($payment_mode_id > 0) {
1061 $result = $this->setPaymentMethods($payment_mode_id);
1062 }
1063 }
1064
1065 if ($error) {
1066 return -1;
1067 }
1068 return 1;
1069 } else {
1070 $this->error = "A request already exists";
1071 dol_syslog(get_class($this).'::demandeprelevement Can t create a request to generate a direct debit, a request already exists.');
1072 return 0;
1073 }
1074 } else {
1075 $this->error = $this->db->error();
1076 dol_syslog(get_class($this).'::demandeprelevement Error -2');
1077 return -2;
1078 }
1079 } else {
1080 $this->error = "Status of invoice does not allow this";
1081 dol_syslog(get_class($this)."::demandeprelevement ".$this->error." $this->status, $this->paye, $this->mode_reglement_id");
1082 return -3;
1083 }
1084 }
1085
1086
1098 public function makeStripeCardRequest($fuser, $id, $sourcetype = 'facture')
1099 {
1100 // TODO See in sellyoursaas
1101 return 0;
1102 }
1103
1116 public function makeStripeSepaRequest($fuser, $did, $type = 'direct-debit', $sourcetype = 'facture', $service = '', $forcestripe = '')
1117 {
1118 global $conf, $user, $langs;
1119
1120 if ($type != 'bank-transfer' && $type != 'credit-transfer' && !getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) {
1121 return 0;
1122 }
1123 if ($type != 'direct-debit' && !getDolGlobalString('STRIPE_SEPA_CREDIT_TRANSFER')) {
1124 return 0;
1125 }
1126 // Set a default value for service if not provided
1127 if (empty($service)) {
1128 $service = 'StripeTest';
1129 if (getDolGlobalString('STRIPE_LIVE') && !GETPOST('forcesandbox', 'alpha')) {
1130 $service = 'StripeLive';
1131 }
1132 }
1133
1134 $error = 0;
1135
1136 dol_syslog(get_class($this)."::makeStripeSepaRequest start did=".$did." type=".$type." service=".$service." sourcetype=".$sourcetype." forcestripe=".$forcestripe, LOG_DEBUG);
1137
1138 if ($this->status > self::STATUS_DRAFT && $this->paye == 0) {
1139 // Get the default payment mode for BAN payment of the third party
1140 require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
1141 $bac = new CompanyBankAccount($this->db); // Table societe_rib
1142 $result = $bac->fetch(0, '', $this->socid, 1, 'ban');
1143 if ($result <= 0 || empty($bac->id)) {
1144 $this->error = $langs->trans("ThirdpartyHasNoDefaultBanAccount");
1145 $this->errors[] = $this->error;
1146 dol_syslog(get_class($this)."::makeStripeSepaRequest ".$this->error);
1147 return -1;
1148 }
1149
1150 // Load the pending payment request to process (with rowid=$did)
1151 $sql = "SELECT rowid, date_demande, amount, fk_facture, fk_facture_fourn, fk_salary, fk_prelevement_bons";
1152 $sql .= " FROM ".$this->db->prefix()."prelevement_demande";
1153 $sql .= " WHERE rowid = ".((int) $did);
1154 if ($type != 'bank-transfer' && $type != 'credit-transfer') {
1155 $sql .= " AND fk_facture = ".((int) $this->id); // Add a protection to not pay another invoice than current one
1156 }
1157 if ($type != 'direct-debit') {
1158 if ($sourcetype == 'salary') {
1159 $sql .= " AND fk_salary = ".((int) $this->id); // Add a protection to not pay another salary than current one
1160 } else {
1161 $sql .= " AND fk_facture_fourn = ".((int) $this->id); // Add a protection to not pay another invoice than current one
1162 }
1163 }
1164 $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)
1165
1166 dol_syslog(get_class($this)."::makeStripeSepaRequest load requests to process", LOG_DEBUG);
1167 $resql = $this->db->query($sql);
1168 if ($resql) {
1169 $obj = $this->db->fetch_object($resql);
1170 if (!$obj) {
1171 dol_print_error($this->db, 'CantFindRequestWithId');
1172 return -2;
1173 }
1174
1175 // amount to pay
1176 $amount = $obj->amount;
1177
1178 if (is_numeric($amount) && $amount != 0) {
1179 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
1180 $companypaymentmode = new CompanyPaymentMode($this->db); // table societe_rib
1181 $companypaymentmode->fetch($bac->id);
1182
1183 $this->stripechargedone = 0;
1184 $this->stripechargeerror = 0;
1185
1186 $now = dol_now();
1187
1188 $currency = $conf->currency;
1189
1190 $errorforinvoice = 0; // We reset the $errorforinvoice at each invoice loop
1191
1192 $this->fetch_thirdparty();
1193
1194 dol_syslog("makeStripeSepaRequest Process payment request amount=".$amount." thirdparty_id=" . $this->thirdparty->id . ", thirdparty_name=" . $this->thirdparty->name . " ban id=" . $bac->id, LOG_DEBUG);
1195
1196 //$alreadypayed = $this->getSommePaiement();
1197 //$amount_credit_notes_included = $this->getSumCreditNotesUsed();
1198 //$amounttopay = $this->total_ttc - $alreadypayed - $amount_credit_notes_included;
1199 $amounttopay = $amount;
1200
1201 // Correct the amount according to unit of currency
1202 // See https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support
1203 $arrayzerounitcurrency = ['BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'];
1204 $amountstripe = $amounttopay;
1205 if (!in_array($currency, $arrayzerounitcurrency)) {
1206 $amountstripe = $amountstripe * 100;
1207 }
1208
1209 $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.
1210 if (!($fk_bank_account > 0)) {
1211 $error++;
1212 $errorforinvoice++;
1213 dol_syslog("makeStripeSepaRequest Error no bank account defined for Stripe payments", LOG_ERR);
1214 $this->errors[] = "Error bank account for Stripe payments not defined into Stripe module";
1215 }
1216
1217 $this->db->begin();
1218
1219 // Create a prelevement_bon
1220 require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php';
1221 $bon = new BonPrelevement($this->db);
1222 if (!$error) {
1223 if (empty($obj->fk_prelevement_bons)) {
1224 // This creates a record into llx_prelevement_bons and updates link with llx_prelevement_demande
1225 $nbinvoices = $bon->create(0, 0, 'real', 'ALL', '', 0, $type, $did, $fk_bank_account);
1226 if ($nbinvoices <= 0) {
1227 $error++;
1228 $errorforinvoice++;
1229 dol_syslog("makeStripeSepaRequest Error on BonPrelevement creation", LOG_ERR);
1230 $this->errors[] = "Error on BonPrelevement creation";
1231 }
1232 /*
1233 if (!$error) {
1234 // Update the direct debit payment request of the processed request to save the id of the prelevement_bon
1235 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
1236 $sql .= " fk_prelevement_bons = ".((int) $bon->id);
1237 $sql .= " WHERE rowid = ".((int) $did);
1238
1239 $result = $this->db->query($sql);
1240 if ($result < 0) {
1241 $error++;
1242 $this->errors[] = "Error on updating fk_prelevement_bons to ".$bon->id;
1243 }
1244 }
1245 */
1246 } else {
1247 $error++;
1248 $errorforinvoice++;
1249 dol_syslog("makeStripeSepaRequest Error Line already part of a bank payment order", LOG_ERR);
1250 $this->errors[] = "The line is already included into a bank payment order. Delete the bank payment order first.";
1251 }
1252 }
1253
1254 if (!$error) {
1255 if ($amountstripe > 0) {
1256 try {
1257 global $savstripearrayofkeysbyenv;
1258 global $stripearrayofkeysbyenv;
1259 $servicestatus = 0;
1260 if ($service == 'StripeLive') {
1261 $servicestatus = 1;
1262 }
1263
1264 //var_dump($companypaymentmode);
1265 dol_syslog("makeStripeSepaRequest We will try to pay with companypaymentmodeid=" . $companypaymentmode->id . " stripe_card_ref=" . $companypaymentmode->stripe_card_ref . " mode=" . $companypaymentmode->status, LOG_DEBUG);
1266
1267 $thirdparty = new Societe($this->db);
1268 $resultthirdparty = $thirdparty->fetch($this->socid);
1269
1270 include_once DOL_DOCUMENT_ROOT . '/stripe/class/stripe.class.php'; // This include the include of htdocs/stripe/config.php
1271 // So it inits or erases the $stripearrayofkeysbyenv
1272 $stripe = new Stripe($this->db);
1273
1274 if (empty($savstripearrayofkeysbyenv)) {
1275 $savstripearrayofkeysbyenv = $stripearrayofkeysbyenv;
1276 }
1277 dol_syslog("makeStripeSepaRequest Current Stripe environment is " . $stripearrayofkeysbyenv[$servicestatus]['publishable_key']);
1278 dol_syslog("makeStripeSepaRequest Current Saved Stripe environment is ".$savstripearrayofkeysbyenv[$servicestatus]['publishable_key']);
1279
1280 $foundalternativestripeaccount = '';
1281
1282 // Force stripe to another value (by default this value is empty)
1283 if (! empty($forcestripe)) {
1284 dol_syslog("makeStripeSepaRequest A dedicated stripe account was forced, so we switch to it.");
1285
1286 $tmparray = explode('@', $forcestripe);
1287 if (! empty($tmparray[1])) {
1288 $tmparray2 = explode(':', $tmparray[1]);
1289 if (! empty($tmparray2[1])) {
1290 $stripearrayofkeysbyenv[$servicestatus]["publishable_key"] = $tmparray2[0];
1291 $stripearrayofkeysbyenv[$servicestatus]["secret_key"] = $tmparray2[1];
1292
1293 $stripearrayofkeys = $stripearrayofkeysbyenv[$servicestatus];
1294 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1295
1296 $foundalternativestripeaccount = $tmparray[0]; // Store the customer id
1297
1298 dol_syslog("makeStripeSepaRequest We use now customer=".$foundalternativestripeaccount." publishable_key=".$stripearrayofkeys['publishable_key'], LOG_DEBUG);
1299 }
1300 }
1301
1302 if (! $foundalternativestripeaccount) {
1303 $stripearrayofkeysbyenv = $savstripearrayofkeysbyenv;
1304
1305 $stripearrayofkeys = $savstripearrayofkeysbyenv[$servicestatus];
1306 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1307 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);
1308 }
1309 } else {
1310 $stripearrayofkeysbyenv = $savstripearrayofkeysbyenv;
1311
1312 $stripearrayofkeys = $savstripearrayofkeysbyenv[$servicestatus];
1313 \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']);
1314 dol_syslog("makeStripeSepaRequest No dedicated Stripe Account requested, so we use global one, so ".$stripearrayofkeys['publishable_key'], LOG_DEBUG);
1315 }
1316
1317 $stripeacc = $stripe->getStripeAccount($service, $this->socid); // Get Stripe OAuth connect account if it exists (no network access here)
1318
1319 if ($foundalternativestripeaccount) {
1320 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
1321 $customer = \Stripe\Customer::retrieve(array('id' => "$foundalternativestripeaccount", 'expand[]' => 'sources'));
1322 } else {
1323 $customer = \Stripe\Customer::retrieve(array('id' => "$foundalternativestripeaccount", 'expand[]' => 'sources'), array("stripe_account" => $stripeacc));
1324 }
1325 } else {
1326 $customer = $stripe->customerStripe($thirdparty, $stripeacc, $servicestatus, 0);
1327 if (empty($customer) && ! empty($stripe->error)) {
1328 $this->errors[] = $stripe->error;
1329 }
1330 /*if (!empty($customer) && empty($customer->sources)) {
1331 $customer = null;
1332 $this->errors[] = '\Stripe\Customer::retrieve did not returned the sources';
1333 }*/
1334 }
1335
1336 // $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)
1337 // $nbdaysbeforeendoftries = (empty($conf->global->SELLYOURSAAS_NBDAYSBEFOREENDOFTRIES) ? 35 : $conf->global->SELLYOURSAAS_NBDAYSBEFOREENDOFTRIES);
1338 $postactionmessages = [];
1339
1340 if ($resultthirdparty > 0 && !empty($customer)) {
1341 if (!$error) { // Payment was not canceled
1342 $stripecard = null;
1343 if ($companypaymentmode->type == 'ban') {
1344 // Check into societe_rib if a payment mode for Stripe and ban payment exists
1345 // To make a Stripe SEPA payment request, we must have the payment mode source already saved into societe_rib and retrieved with ->sepaStripe
1346 // The payment mode source is created when we create the bank account on Stripe with paymentmodes.php?action=create
1347 $stripecard = $stripe->sepaStripe($customer, $companypaymentmode, $stripeacc, $servicestatus, 0);
1348 } else {
1349 $error++;
1350 $this->error = 'The payment mode type is not "ban"';
1351 }
1352
1353 if ($stripecard) { // Can be src_... (for sepa) or pm_... (new card mode). Note that card_... (old card mode) should not happen here.
1354 $FULLTAG = 'DID='.$did.'-INV=' . $this->id . '-CUS=' . $thirdparty->id;
1355 $description = 'Stripe payment from makeStripeSepaRequest: ' . $FULLTAG . ' did='.$did.' ref=' . $this->ref;
1356
1357 $stripefailurecode = '';
1358 $stripefailuremessage = '';
1359 $stripefailuredeclinecode = '';
1360
1361 // Using new SCA method
1362 dol_syslog("* Create payment on SEPA " . $stripecard->id . ", amounttopay=" . $amounttopay . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG);
1363
1364 // Create payment intent and charge payment (confirmnow = true)
1365 $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $this, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1, 1, $did);
1366
1367 $charge = new stdClass();
1368
1369 if ($paymentintent->status === 'succeeded' || $paymentintent->status === 'processing') {
1370 $charge->status = 'ok';
1371 $charge->id = $paymentintent->id;
1372 $charge->customer = $customer->id;
1373 } elseif ($paymentintent->status === 'requires_action') {
1374 //paymentintent->status may be => 'requires_action' (no error in such a case)
1375 dol_syslog(var_export($paymentintent, true), LOG_DEBUG);
1376
1377 $charge->status = 'failed';
1378 $charge->customer = $customer->id;
1379 $charge->failure_code = $stripe->code;
1380 $charge->failure_message = $stripe->error;
1381 $charge->failure_declinecode = $stripe->declinecode;
1382 $stripefailurecode = $stripe->code;
1383 $stripefailuremessage = 'Action required. Contact the support at ';// . $conf->global->SELLYOURSAAS_MAIN_EMAIL;
1384 $stripefailuredeclinecode = $stripe->declinecode;
1385 } else {
1386 dol_syslog(var_export($paymentintent, true), LOG_DEBUG);
1387
1388 $charge->status = 'failed';
1389 $charge->customer = $customer->id;
1390 $charge->failure_code = $stripe->code;
1391 $charge->failure_message = $stripe->error;
1392 $charge->failure_declinecode = $stripe->declinecode;
1393 $stripefailurecode = $stripe->code;
1394 $stripefailuremessage = $stripe->error;
1395 $stripefailuredeclinecode = $stripe->declinecode;
1396 }
1397
1398 //var_dump("stripefailurecode=".$stripefailurecode." stripefailuremessage=".$stripefailuremessage." stripefailuredeclinecode=".$stripefailuredeclinecode);
1399 //exit;
1400
1401
1402 // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...)
1403 if (empty($charge) || $charge->status == 'failed') {
1404 dol_syslog('Failed to charge payment mode ' . $stripecard->id . ' stripefailurecode=' . $stripefailurecode . ' stripefailuremessage=' . $stripefailuremessage . ' stripefailuredeclinecode=' . $stripefailuredeclinecode, LOG_WARNING);
1405
1406 // Save a stripe payment was in error
1407 $this->stripechargeerror++;
1408
1409 $error++;
1410 $errorforinvoice++;
1411 $errmsg = $langs->trans("FailedToChargeCard");
1412 if (!empty($charge)) {
1413 if ($stripefailuredeclinecode == 'authentication_required') {
1414 $errauthenticationmessage = $langs->trans("ErrSCAAuthentication");
1415 $errmsg = $errauthenticationmessage;
1416 } elseif (in_array($stripefailuredeclinecode, ['insufficient_funds', 'generic_decline'])) {
1417 $errmsg .= ': ' . $charge->failure_code;
1418 $errmsg .= ($charge->failure_message ? ' - ' : '') . ' ' . $charge->failure_message;
1419 if (empty($stripefailurecode)) {
1420 $stripefailurecode = $charge->failure_code;
1421 }
1422 if (empty($stripefailuremessage)) {
1423 $stripefailuremessage = $charge->failure_message;
1424 }
1425 } else {
1426 $errmsg .= ': failure_code=' . $charge->failure_code;
1427 $errmsg .= ($charge->failure_message ? ' - ' : '') . ' failure_message=' . $charge->failure_message;
1428 if (empty($stripefailurecode)) {
1429 $stripefailurecode = $charge->failure_code;
1430 }
1431 if (empty($stripefailuremessage)) {
1432 $stripefailuremessage = $charge->failure_message;
1433 }
1434 }
1435 } else {
1436 $errmsg .= ': ' . $stripefailurecode . ' - ' . $stripefailuremessage;
1437 $errmsg .= ($stripefailuredeclinecode ? ' - ' . $stripefailuredeclinecode : '');
1438 }
1439
1440 $description = 'Stripe payment ERROR from makeStripeSepaRequest: ' . $FULLTAG;
1441 $postactionmessages[] = $errmsg . ' (' . $stripearrayofkeys['publishable_key'] . ')';
1442 $this->errors[] = $errmsg;
1443 } else {
1444 dol_syslog('Successfuly request '.$type.' '.$stripecard->id);
1445
1446 $postactionmessages[] = 'Success to request '.$type.' (' . $charge->id . ' with ' . $stripearrayofkeys['publishable_key'] . ')';
1447
1448 // Save a stripe payment was done in real life so later we will be able to force a commit on recorded payments
1449 // even if in batch mode (method doTakePaymentStripe), we will always make all action in one transaction with a forced commit.
1450 $this->stripechargedone++;
1451
1452 // Default description used for label of event. Will be overwrite by another value later.
1453 $description = 'Stripe payment request OK (' . $charge->id . ') from makeStripeSepaRequest: ' . $FULLTAG;
1454 }
1455
1456 $object = $this;
1457
1458 // Track an event
1459 if (empty($charge) || $charge->status == 'failed') {
1460 $actioncode = 'PAYMENT_STRIPE_KO';
1461 $extraparams = $stripefailurecode;
1462 $extraparams .= (($extraparams && $stripefailuremessage) ? ' - ' : '') . $stripefailuremessage;
1463 $extraparams .= (($extraparams && $stripefailuredeclinecode) ? ' - ' : '') . $stripefailuredeclinecode;
1464 } else {
1465 $actioncode = 'PAYMENT_STRIPE_OK';
1466 $extraparams = '';
1467 }
1468 } else {
1469 $error++;
1470 $errorforinvoice++;
1471 dol_syslog("No ban payment method found for this stripe customer " . $customer->id, LOG_WARNING);
1472 $this->errors[] = 'Failed to get direct debit payment method for stripe customer = ' . $customer->id;
1473
1474 $description = 'Failed to find or use the payment mode - no ban defined for the thirdparty account';
1475 $stripefailurecode = 'BADPAYMENTMODE';
1476 $stripefailuremessage = 'Failed to find or use the payment mode - no ban defined for the thirdparty account';
1477 $postactionmessages[] = $description . ' (' . $stripearrayofkeys['publishable_key'] . ')';
1478
1479 $object = $this;
1480
1481 $actioncode = 'PAYMENT_STRIPE_KO';
1482 $extraparams = '';
1483 }
1484 } else {
1485 // If error because payment was canceled for a logical reason, we do nothing (no event added)
1486 $description = '';
1487 $stripefailurecode = '';
1488 $stripefailuremessage = '';
1489
1490 $object = $this;
1491
1492 $actioncode = '';
1493 $extraparams = '';
1494 }
1495 } else { // Else of the if ($resultthirdparty > 0 && ! empty($customer)) {
1496 if ($resultthirdparty <= 0) {
1497 dol_syslog('SellYourSaasUtils Failed to load customer for thirdparty_id = ' . $thirdparty->id, LOG_WARNING);
1498 $this->errors[] = 'Failed to load Stripe account for thirdparty_id = ' . $thirdparty->id;
1499 } else { // $customer stripe not found
1500 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);
1501 $this->errors[] = 'Failed to get Stripe account id for thirdparty_id = ' . $thirdparty->id . " in mode " . $servicestatus . " in Stripe env " . $stripearrayofkeysbyenv[$servicestatus]['publishable_key'];
1502 }
1503 $error++;
1504 $errorforinvoice++;
1505
1506 $description = 'Failed to find or use your payment mode (no payment mode for this customer id)';
1507 $stripefailurecode = 'BADPAYMENTMODE';
1508 $stripefailuremessage = 'Failed to find or use your payment mode (no payment mode for this customer id)';
1509 $postactionmessages = [];
1510
1511 $object = $this;
1512
1513 $actioncode = 'PAYMENT_STRIPE_KO';
1514 $extraparams = '';
1515 }
1516
1517 if ($description) {
1518 dol_syslog("* Record event for credit transfer or direct debit request result - " . $description);
1519 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1520
1521 // Insert record of payment (success or error)
1522 $actioncomm = new ActionComm($this->db);
1523
1524 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
1525 $actioncomm->code = 'AC_' . $actioncode;
1526 $actioncomm->label = $description;
1527 $actioncomm->note_private = implode(",\n", $postactionmessages);
1528 $actioncomm->fk_project = $this->fk_project;
1529 $actioncomm->datep = $now;
1530 $actioncomm->datef = $now;
1531 $actioncomm->percentage = -1; // Not applicable
1532 $actioncomm->socid = $thirdparty->id;
1533 $actioncomm->contactid = 0;
1534 $actioncomm->authorid = $user->id; // User saving action
1535 $actioncomm->userownerid = $user->id; // Owner of action
1536 // Fields when action is a real email (content is already into note)
1537 /*$actioncomm->email_msgid = $object->email_msgid;
1538 $actioncomm->email_from = $object->email_from;
1539 $actioncomm->email_sender= $object->email_sender;
1540 $actioncomm->email_to = $object->email_to;
1541 $actioncomm->email_tocc = $object->email_tocc;
1542 $actioncomm->email_tobcc = $object->email_tobcc;
1543 $actioncomm->email_subject = $object->email_subject;
1544 $actioncomm->errors_to = $object->errors_to;*/
1545 $actioncomm->fk_element = $this->id;
1546 $actioncomm->elementtype = $this->element;
1547 $actioncomm->extraparams = dol_trunc($extraparams, 250);
1548
1549 $actioncomm->create($user);
1550 }
1551
1552 $this->description = $description;
1553 $this->postactionmessages = $postactionmessages;
1554 } catch (Exception $e) {
1555 $error++;
1556 $errorforinvoice++;
1557 dol_syslog('Error ' . $e->getMessage(), LOG_ERR);
1558 $this->errors[] = 'Error ' . $e->getMessage();
1559 }
1560 } else { // If remain to pay is null
1561 $error++;
1562 $errorforinvoice++;
1563 dol_syslog("Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?", LOG_WARNING);
1564 $this->errors[] = "Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?";
1565 }
1566 }
1567
1568 // Set status of the order to "Transferred" with method 'api'
1569 if (!$error && !$errorforinvoice) {
1570 $result = $bon->set_infotrans($user, $now, 3);
1571 if ($result < 0) {
1572 $error++;
1573 $errorforinvoice++;
1574 dol_syslog("Error on BonPrelevement creation", LOG_ERR);
1575 $this->errors[] = "Error on BonPrelevement creation";
1576 }
1577 }
1578
1579 if (!$error && !$errorforinvoice) {
1580 // Update the direct debit payment request of the processed invoice to save the id of the prelevement_bon
1581 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
1582 $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."',";
1583 $sql .= " ext_payment_site = '".$this->db->escape($service)."'";
1584 $sql .= " WHERE rowid = ".((int) $did);
1585
1586 dol_syslog(get_class($this)."::makeStripeSepaRequest update to save stripe paymentintent ids", LOG_DEBUG);
1587 $resql = $this->db->query($sql);
1588 if (!$resql) {
1589 $this->error = $this->db->lasterror();
1590 dol_syslog(get_class($this).'::makeStripeSepaRequest Erreur');
1591 $error++;
1592 }
1593 }
1594
1595 if (!$error && !$errorforinvoice) {
1596 $this->db->commit();
1597 } else {
1598 $this->db->rollback();
1599 }
1600 } else {
1601 $this->error = 'WithdrawRequestErrorNilAmount';
1602 dol_syslog(get_class($this).'::makeStripeSepaRequest WithdrawRequestErrorNilAmount');
1603 $error++;
1604 }
1605
1606 /*
1607 if (!$error) {
1608 // Force payment mode of the invoice to withdraw
1609 $payment_mode_id = dol_getIdFromCode($this->db, ($type == 'bank-transfer' ? 'VIR' : 'PRE'), 'c_paiement', 'code', 'id', 1);
1610 if ($payment_mode_id > 0) {
1611 $result = $this->setPaymentMethods($payment_mode_id);
1612 }
1613 }*/
1614
1615 if ($error) {
1616 return -1;
1617 }
1618 return 1;
1619 } else {
1620 $this->error = $this->db->error();
1621 dol_syslog(get_class($this).'::makeStripeSepaRequest Erreur -2');
1622 return -2;
1623 }
1624 } else {
1625 $this->error = "Status of invoice does not allow this";
1626 dol_syslog(get_class($this)."::makeStripeSepaRequest ".$this->error." ".$this->status." ,".$this->paye.", ".$this->mode_reglement_id, LOG_WARNING);
1627 return -3;
1628 }
1629 }
1630
1631 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1639 public function demande_prelevement_delete($fuser, $did)
1640 {
1641 // phpcs:enable
1642 $sql = 'DELETE FROM '.$this->db->prefix().'prelevement_demande';
1643 $sql .= ' WHERE rowid = '.((int) $did);
1644 $sql .= ' AND traite = 0';
1645 if ($this->db->query($sql)) {
1646 return 0;
1647 } else {
1648 $this->error = $this->db->lasterror();
1649 dol_syslog(get_class($this).'::demande_prelevement_delete Error '.$this->error);
1650 return -1;
1651 }
1652 }
1653
1659 public function buildEPCQrCodeString()
1660 {
1661 global $mysoc;
1662
1663 // Convert total_ttc to a string with 2 decimal places
1664 $totalTTCString = number_format($this->total_ttc, 2, '.', '');
1665
1666 // Initialize an array to hold the lines of the QR code
1667 $lines = array();
1668
1669 // Add the standard elements to the QR code
1670 $lines = [
1671 'BCD', // Service Tag (optional)
1672 '002', // Version (optional)
1673 '1', // Character set (optional)
1674 'SCT', // Identification (optional)
1675 ];
1676
1677 // Add the bank account information
1678 include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
1679 $bankAccount = new Account($this->db);
1680 if ($this->fk_account > 0) {
1681 $bankAccount->fetch($this->fk_account);
1682 $lines[] = $bankAccount->bic; //BIC (required)
1683 $lines[] = $mysoc->name; //Name (required)
1684 $lines[] = $bankAccount->iban; //IBAN (required)
1685 } else {
1686 $lines[] = ""; //BIC (required)
1687 $lines[] = $mysoc->name; //Name (required)
1688 $lines[] = ""; //IBAN (required)
1689 }
1690
1691 // Add the amount and reference
1692 $lines[] = 'EUR' . $totalTTCString; // Amount (optional)
1693 $lines[] = ''; // Payment reference (optional)
1694 $lines[] = $this->ref; // Remittance Information (optional)
1695
1696 // Join the lines with newline characters and return the result
1697 return implode("\n", $lines);
1698 }
1704 public function buildZATCAQRString()
1705 {
1706 global $conf, $mysoc;
1707
1708 $tmplang = new Translate('', $conf);
1709 $tmplang->setDefaultLang('en_US');
1710 $tmplang->load("main");
1711
1712 $datestring = dol_print_date($this->date, 'dayhourrfc');
1713 //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
1714 //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
1715 $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
1716 $pricetaxstring = price2num($this->total_tva, 2, 1);
1717
1718 /*
1719 $name = implode(unpack("H*", $this->thirdparty->name));
1720 $vatnumber = implode(unpack("H*", $this->thirdparty->tva_intra));
1721 $date = implode(unpack("H*", $datestring));
1722 $pricewithtax = implode(unpack("H*", price2num($pricewithtaxstring, 2)));
1723 $pricetax = implode(unpack("H*", $pricetaxstring));
1724
1725 //var_dump(strlen($this->thirdparty->name));
1726 //var_dump(str_pad(dechex('9'), 2, '0', STR_PAD_LEFT));
1727 //var_dump($this->thirdparty->name);
1728 //var_dump(implode(unpack("H*", $this->thirdparty->name)));
1729 //var_dump(price($this->total_tva, 0, $tmplang, 0, -1, 2));
1730
1731 $s = '01'.str_pad(dechex(strlen($this->thirdparty->name)), 2, '0', STR_PAD_LEFT).$name;
1732 $s .= '02'.str_pad(dechex(strlen($this->thirdparty->tva_intra)), 2, '0', STR_PAD_LEFT).$vatnumber;
1733 $s .= '03'.str_pad(dechex(strlen($datestring)), 2, '0', STR_PAD_LEFT).$date;
1734 $s .= '04'.str_pad(dechex(strlen($pricewithtaxstring)), 2, '0', STR_PAD_LEFT).$pricewithtax;
1735 $s .= '05'.str_pad(dechex(strlen($pricetaxstring)), 2, '0', STR_PAD_LEFT).$pricetax;
1736 $s .= ''; // Hash of xml invoice
1737 $s .= ''; // ecda signature
1738 $s .= ''; // ecda public key
1739 $s .= ''; // ecda signature of public key stamp
1740 */
1741
1742 // Using TLV format
1743 $s = pack('C1', 1).pack('C1', strlen($mysoc->name)).$mysoc->name;
1744 $s .= pack('C1', 2).pack('C1', strlen($mysoc->tva_intra)).$mysoc->tva_intra;
1745 $s .= pack('C1', 3).pack('C1', strlen($datestring)).$datestring;
1746 $s .= pack('C1', 4).pack('C1', strlen($pricewithtaxstring)).$pricewithtaxstring;
1747 $s .= pack('C1', 5).pack('C1', strlen($pricetaxstring)).$pricetaxstring;
1748 $s .= ''; // Hash of xml invoice
1749 $s .= ''; // ecda signature
1750 $s .= ''; // ecda public key
1751 $s .= ''; // ecda signature of public key stamp
1752
1753 $s = base64_encode($s);
1754
1755 return $s;
1756 }
1757
1758
1765 {
1766 global $conf, $mysoc;
1767
1768 $tmplang = new Translate('', $conf);
1769 $tmplang->setDefaultLang('en_US');
1770 $tmplang->load("main");
1771
1772 $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
1773 $pricetaxstring = price2num($this->total_tva, 2, 1);
1774
1775 $complementaryinfo = '';
1776 /*
1777 Example: //S1/10/10201409/11/190512/20/1400.000-53/30/106017086/31/180508/32/7.7/40/2:10;0:30
1778 /10/ Numéro de facture – 10201409
1779 /11/ Date de facture – 12.05.2019
1780 /20/ Référence client – 1400.000-53
1781 /30/ Numéro IDE pour la TVA – CHE-106.017.086 TVA
1782 /31/ Date de la prestation pour la comptabilisation de la TVA – 08.05.2018
1783 /32/ Taux de TVA sur le montant total de la facture – 7.7%
1784 /40/ Conditions – 2% d’escompte à 10 jours, paiement net à 30 jours
1785 */
1786 $datestring = dol_print_date($this->date, '%y%m%d');
1787 //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
1788 //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
1789 $complementaryinfo = '//S1/10/'.str_replace('/', '', $this->ref).'/11/'.$datestring;
1790 if ($this->ref_client) {
1791 $complementaryinfo .= '/20/'.$this->ref_client;
1792 }
1793 if ($this->thirdparty->tva_intra) {
1794 $complementaryinfo .= '/30/'.$this->thirdparty->tva_intra;
1795 }
1796
1797 include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
1798 $bankaccount = new Account($this->db);
1799
1800 // Header
1801 $s = '';
1802 $s .= "SPC\n";
1803 $s .= "0200\n";
1804 $s .= "1\n";
1805 // Info Seller ("Compte / Payable à")
1806 if ($this->fk_account > 0) {
1807 // Bank BAN if country is LI or CH. TODO Add a test to check than IBAN start with CH or LI
1808 $bankaccount->fetch($this->fk_account);
1809 $s .= $bankaccount->iban."\n";
1810 } else {
1811 $s .= "\n";
1812 }
1813 if ($bankaccount->id > 0 && getDolGlobalString('PDF_SWISS_QRCODE_USE_OWNER_OF_ACCOUNT_AS_CREDITOR')) {
1814 // If a bank account is provided and we ask to use it as creditor, we use the bank address
1815 // 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 ?
1816 $s .= "S\n";
1817 $s .= dol_trunc($bankaccount->proprio, 70, 'right', 'UTF-8', 1)."\n";
1818 $addresslinearray = explode("\n", $bankaccount->owner_address);
1819 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1820 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1821 /*$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
1822 $s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
1823 $s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";*/
1824 } else {
1825 $s .= "S\n";
1826 $s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
1827 $addresslinearray = explode("\n", $mysoc->address);
1828 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1829 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1830 $s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
1831 $s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
1832 $s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
1833 }
1834 // Final seller (Ultimate seller) ("Créancier final" = "En faveur de")
1835 $s .= "\n";
1836 $s .= "\n";
1837 $s .= "\n";
1838 $s .= "\n";
1839 $s .= "\n";
1840 $s .= "\n";
1841 $s .= "\n";
1842 // Amount of payment (to do?)
1843 $s .= price($pricewithtaxstring, 0, 'none', 0, 0, 2)."\n";
1844 $s .= ($this->multicurrency_code ? $this->multicurrency_code : $conf->currency)."\n";
1845 // Buyer
1846 $s .= "S\n";
1847 $s .= dol_trunc($this->thirdparty->name, 70, 'right', 'UTF-8', 1)."\n";
1848 $addresslinearray = explode("\n", $this->thirdparty->address);
1849 $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
1850 $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
1851 $s .= dol_trunc($this->thirdparty->zip, 16, 'right', 'UTF-8', 1)."\n";
1852 $s .= dol_trunc($this->thirdparty->town, 35, 'right', 'UTF-8', 1)."\n";
1853 $s .= dol_trunc($this->thirdparty->country_code, 2, 'right', 'UTF-8', 1)."\n";
1854 // ID of payment
1855 $s .= "NON\n"; // NON or QRR
1856 $s .= "\n"; // QR Code reference if previous field is QRR
1857 // Free text
1858 if ($complementaryinfo) {
1859 $s .= $complementaryinfo."\n";
1860 } else {
1861 $s .= "\n";
1862 }
1863 $s .= "EPD\n";
1864 // More text, complementary info
1865 if ($complementaryinfo) {
1866 $s .= $complementaryinfo."\n";
1867 }
1868 $s .= "\n";
1869 //var_dump($s);exit;
1870 return $s;
1871 }
1872}
1873
1874
1875
1876require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
1877
1882{
1887 public $label;
1888
1893 public $ref; // Product ref (deprecated)
1898 public $libelle; // Product label (deprecated)
1899
1904 public $product_type = 0;
1905
1910 public $product_ref;
1911
1916 public $product_label;
1917
1922 public $product_desc;
1923
1928 public $qty;
1929
1934 public $subprice;
1935
1941 public $price;
1942
1947 public $fk_product;
1948
1953 public $vat_src_code;
1954
1959 public $tva_tx;
1960
1965 public $localtax1_tx;
1966
1971 public $localtax2_tx;
1972
1978 public $localtax1_type;
1979
1985 public $localtax2_type;
1986
1991 public $remise_percent;
1992
1998 public $remise;
1999
2004 public $total_ht;
2005
2010 public $total_tva;
2011
2016 public $total_localtax1;
2017
2022 public $total_localtax2;
2023
2028 public $total_ttc;
2029
2030 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
2031 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
2032
2033 public $buy_price_ht;
2034 public $buyprice; // For backward compatibility
2035 public $pa_ht; // For backward compatibility
2036
2037 public $marge_tx;
2038 public $marque_tx;
2039
2046 public $info_bits = 0;
2047
2056 public $special_code = 0;
2057
2062
2067
2068 public $fk_accounting_account;
2069}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:637
$object ref
Definition info.php:79
Class to manage bank accounts.
Class to manage agenda events (actions)
Class to manage withdrawal receipts.
Superclass for invoices 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,...
demande_prelevement($fuser, $amount=0, $type='direct-debit', $sourcetype='facture', $checkduplicateamongall=0)
Create a withdrawal request for a direct debit order or a credit transfer order.
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 dispatched into bookkeeping.
const TYPE_PROFORMA
Proforma invoice.
buildSwitzerlandQRString()
Build string for QR-Bill (Switzerland)
const STATUS_VALIDATED
Validated (need to be paid)
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.
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.
getListOfPayments($filtertype='', $multicurrency=0)
Return list of payments.
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,...
$label
Custom label of line.
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.
trait CommonIncoterm
Superclass for incoterm classes.
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).
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 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.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:137
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1991