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