dolibarr 24.0.1
bonprelevement.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2004-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
5 * Copyright (C) 2010-2014 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2014-2016 Ferran Marcet <fmarcet@2byte.es>
7 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
8 * Copyright (C) 2019 JC Prieto <jcprieto@virtual20.com><prietojc@gmail.com>
9 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
10 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
32require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
33require_once DOL_DOCUMENT_ROOT . '/core/lib/bank.lib.php';
34require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
35require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
36require_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php';
37require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture.class.php';
38require_once DOL_DOCUMENT_ROOT . '/fourn/class/paiementfourn.class.php';
39require_once DOL_DOCUMENT_ROOT . '/salaries/class/salary.class.php';
40require_once DOL_DOCUMENT_ROOT . '/salaries/class/paymentsalary.class.php';
41require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
42require_once DOL_DOCUMENT_ROOT . '/user/class/userbankaccount.class.php';
43
44
49{
53 public $element = 'widthdraw';
54
58 public $table_element = 'prelevement_bons';
59
63 public $picto = 'payment';
64
68 public $date_echeance;
72 public $raison_sociale;
76 public $reference_remise;
80 public $emetteur_code_guichet;
84 public $emetteur_numero_compte;
88 public $emetteur_code_banque;
92 public $emetteur_number_key;
96 public $sepa_xml_pti_in_ctti;
97
101 public $emetteur_iban;
105 public $emetteur_bic;
109 public $emetteur_ics;
110
114 public $user_trans;
118 public $user_credit;
119
123 public $total;
124
128 public $fetched;
129
130 public $labelStatus = array();
131
135 public $factures = array();
136
140 public $methodes_trans = array();
141
145 public $invoice_in_error = array();
146
150 public $thirdparty_in_error = array();
151
155 public $file;
156
160 public $filename;
161
162 const STATUS_DRAFT = 0;
163 const STATUS_TRANSFERED = 1;
164 const STATUS_CREDITED = 2; // STATUS_CREDITED and STATUS_DEBITED is same. Difference is in ->type
165 const STATUS_DEBITED = 2; // STATUS_CREDITED and STATUS_DEBITED is same. Difference is in ->type
166 const STATUS_CANCELED = 9;
167
168
208 // BEGIN MODULEBUILDER PROPERTIES
212 public $fields = array(
213 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'visible' => 0,),
214 'ref' => array('type' => 'varchar(12)', 'label' => 'Ref', 'enabled' => 1, 'position' => 15, 'notnull' => 0, 'visible' => -1, 'csslist' => 'tdoverflowmax150', 'showoncombobox' => 1,),
215 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 25, 'notnull' => 0, 'visible' => -1,),
216 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'position' => 30, 'notnull' => 0, 'visible' => -1,),
217 'statut' => array('type' => 'smallint(6)', 'label' => 'Statut', 'enabled' => 1, 'position' => 500, 'notnull' => 0, 'visible' => -1, 'arrayofkeyval' => array(0 => 'Wait', 1 => 'Transfered', 2 => 'Credited')),
218 'credite' => array('type' => 'smallint(6)', 'label' => 'Credite', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => -1,),
219 'note' => array('type' => 'text', 'label' => 'Note', 'enabled' => 1, 'position' => 45, 'notnull' => 0, 'visible' => -1,),
220 'date_trans' => array('type' => 'datetime', 'label' => 'TransData', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => -1,),
221 'method_trans' => array('type' => 'smallint(6)', 'label' => 'Methodtrans', 'enabled' => 1, 'position' => 55, 'notnull' => 0, 'visible' => -1,),
222 'fk_user_trans' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fkusertrans', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => -1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150',),
223 'date_credit' => array('type' => 'datetime', 'label' => 'CreditDate', 'enabled' => 1, 'position' => 65, 'notnull' => 0, 'visible' => -1,),
224 'fk_user_credit' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fkusercredit', 'enabled' => 1, 'position' => 70, 'notnull' => 0, 'visible' => -1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150',),
225 'type' => array('type' => 'varchar(16)', 'label' => 'Type', 'enabled' => 1, 'position' => 75, 'notnull' => 0, 'visible' => -1,),
226 'fk_bank_account' => array('type' => 'integer', 'label' => 'Fkbankaccount', 'enabled' => 1, 'position' => 80, 'notnull' => 0, 'visible' => -1, 'css' => 'maxwidth500 widthcentpercentminusxx',),
227 );
231 public $rowid;
235 public $ref;
239 public $datec;
240
244 public $amount;
245
250 public $statut;
254 public $status;
255
259 public $credite;
260
265 public $note;
266
270 public $note_private;
271
275 public $date_trans;
279 public $method_trans;
283 public $fk_user_trans;
287 public $date_credit;
291 public $fk_user_credit;
295 public $type;
299 public $fk_bank_account;
300 // END MODULEBUILDER PROPERTIES
301
302
303
309 public function __construct($db)
310 {
311 $this->db = $db;
312
313 $this->filename = '';
314
315 $this->date_echeance = dol_now();
316 $this->raison_sociale = "";
317 $this->reference_remise = "";
318
319 $this->emetteur_code_guichet = "";
320 $this->emetteur_numero_compte = "";
321 $this->emetteur_code_banque = "";
322 $this->emetteur_number_key = "";
323 $this->sepa_xml_pti_in_ctti = false;
324
325 $this->emetteur_iban = "";
326 $this->emetteur_bic = "";
327 $this->emetteur_ics = "";
328
329 $this->factures = array();
330
331 $this->methodes_trans = array(0 => 'Internet', 2 => 'Email', 3 => 'Api');
332
333 $this->fetched = 0;
334 }
335
336 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
356 public function addWithdrawDetail($invoice_id, $client_id, $client_nom, $amount, $code_banque, $code_guichet, $number, $number_key, $type = 'debit-order', $sourcetype = '', $bic = '', $iban = '', $rum = '', $id_prelevement_demande = 0)
357 {
358 // phpcs:enable
359 $result = 0;
360 $line_id = 0;
361
362 // TODO Save the $id_prelevement_demande
363
364 // Add lines into prelevement_lignes for tracking. The ID of line inserted is returned into $line_id.
365 $result = $this->addline($line_id, $client_id, $client_nom, $amount, $code_banque, $code_guichet, $number, $number_key, $sourcetype, $bic, $iban, $rum, $id_prelevement_demande);
366
367
368 if ($result == 0) {
369 if ($line_id > 0) {
370 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement (";
371 if ($type != 'bank-transfer') {
372 $sql .= "fk_facture";
373 } else {
374 if ($sourcetype == 'salary') {
375 $sql .= "fk_salary";
376 } else {
377 $sql .= "fk_facture_fourn";
378 }
379 }
380 $sql .= ",fk_prelevement_lignes";
381 $sql .= ") VALUES (";
382 $sql .= ((int) $invoice_id);
383 $sql .= ", " . ((int) $line_id);
384 $sql .= ")";
385
386 if ($this->db->query($sql)) {
387 $result = 0;
388 } else {
389 $result = -1;
390 $this->errors[] = get_class($this) . "::addWithdrawDetail " . $this->db->lasterror;
391 dol_syslog(get_class($this) . "::addWithdrawDetail Error $result");
392 }
393 } else {
394 $result = -2;
395 $this->errors[] = get_class($this) . "::addWithdrawDetail linedid Empty";
396 dol_syslog(get_class($this) . "::addWithdrawDetail Error $result");
397 }
398 } else {
399 $result = -3;
400 dol_syslog(get_class($this) . "::addWithdrawDetail Error $result");
401 }
402
403 return $result;
404 }
405
424 public function addline(&$line_id, $client_id, $client_nom, $amount, $code_banque, $code_guichet, $number, $number_key, $sourcetype = '', $bic = '', $iban = '', $rum = '', $id_prelevement_demande = 0)
425 {
426 $result = -1;
427 $concat = getDolGlobalInt('MAIN_MODULE_PRELEVEMENT_CONCAT'); // ??? what is this for. Seems not used.
428
429 if ($concat == 1) {
434 $sql = "SELECT rowid";
435 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_lignes";
436 $sql .= " WHERE fk_prelevement_bons = " . ((int) $this->id);
437 if ($sourcetype == 'salary') {
438 $sql .= " AND fk_soc = " . ((int) $client_id);
439 } else {
440 $sql .= " AND fk_user = " . ((int) $client_id);
441 }
442 $sql .= " AND code_banque = '" . $this->db->escape($code_banque) . "'";
443 $sql .= " AND code_guichet = '" . $this->db->escape($code_guichet) . "'";
444 $sql .= " AND number = '" . $this->db->escape($number) . "'";
445
446 $resql = $this->db->query($sql);
447 if ($resql) {
448 $num = $this->db->num_rows($resql);
449 } else {
450 $result = -1;
451 }
452 } else {
453 /*
454 * No aggregate, use 1 line per request of invoice/salary to pay
455 */
456 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_lignes (";
457 $sql .= "fk_prelevement_bons";
458 $sql .= ", fk_soc";
459 $sql .= ", client_nom";
460 $sql .= ", amount";
461 $sql .= ", fk_prelevement_demande";
462 $sql .= ($sourcetype == 'salary' ? ", fk_user" : "");
463 $sql .= ") VALUES (";
464 $sql .= ((int) $this->id);
465 $sql .= ", " . (($sourcetype != 'salary') ? ((int) $client_id) : "0"); // fk_soc can't be null
466 $sql .= ", '" . $this->db->escape($client_nom) . "'";
467 $sql .= ", " . ((float) price2num($amount));
468 $sql .= ", " . ((int) $id_prelevement_demande);
469 $sql .= (($sourcetype == 'salary') ? ", " . ((int) $client_id) : '');
470 $sql .= ")";
471 if ($this->db->query($sql)) {
472 $line_id = $this->db->last_insert_id(MAIN_DB_PREFIX . "prelevement_lignes");
473 $result = 0;
474 } else {
475 $this->errors[] = get_class($this) . "::addline Error -2 " . $this->db->lasterror;
476 dol_syslog(get_class($this) . "::addline Error -2");
477 $result = -2;
478 }
479 }
480
481 return $result;
482 }
483
490 public function getErrorString($error)
491 {
492 global $langs;
493
494 $errors = array();
495
496 $errors[1027] = $langs->trans("DateInvalid");
497
498 return $errors[abs($error)] ?? 'unknown error code';
499 }
500
508 public function fetch($rowid, $ref = '')
509 {
510 $sql = "SELECT p.rowid, p.ref, p.amount, p.note";
511 $sql .= ", p.datec as dc";
512 $sql .= ", p.date_trans as date_trans";
513 $sql .= ", p.method_trans, p.fk_user_trans";
514 $sql .= ", p.date_credit as date_credit";
515 $sql .= ", p.fk_user_credit";
516 $sql .= ", p.type";
517 $sql .= ", p.fk_bank_account";
518 $sql .= ", p.statut as status";
519 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as p";
520 $sql .= " WHERE p.entity IN (" . getEntity('invoice') . ")";
521 if ($rowid > 0) {
522 $sql .= " AND p.rowid = " . ((int) $rowid);
523 } else {
524 $sql .= " AND p.ref = '" . $this->db->escape($ref) . "'";
525 }
526
527 dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
528 $result = $this->db->query($sql);
529 if ($result) {
530 if ($this->db->num_rows($result)) {
531 $obj = $this->db->fetch_object($result);
532
533 $this->id = $obj->rowid;
534 $this->ref = $obj->ref;
535 $this->amount = $obj->amount;
536 $this->note = $obj->note;
537 $this->note_private = $obj->note;
538 $this->datec = $this->db->jdate($obj->dc);
539
540 $this->date_trans = $this->db->jdate($obj->date_trans);
541 $this->method_trans = $obj->method_trans;
542 $this->user_trans = $obj->fk_user_trans;
543
544 $this->date_credit = $this->db->jdate($obj->date_credit);
545 $this->user_credit = $obj->fk_user_credit;
546
547 $this->type = $obj->type;
548 $this->fk_bank_account = $obj->fk_bank_account;
549
550 $this->status = $obj->status;
551 if (empty($this->status)) { // Value is sometimes null in database
552 $this->status = 0;
553 }
554 $this->statut = $this->status; // For backward compatibility
555
556 $this->fetched = 1;
557
558 return 1;
559 } else {
560 dol_syslog(get_class($this) . "::Fetch no record found");
561 return 0;
562 }
563 } else {
564 return -1;
565 }
566 }
567
575 public function update(User $user, $notrigger = 0)
576 {
577 return $this->updateCommon($user, $notrigger);
578 }
579
580 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
590 public function set_infocredit($user, $date, $type = '')
591 {
592 // phpcs:enable
593 global $conf, $langs;
594
595 $error = 0;
596
597 if ($this->fetched == 1) {
598 if ($date < $this->date_trans) {
599 $langs->load("errors");
600 $this->error = $langs->trans('ErrorDateOfMovementLowerThanDateOfFileTransmission');
601 dol_syslog("bon-prelevment::set_infocredit 1027 " . $this->error);
602 return -1027;
603 }
604
605 $this->db->begin();
606
607 $sql = " UPDATE " . MAIN_DB_PREFIX . "prelevement_bons";
608 $sql .= " SET fk_user_credit = " . ((int) $user->id);
609 $sql .= ", statut = " . self::STATUS_CREDITED;
610 $sql .= ", date_credit = '" . $this->db->idate($date) . "'";
611 $sql .= " WHERE rowid = " . ((int) $this->id);
612 $sql .= " AND entity = " . ((int) $conf->entity);
613 $sql .= " AND statut = " . self::STATUS_TRANSFERED;
614
615 $resql = $this->db->query($sql);
616 if ($resql) {
617 $langs->load('withdrawals');
618 $subject = $langs->trans("InfoCreditSubject", $this->ref);
619 $message = $langs->trans("InfoCreditMessage", $this->ref, dol_print_date($date, 'dayhour'));
620
621 // Add payment of withdrawal into bank
622 $fk_bank_account = $this->fk_bank_account;
623 if (empty($fk_bank_account)) {
624 $fk_bank_account = ($this->type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
625 }
626
627 $amounts = array();
628 $amountsperthirdparty = array();
629
630 $facs = $this->getListInvoices(1, $type);
631 if ($this->error) {
632 $error++;
633 }
634
635 // Loop on each invoice or salary.
636 // $facs should be array(0=>id, 1=>amount requested)
637 $num = count($facs);
638 for ($i = 0; $i < $num; $i++) {
639 if ($this->type == 'bank-transfer') {
640 if ($type == 'salary') {
641 $fac = new Salary($this->db);
642 } else {
643 $fac = new FactureFournisseur($this->db);
644 }
645 } else {
646 $fac = new Facture($this->db);
647 }
648
649 $result = $fac->fetch($facs[$i][0]);
650
651 $amounts[$fac->id] = $facs[$i][1];
652 if ($this->type == 'bank-transfer') {
653 if ($type == 'salary') {
654 $amountsperthirdparty[$fac->fk_user][$fac->id] = $facs[$i][1];
655 } else {
656 $amountsperthirdparty[$fac->socid][$fac->id] = $facs[$i][1];
657 }
658 } else {
659 $amountsperthirdparty[$fac->socid][$fac->id] = $facs[$i][1];
660 }
661
662 $totalpaid = $fac->getSommePaiement();
663 $totalcreditnotes = 0;
664 if (method_exists($fac, 'getSumCreditNotesUsed')) {
665 $totalcreditnotes = $fac->getSumCreditNotesUsed();
666 }
667 $totaldeposits = 0;
668 if (method_exists($fac, 'getSumDepositsUsed')) {
669 $totaldeposits = $fac->getSumDepositsUsed();
670 }
671 $alreadypayed = $totalpaid + $totalcreditnotes + $totaldeposits;
672
673 // Set the main document to pay with status Paid.
674 // @TODO Move this after creation of payments done after
675 $amountofdocument = $fac->total_ttc;
676 if ($type == 'salary') {
677 $amountofdocument = $fac->amount;
678 }
679 if (price2num($alreadypayed + $facs[$i][1], 'MT') == price2num($amountofdocument, 'MT')) {
680 $result = $fac->setPaid($user);
681 if ($result < 0) {
682 $this->error = $fac->error;
683 $this->errors = $fac->errors;
684 }
685 }
686 }
687
688 // Make one payment per customer or employee
689 foreach ($amountsperthirdparty as $thirdpartyid => $cursoramounts) {
690 if ($this->type == 'bank-transfer') {
691 if ($type == 'salary') {
692 $paiement = new PaymentSalary($this->db);
693 } else {
694 $paiement = new PaiementFourn($this->db);
695 }
696 } else {
697 $paiement = new Paiement($this->db);
698 }
699 $paiement->datepaye = $date;
700 $paiement->amounts = $cursoramounts; // Array with detail of dispatching of payments for each invoice
701
702 if ($this->type == 'bank-transfer') {
703 if ($type == 'salary') {
704 $paiement->datep = $date;
705
706 $paiement->paiementid = 2;
707 $paiement->fk_typepayment = 2;
708 $paiement->paiementcode = 'VIR';
709 } else {
710 $paiement->paiementid = 2;
711 $paiement->paiementcode = 'VIR';
712 }
713 } else {
714 $paiement->paiementid = 3;
715 $paiement->paiementcode = 'PRE';
716 }
717
718 $paiement->num_payment = $this->ref; // Set ref of direct debit note
719 $paiement->id_prelevement = $this->id;
720
721 $result = $paiement->create($user, 1); // This use ->paiementid, that is ID of payment mode. closepaidinvoices=1 to convert deposit invoice to available credit
722
723 if ($result < 0) {
724 $error++;
725 $this->error = $paiement->error;
726 $this->errors = $paiement->errors;
727 dol_syslog(get_class($this) . "::set_infocredit AddPayment Error " . $this->error);
728 } else {
729 if ($this->type == 'bank-transfer') {
730 if ($type == 'salary') {
731 $modeforaddpayment = 'payment_salary';
732 $labelforaddpayment = '(SalaryPayment)';
733 $addbankurl = 'credit-transfer';
734 } else {
735 $modeforaddpayment = 'payment_supplier';
736 $labelforaddpayment = '(SupplierInvoicePayment)';
737 $addbankurl = 'credit-transfer';
738 }
739 } else {
740 $modeforaddpayment = 'payment';
741 $labelforaddpayment = '(CustomerInvoicePayment)';
742 $addbankurl = 'direct-debit'; // = 'directdebit'
743 }
744
745
746 if ($paiement instanceof PaymentSalary) {
747 // Only 6 arguments for PaymentSalary
748 $result = $paiement->addPaymentToBank($user, $modeforaddpayment, $labelforaddpayment, $fk_bank_account, '', '');
749 } else {
750 $result = $paiement->addPaymentToBank($user, $modeforaddpayment, $labelforaddpayment, $fk_bank_account, '', '', 0, '', $addbankurl);
751 }
752
753 if ($result < 0) {
754 $error++;
755 $this->error = $paiement->error;
756 $this->errors = $paiement->errors;
757 dol_syslog(get_class($this) . "::set_infocredit AddPaymentToBank Error " . $this->error);
758 }
759 }
760 }
761
762 // Update withdrawal line
763 // TODO: Translate to ligneprelevement.class.php
764 if (!$error) {
765 $sql = " UPDATE " . MAIN_DB_PREFIX . "prelevement_lignes";
766 $sql .= " SET statut = 2";
767 $sql .= " WHERE fk_prelevement_bons = " . ((int) $this->id);
768
769 if (!$this->db->query($sql)) {
770 dol_syslog(get_class($this) . "::set_infocredit Update lines Error");
771 $error++;
772 }
773 }
774 } else {
775 $this->error = $this->db->lasterror();
776 dol_syslog(get_class($this) . "::set_infocredit Update Bons Error");
777 $error++;
778 }
779
780 // End of procedure
781 if ($error == 0) {
782 $this->date_credit = $date; // date credit or debit
783 $this->statut = self::STATUS_CREDITED;
784 $this->status = self::STATUS_CREDITED;
785
786 $this->db->commit();
787 return 0;
788 } else {
789 $this->db->rollback();
790 return -1;
791 }
792 } else {
793 return -1026;
794 }
795 }
796
797 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
806 public function set_infotrans($user, $date, $method)
807 {
808 // phpcs:enable
809 global $conf, $langs;
810
811 $error = 0;
812
813 dol_syslog(get_class($this) . "::set_infotrans Start", LOG_INFO);
814
815 if ($this->db->begin()) {
816 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_bons ";
817 $sql .= " SET fk_user_trans = " . ((int) $user->id);
818 $sql .= " , date_trans = '" . $this->db->idate($date) . "'";
819 $sql .= " , method_trans = " . ((int) $method);
820 $sql .= " , statut = " . self::STATUS_TRANSFERED;
821 $sql .= " WHERE rowid = " . ((int) $this->id);
822 $sql .= " AND entity = " . ((int) $conf->entity);
823 $sql .= " AND statut = " . self::STATUS_DRAFT;
824
825 if ($this->db->query($sql)) {
826 $this->method_trans = $method;
827 $langs->load('withdrawals');
828 $subject = $langs->trans("InfoTransSubject", $this->ref);
829 $message = $langs->trans("InfoTransMessage", $this->ref, dolGetFirstLastname($user->firstname, $user->lastname));
830 $message .= $langs->trans("InfoTransData", price($this->amount), $this->methodes_trans[$this->method_trans], dol_print_date($date, 'day'));
831
832 // TODO Call trigger to create a notification using notification module
833 } else {
834 $error++;
835 }
836
837 if ($error == 0) {
838 $this->date_trans = $date;
839 $this->statut = self::STATUS_TRANSFERED;
840 $this->status = self::STATUS_TRANSFERED;
841 $this->user_trans = $user->id;
842
843 $this->db->commit();
844
845 return 0;
846 } else {
847 $this->db->rollback();
848 dol_syslog(get_class($this) . "::set_infotrans ROLLBACK", LOG_ERR);
849
850 return -1;
851 }
852 } else {
853 dol_syslog(get_class($this) . "::set_infotrans Ouverture transaction SQL impossible", LOG_CRIT);
854 return -2;
855 }
856 }
857
866 private function getListInvoices($amounts = 0, $type = '')
867 {
868 global $conf;
869
870 $arr = array();
871
872 dol_syslog(get_class($this) . "::getListInvoices");
873
874 // Returns all invoices presented within same order
875 $sql = "SELECT ";
876 if ($this->type == 'bank-transfer') {
877 if ($type == 'salary') {
878 $sql .= " p.fk_salary";
879 } else {
880 $sql .= " p.fk_facture_fourn";
881 }
882 } else {
883 $sql .= " p.fk_facture";
884 }
885 if ($amounts) {
886 $sql .= ", SUM(pl.amount)";
887 }
888 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as pb,";
889 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
890 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p";
891 $sql .= " WHERE p.fk_prelevement_lignes = pl.rowid";
892 $sql .= " AND pl.fk_prelevement_bons = pb.rowid";
893 $sql .= " AND pb.rowid = " . ((int) $this->id);
894 $sql .= " AND pb.entity = " . ((int) $conf->entity);
895 if ($amounts) {
896 if ($this->type == 'bank-transfer') {
897 if ($type == 'salary') {
898 $sql .= " GROUP BY p.fk_salary";
899 } else {
900 $sql .= " GROUP BY p.fk_facture_fourn";
901 }
902 } else {
903 $sql .= " GROUP BY p.fk_facture";
904 }
905 }
906
907 $resql = $this->db->query($sql);
908 if ($resql) {
909 $num = $this->db->num_rows($resql);
910
911 if ($num) {
912 $i = 0;
913 while ($i < $num) {
914 $row = $this->db->fetch_row($resql);
915 if (!$amounts) {
916 $arr[$i] = $row[0];
917 } else {
918 $arr[$i] = array(
919 $row[0],
920 $row[1]
921 );
922 }
923 $i++;
924 }
925 }
926 $this->db->free($resql);
927 } else {
928 $this->error = $this->db->lasterror();
929 }
930
931 return $arr;
932 }
933
934 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
942 public function SommeAPrelever($mode = 'direct-debit', $type = '')
943 {
944 // phpcs:enable
945 $sql = "SELECT sum(pd.amount) as nb";
946 if ($type !== 'salary') {
947 if ($mode != 'bank-transfer') {
948 $sql .= " FROM " . MAIN_DB_PREFIX . "facture as f,";
949 } else {
950 $sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f,";
951 }
952 } else {
953 $sql .= " FROM " . MAIN_DB_PREFIX . "salary as s,";
954 }
955 $sql .= " " . MAIN_DB_PREFIX . "prelevement_demande as pd";
956 $sql .= ($type !== 'salary' ? " WHERE f.entity IN (" . getEntity('invoice') . ")" : " WHERE s.entity IN (" . getEntity('salary') . ")");
957 if (!getDolGlobalString('WITHDRAWAL_ALLOW_ANY_INVOICE_STATUS')) {
958 $sql .= ($type !== 'salary' ? " AND f.fk_statut = " . Facture::STATUS_VALIDATED : " AND s.paye = " . Salary::STATUS_UNPAID);
959 }
960 if ($type !== 'salary') {
961 if ($mode != 'bank-transfer') {
962 $sql .= " AND f.rowid = pd.fk_facture";
963 } else {
964 $sql .= " AND f.rowid = pd.fk_facture_fourn";
965 }
966 } else {
967 $sql .= " AND s.rowid = pd.fk_salary";
968 }
969 $sql .= ($type !== 'salary' ? " AND f.paye = 0" : "");
970 $sql .= " AND pd.traite = 0";
971 $sql .= " AND pd.ext_payment_id IS NULL";
972 $sql .= ($type !== 'salary' ? " AND f.total_ttc > 0" : "");
973
974 $resql = $this->db->query($sql);
975 if ($resql) {
976 $obj = $this->db->fetch_object($resql);
977
978 $this->db->free($resql);
979
980 return $obj->nb;
981 } else {
982 $error = 1;
983 dol_syslog(get_class($this) . "::SommeAPrelever Erreur -1");
984 dol_syslog($this->db->error());
985
986 return -1;
987 }
988 }
989
997 public function nbOfInvoiceToPay($mode = 'direct-debit', $type = '')
998 {
999 if ($type === 'salary') {
1000 return $this->NbFactureAPrelever($mode, 1);
1001 } else {
1002 return $this->NbFactureAPrelever($mode);
1003 }
1004 }
1005
1006 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1014 public function NbFactureAPrelever($type = 'direct-debit', $forsalary = 0)
1015 {
1016 // phpcs:enable
1017 if ($forsalary == 1) {
1018 $sql = "SELECT count(s.rowid) as nb";
1019 $sql .= " FROM " . MAIN_DB_PREFIX . "salary as s";
1020 } else {
1021 $sql = "SELECT count(f.rowid) as nb";
1022
1023 if ($type == 'bank-transfer') {
1024 $sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
1025 } else {
1026 $sql .= " FROM " . MAIN_DB_PREFIX . "facture as f";
1027 }
1028 }
1029 $sql .= ", " . MAIN_DB_PREFIX . "prelevement_demande as pd";
1030 if ($forsalary == 1) {
1031 $sql .= " WHERE s.entity IN (" . getEntity('invoice') . ")";
1032 if (!getDolGlobalString('WITHDRAWAL_ALLOW_ANY_INVOICE_STATUS')) {
1033 $sql .= " AND s.paye = 0";
1034 }
1035 } else {
1036 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
1037 if (!getDolGlobalString('WITHDRAWAL_ALLOW_ANY_INVOICE_STATUS')) {
1038 $sql .= " AND f.fk_statut = " . Facture::STATUS_VALIDATED;
1039 }
1040 }
1041 if ($forsalary == 1) {
1042 $sql .= " AND s.rowid = pd.fk_salary";
1043 } else {
1044 if ($type == 'bank-transfer') {
1045 $sql .= " AND f.rowid = pd.fk_facture_fourn";
1046 } else {
1047 $sql .= " AND f.rowid = pd.fk_facture";
1048 }
1049 }
1050 $sql .= " AND pd.traite = 0";
1051 $sql .= " AND pd.ext_payment_id IS NULL";
1052 if (!$forsalary == 1) {
1053 $sql .= " AND f.total_ttc > 0";
1054 } else {
1055 $sql .= " AND s.paye = 0";
1056 }
1057
1058 dol_syslog(get_class($this) . "::NbFactureAPrelever");
1059 $resql = $this->db->query($sql);
1060
1061 if ($resql) {
1062 $obj = $this->db->fetch_object($resql);
1063 $this->db->free($resql);
1064
1065 return $obj->nb;
1066 } else {
1067 $this->error = get_class($this) . "::NbFactureAPrelever Erreur -1 sql=" . $this->db->error();
1068 return -1;
1069 }
1070 }
1071
1072
1073 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1097 public function create($banque = '', $agence = '', $mode = 'real', $format = 'ALL', $executiondate = 0, $notrigger = 0, $type = 'direct-debit', $dids = 0, $fk_bank_account = 0, $sourcetype = 'invoice')
1098 {
1099 // phpcs:enable
1100 global $conf, $langs, $user;
1101
1102 dol_syslog(__METHOD__ . " mode=".$mode." format=".$format." type=".$type." dids=".(is_array($dids) ? implode(',', $dids) : $dids)." fk_bank_account=".$fk_bank_account." sourcetype=".$sourcetype, LOG_DEBUG);
1103
1104 require_once DOL_DOCUMENT_ROOT . "/compta/facture/class/facture.class.php";
1105 require_once DOL_DOCUMENT_ROOT . "/societe/class/societe.class.php";
1106
1107 // Check params
1108 if ($type != 'bank-transfer') {
1109 if (empty($format)) {
1110 $this->error = 'ErrorBadParametersForDirectDebitFileCreate';
1111 return -1;
1112 }
1113 }
1114
1115 if (!is_int($dids) && !is_array($dids)) {
1116 $this->error = 'ErrorBadParametersForDirectDebitFileCreateDids';
1117 return -1;
1118 }
1119
1120 // Clean params
1121 if (empty($fk_bank_account)) {
1122 $fk_bank_account = ($type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
1123 }
1124 if (is_int($dids)) {
1125 $dids = array($dids);
1126 }
1127
1128 $error = 0;
1129 // Pre-store some values into variables to simplify following sql requests
1130 if ($sourcetype != 'salary') {
1131 $entity = $type != 'bank-transfer' ? getEntity('invoice', 1) : getEntity('supplier_invoice', 1); // Return entity
1132 $sqlTable = $type != 'bank-transfer' ? "facture" : "facture_fourn";
1133 $socOrUser = 'fk_soc';
1134 $societeOrUser = 'societe';
1135 } else {
1136 $entity = getEntity('salary', 1); // Return entity
1137 $sqlTable = 'salary';
1138 $socOrUser = 'fk_user';
1139 $societeOrUser = 'user';
1140 }
1141
1142 /*
1143 $thirdpartyBANId = 0;
1144
1145 // Check if there is an iban associated to the bank transfer request or if we take the default
1146 if ($dids !== [0] && !empty($dids)) {
1147 $sql = "SELECT pd.fk_societe_rib";
1148 $sql .= " FROM " . $this->db->prefix() . "prelevement_demande as pd";
1149 $sql .= " WHERE pd.rowid IN (".$this->db->sanitize(implode(',', $dids)).")";
1150
1151 $resql = $this->db->query($sql);
1152
1153 if (!$resql) {
1154 $this->error = $this->db->lasterror();
1155 dol_syslog(__METHOD__ . " Read fk_societe_rib error " . $this->db->lasterror(), LOG_ERR);
1156 return -1;
1157 }
1158
1159 $obj = $this->db->fetch_object($resql);
1160 if ($obj) {
1161 $thirdpartyBANId = $obj->fk_societe_rib;
1162
1163 dol_syslog(__METHOD__ . " Found a BAN ID to use: ".$thirdpartyBANId);
1164 }
1165
1166 $this->db->free($resql);
1167 }
1168 */
1169
1170 $datetimeprev = dol_now('gmt');
1171 // Choice of the date of the execution direct debit
1172 if (!empty($executiondate)) {
1173 $datetimeprev = $executiondate;
1174 }
1175
1176 $month = dol_print_date($datetimeprev, "%m", 'gmt');
1177 $year = dol_print_date($datetimeprev, "%Y", 'gmt');
1178
1179 $this->invoice_in_error = array();
1180 $this->thirdparty_in_error = array();
1181
1182 // Get all invoices to process into $factures
1183 $factures = array(); // TODO Rename $factures... into $payment_requests...
1184 $factures_prev = array();
1185 $factures_prev_id = array();
1186
1187 dol_syslog(__METHOD__ . " Read invoices/salaries for dids=" . implode(', ', $dids), LOG_DEBUG);
1188
1189 $sql = "SELECT f.rowid, pd.rowid as pfdrowid";
1190 $sql .= ", f.".$this->db->sanitize($socOrUser); // fk_soc or fk_user
1191 $sql .= ", pd.code_banque, pd.code_guichet, pd.number, pd.cle_rib";
1192 $sql .= ", pd.amount";
1193 if ($sourcetype != 'salary') {
1194 $sql .= ", s.nom as name";
1195 $sql .= ", f.ref";
1196 $sql .= ", sr.bic, sr.iban_prefix, sr.frstrecur, sr.default_rib, sr.rum";
1197 } else {
1198 $sql .= ", CONCAT(s.firstname, ' ', s.lastname) as name";
1199 $sql .= ", f.ref";
1200 $sql .= ", sr.bic, sr.iban_prefix, 'FRST' as frstrecur, sr.default_rib, '' as rum";
1201 }
1202 $sql .= ", pd.fk_societe_rib as soc_rib_id"; // Forced payment IBAN to use in priority
1203 $sql .= " FROM " . $this->db->prefix() . $sqlTable . " as f"; // f is salary, facture or facture_fourn
1204 $sql .= " INNER JOIN " . $this->db->prefix() . "prelevement_demande as pd ON f.rowid = pd.fk_".$this->db->sanitize($sqlTable);
1205 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($societeOrUser)." as s ON s.rowid = f.".$this->db->sanitize($socOrUser);
1206 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($societeOrUser."_rib")." as sr ON s.rowid = sr.".$this->db->sanitize($socOrUser); // To get the default BAN of thirdparty
1207 if ($sourcetype != 'salary') {
1208 $sql .= " AND sr.default_rib = 1";
1209 $sql .= " AND sr.type = 'ban'";
1210 } else {
1211 //$sql .= " AND sr.type = 'ban'"; // TODO Add AND sr.type = 'ban' for users too
1212 // TODO Add 'AND sr.default_rib = 1' in sourcetype salary too
1213 // Note: the column has been created in v21 in llx_user_rib and default to 0
1214 // If we add a test on sr.default_rib = 1, we must also check we have a correct error management to stop if no default BAN is found.
1215 // Also it may be found for on thirdparty and not for the other.
1216 }
1217 $sql .= " WHERE f.entity IN (".((int) $entity).')';
1218 if ($sourcetype != 'salary') {
1219 $sql .= " AND f.fk_statut = ".Facture::STATUS_VALIDATED; // Invoice validated
1220 $sql .= " AND f.paye = 0";
1221 $sql .= " AND f.total_ttc > 0";
1222 /*if ($socid > 0) {
1223 $sql .= " AND f.fk_soc = ".((int) $socid);
1224 }*/
1225 } else {
1226 //$sql .= " AND f.fk_statut = 1"; // Invoice validated
1227 $sql .= " AND f.paye = 0";
1228 $sql .= " AND f.amount > 0";
1229 }
1230 $sql .= " AND pd.traite = 0";
1231 $sql .= " AND pd.ext_payment_id IS NULL";
1232 if ($dids !== [0] && !empty($dids)) {
1233 $sql .= " AND pd.rowid IN (".$this->db->sanitize(implode(',', $dids)).")";
1234 }
1235
1236 $resql = $this->db->query($sql);
1237 if ($resql) {
1238 $num = $this->db->num_rows($resql);
1239 $i = 0;
1240
1241 while ($i < $num) {
1242 $row = $this->db->fetch_row($resql); // TODO Replace with fetch_object()
1243 '@phan-var-force array<int<0,12>,string> $row';
1246 // All fields:
1247 // 0=invoice or salary id, 1=pfdrowid (id of payment request), 2=$socOrUser, 3=code_banque, 4=code_guichet, 5=number, 6=key,
1248 // 7=amount, 8=company name, 9=invoice ref,
1249 // 10=default bic, 11=default iban (=the BAN to use if no soc_rib_id set), 12=default frstrecur, 13=default_rib, 14=default rum, 15=soc_rib_id (=the BAN to use in priority)
1250 $factures[$i] = $row;
1251
1252 // Decode default BAN
1253 $factures[$i][11] = dolDecrypt($factures[$i][11]);
1254
1255 if ($row[7] == 0) {
1256 $error++;
1257 dol_syslog(__METHOD__ . " Read invoices/salaries error Found a null amount", LOG_WARNING);
1258 $this->invoice_in_error[$row[0]] = "Error for invoice or salary id " . $row[0] . ", found a null amount";
1259 break;
1260 }
1261 $i++;
1262 }
1263
1264 $this->db->free($resql);
1265 dol_syslog(__METHOD__ . " Read invoices/salaries, " . $i . " invoices/salaries to withdraw", LOG_DEBUG);
1266 } else {
1267 $this->error = $this->db->lasterror();
1268 dol_syslog(__METHOD__ . " Read invoices/salaries error " . $this->db->lasterror(), LOG_ERR);
1269 return -1;
1270 }
1271
1272 if (!$error) {
1273 // Make some checks
1274 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1275 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
1276 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
1277 require_once DOL_DOCUMENT_ROOT . '/core/lib/bank.lib.php';
1278
1279 $tmpsoc = new Societe($this->db);
1280 $tmpuser = new User($this->db);
1281
1282 // Check BAN
1283 $i = 0;
1284 dol_syslog(__METHOD__ . " Check BAN for each invoices or salaries", LOG_DEBUG);
1285
1286 if (count($factures) > 0) {
1287 foreach ($factures as $key => $fac) {
1288 //var_dump($type, $format, $fac[8], $fac[12]);
1289
1290 // Check if $fac[8] s.nom is null
1291 if ($fac[8] != null) {
1292 if ($type != 'bank-transfer') {
1293 if ($format == 'FRST' && $fac[12] && $fac[12] != 'FRST') {
1294 continue;
1295 }
1296 if ($format == 'RCUR' && $fac[12] && $fac[12] != 'RCUR') {
1297 continue;
1298 }
1299 }
1300
1301 // If a bank account was forced on llx_prelevement_demande (the direct debit or credit transfer request),
1302 // we must use this one, so we reload values from soc_rib_id
1303 if (!empty($fac[15])) {
1304 $bankaccount = new CompanyBankAccount($this->db);
1305 $bankaccount->fetch((int) $fac[15]);
1306 if ($bankaccount->id > 0) {
1307 $fac[3] = $bankaccount->code_banque;
1308 $fac[4] = $bankaccount->code_guichet;
1309 $fac[5] = $bankaccount->number;
1310 $fac[6] = $bankaccount->cle_rib;
1311 $fac[10] = $bankaccount->bic;
1312 $fac[11] = $bankaccount->iban;
1313 $fac[14] = $bankaccount->rum;
1314 //var_dump('fetch id='.(int) $fac[14].' => '.$fac[3].'-'.$fac[4].'-'.$fac[5].'-'.$fac[6].'-'.$fac[10]."-".$fac[11]."-".$fac[14]);
1315 }
1316 }
1317
1318 $bicinloop = $fac[10];
1319 $baninloop = $fac[11];
1320
1321 $verif = checkSwiftForAccount(null, $bicinloop);
1322
1323 if ($verif || (empty($fac[10]) && getDolGlobalInt("WITHDRAWAL_WITHOUT_BIC"))) {
1324 dol_syslog(__METHOD__." now call checkIbanForAccount(null, ".$baninloop.")");
1325 $verif = checkIbanForAccount(null, $baninloop);
1326 }
1327
1328 if ($verif) {
1329 $factures_prev[$i] = $fac;
1330 /* second array necessary for BonPrelevement */
1331 $factures_prev_id[$i] = $fac[0];
1332 $i++;
1333 //dol_syslog(__METHOD__."::RIB is ok", LOG_DEBUG);
1334 } else {
1335 if ($type != 'bank-transfer') {
1336 $tmpsoc->id = (int) $fac[2];
1337 $tmpsoc->name = $fac[8];
1338 $invoice_url = "<a href='" . DOL_URL_ROOT . '/compta/facture/card.php?facid=' . $fac[0] . "'>" . $fac[9] . "</a>";
1339 $this->invoice_in_error[$fac[0]] = "Error incomplete properties of the default bank number ".$baninloop." IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1340 $this->thirdparty_in_error[$tmpsoc->id] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1341 $error++;
1342 }
1343 if ($type == 'bank-transfer' && $sourcetype != 'salary') {
1344 $tmpsoc->id = (int) $fac[2];
1345 $tmpsoc->name = $fac[8];
1346 $invoice_url = "<a href='" . DOL_URL_ROOT . '/fourn/facture/card.php?facid=' . $fac[0] . "'>" . $fac[9] . "</a>";
1347 $this->invoice_in_error[$fac[0]] = "Error incomplete properties of the default bank number ".$baninloop." IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1348 $this->thirdparty_in_error[$tmpsoc->id] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1349 $error++;
1350 }
1351 if ($type == 'bank-transfer' && $sourcetype == 'salary') {
1352 $tmpuser->id = (int) $fac[2];
1353 $tmpuser->firstname = $fac[8];
1354 $salary_url = "<a href='" . DOL_URL_ROOT . '/salaries/card.php?id=' . $fac[0] . "'>" . $fac[0] . "</a>";
1355 $this->invoice_in_error[$fac[0]] = "Error incomplete properties of the default bank number ".$baninloop." IBAN/BIC for salary " . $salary_url . " for employee " . $tmpuser->getNomUrl(0);
1356 $this->thirdparty_in_error[$tmpuser->id] = "Error on default bank number IBAN/BIC for salary " . $salary_url . " for employee " . $tmpuser->getNomUrl(0);
1357 $error++;
1358 }
1359 dol_syslog(__METHOD__ . " Check BAN Error on default bank number IBAN/BIC reported by verif(): " . implode(', ', $fac), LOG_WARNING);
1360 }
1361 } else {
1362 dol_syslog(__METHOD__ . " Check BAN Failed to read company", LOG_WARNING);
1363 }
1364 /*
1365 } else {
1366 dol_syslog(__METHOD__." Check BAN Failed to read invoice", LOG_WARNING);
1367 }
1368 */
1369 }
1370 } else {
1371 dol_syslog(__METHOD__ . " Check BAN No invoice to process", LOG_WARNING);
1372 }
1373 }
1374
1375 $ok = 0;
1376
1377 // Withdraw invoices in factures_prev array
1378 $out = count($factures_prev) . " invoices or salaries will be included.";
1379 dol_syslog($out);
1380
1381 //var_dump($factures_prev, $this->invoice_in_error, $this->thirdparty_in_error);exit;
1382
1383 if (count($factures_prev) > 0) {
1384 if ($mode == 'real') {
1385 $ok = 1;
1386 } else {
1387 print $langs->trans("ModeWarning"); // "Option for real mode was not set, we stop after this simulation\n";
1388 }
1389 }
1390 if ($ok) {
1391 /*
1392 * We are in real mode.
1393 * We create order and build file into disk
1394 */
1395 $this->db->begin();
1396
1397 $now = dol_now();
1398 $ref = '';
1399
1400 /*
1401 * Process order generation
1402 */
1403 if (!$error) {
1404 $ref = substr($year, -2) . $month;
1405
1406 $prefixt = "T";
1407
1408 // Get next free number for the ref of bon prelevement
1409 $sql = "SELECT SUBSTRING(ref, 6) AS refnumber"; // To suppress "TYYMM" from "TYYMMXXX"
1410 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons";
1411 $sql .= " WHERE ref LIKE '" . $this->db->escape($prefixt . $ref) . "%'";
1412 $sql .= " AND entity = " . ((int) $conf->entity);
1413 $sql .= " ORDER BY LENGTH(ref) DESC, ref DESC";
1414 $sql .= " LIMIT 1";
1415
1416 dol_syslog(get_class($this) . " get next free number", LOG_DEBUG);
1417
1418 $resql = $this->db->query($sql);
1419
1420 if ($resql) {
1421 $row = $this->db->fetch_row($resql);
1422
1423 // Build the new ref
1424 $ref = $prefixt . $ref . sprintf("%03d", (intval($row[0] ?? 0) + 1));
1425
1426 // $conf->abc->dir_output may be:
1427 // /home/ldestailleur/git/dolibarr_15.0/documents/abc/
1428 // or
1429 // /home/ldestailleur/git/dolibarr_15.0/documents/X/abc with X >= 2 with multicompany.
1430 if ($type != 'bank-transfer') {
1431 $dir = $conf->prelevement->dir_output . '/receipts';
1432 } else {
1433 $dir = $conf->paymentbybanktransfer->dir_output . '/receipts';
1434 }
1435 if (!is_dir($dir)) {
1436 dol_mkdir($dir);
1437 }
1438
1439 if (isModEnabled('multicompany')) {
1440 $labelentity = $conf->entity;
1441 $this->filename = $dir . '/' . $ref . '-' . $labelentity . '.xml';
1442 } else {
1443 $this->filename = $dir . '/' . $ref . '.xml';
1444 }
1445
1446 // Create withdraw order in database
1447 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_bons (";
1448 $sql .= "ref, entity, datec, type, fk_bank_account";
1449 $sql .= ") VALUES (";
1450 $sql .= "'" . $this->db->escape($ref) . "'";
1451 $sql .= ", " . ((int) $conf->entity);
1452 $sql .= ", '" . $this->db->idate($now) . "'";
1453 $sql .= ", '" . ($type == 'bank-transfer' ? 'bank-transfer' : 'debit-order') . "'";
1454 $sql .= ", " . ((int) $fk_bank_account);
1455 $sql .= ")";
1456
1457 $resql = $this->db->query($sql);
1458
1459 if ($resql) {
1460 $prev_id = $this->db->last_insert_id(MAIN_DB_PREFIX . "prelevement_bons");
1461 $this->id = $prev_id;
1462 $this->ref = $ref;
1463 } else {
1464 $error++;
1465 $this->errors[] = $this->db->lasterror();
1466 dol_syslog(__METHOD__ . " Create withdraw receipt " . $this->db->lasterror(), LOG_ERR);
1467 }
1468 } else {
1469 $error++;
1470 $this->errors[] = $this->db->lasterror();
1471 dol_syslog(__METHOD__ . " Get last withdraw receipt " . $this->db->lasterror(), LOG_ERR);
1472 }
1473 }
1474
1475 if (!$error) {
1476 dol_syslog(__METHOD__ . " Now loop on each document to insert them in llx_prelevement_demande");
1477
1478 // Add lines for the direct debit or crdit transfer
1479 if (count($factures_prev) > 0) {
1480 foreach ($factures_prev as $fac) { // Add a link in database for each invoice or salary
1481 /*
1482 * Add standing order. This add record into llx_prelevement_lignes and llx_prelevement
1483 * The llx_prelevement_ligne will then be read to build the XML SEPA file.
1484 *
1485 * $fac[0] : invoice or salary id
1486 * $fac[1] : id of payment request (rowid in llx_prelevement_demande)
1487 * $fac[2] : third party id
1488 * $fac[3] : banque
1489 * $fac[4] : guichet
1490 * $fac[5] : number
1491 * $fac[6] : cle rib
1492 * $fac[7] : amount
1493 * $fac[8] : client nom
1494 * $fac[9] : Invoice ref
1495 * $fac[10] : BIC
1496 * $fac[11] : IBAN
1497 * $fac[12] : frstrcur
1498 * $fac[13] : default_rib (0 or 1)
1499 * $fac[14] : rum
1500 * $fac[15] : soc_rib_id (id bank account preselected for direct debit or credit transfer)
1501 */
1502 $ri = $this->addWithdrawDetail(
1503 $fac[0],
1504 $fac[2],
1505 $fac[8],
1506 $fac[7],
1507 $fac[3],
1508 $fac[4],
1509 $fac[5],
1510 $fac[6],
1511 $type,
1512 $sourcetype,
1513 $fac[10],
1514 $fac[11],
1515 $fac[14],
1516 $fac[1]
1517 );
1518
1519 if ($ri != 0) {
1520 $error++;
1521 }
1522
1523 // Update invoice requests as done
1524 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_demande";
1525 $sql .= " SET traite = 1";
1526 $sql .= ", date_traite = '" . $this->db->idate($now) . "'";
1527 $sql .= ", fk_prelevement_bons = " . ((int) $this->id);
1528 $sql .= " WHERE rowid = " . ((int) $fac[1]);
1529
1530 $resql = $this->db->query($sql);
1531 if (!$resql) {
1532 $error++;
1533 $this->errors[] = $this->db->lasterror();
1534 dol_syslog(__METHOD__ . " Update Error=" . $this->db->lasterror(), LOG_ERR);
1535 }
1536 }
1537 }
1538 }
1539
1540 if (!$error) {
1541 /*
1542 * Create file of type='direct-debit' for direct debit order or type='bank-transfer' for credit transfer into a XML file
1543 */
1544
1545 dol_syslog(__METHOD__ . " Init direct debit or credit transfer file for " . count($factures_prev) . " invoices", LOG_DEBUG);
1546
1547 if (count($factures_prev) > 0) {
1548 $this->date_echeance = $datetimeprev;
1549 $this->reference_remise = $ref;
1550
1551 $account = new Account($this->db);
1552 if ($account->fetch($fk_bank_account) > 0) {
1553 $this->emetteur_code_banque = $account->code_banque;
1554 $this->emetteur_code_guichet = $account->code_guichet;
1555 $this->emetteur_numero_compte = $account->number;
1556 $this->emetteur_number_key = $account->cle_rib;
1557 $this->sepa_xml_pti_in_ctti = (bool) $account->pti_in_ctti;
1558 $this->emetteur_iban = $account->iban;
1559 $this->emetteur_bic = $account->bic;
1560
1561 $this->emetteur_ics = (($type == 'bank-transfer' && getDolGlobalString("SEPA_USE_IDS")) ? $account->ics_transfer : $account->ics); // Example "FR78ZZZ123456"
1562
1563 $this->raison_sociale = $account->owner_name;
1564 }
1565 $this->factures = $factures_prev_id;
1566 $this->context['factures_prev'] = $factures_prev;
1567 // Generation of direct debit or credit transfer file $this->filename (May be a SEPA file for european countries)
1568 // This also set the property $this->total with amount that is included into file
1569 $userid = 0;
1570 if ($sourcetype == 'salary') {
1571 $userid = $this->context['factures_prev'][0][2];
1572 }
1573 $result = $this->generate($format, $executiondate, $type, $fk_bank_account, $userid, 0);
1574 if ($result < 0) {
1575 //var_dump($this->error);
1576 //var_dump($this->invoice_in_error);
1577 $error++;
1578 }
1579 }
1580 dol_syslog(__METHOD__ . " Bank order file has been generated under filename " . $this->filename, LOG_DEBUG);
1581 }
1582
1583
1584 /*
1585 * Update total defined after generation of file
1586 */
1587 if (!$error) {
1588 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_bons";
1589 $sql .= " SET amount = " . price2num($this->total);
1590 $sql .= " WHERE rowid = " . ((int) $this->id);
1591 $sql .= " AND entity = " . ((int) $conf->entity);
1592 $resql = $this->db->query($sql);
1593
1594 if (!$resql) {
1595 $error++;
1596 dol_syslog(__METHOD__ . " Error update total: " . $this->db->error(), LOG_ERR);
1597 }
1598 }
1599
1600 if (!$error && !$notrigger) {
1601 $triggerName = ($type == 'bank-transfer') ? 'CREDIT_TRANSFER_ORDER_CREATE' : 'DIRECT_DEBIT_ORDER_CREATE';
1602 $this->amount = $this->total;
1603
1604 // Call trigger
1605 $result = $this->call_trigger($triggerName, $user);
1606 if ($result < 0) {
1607 $error++;
1608 }
1609 // End call triggers
1610 }
1611
1612 if (!$error) {
1613 $this->db->commit();
1614 return count($factures_prev); // The error of failed lines are into $this->invoice_in_error and $this->thirdparty_in_error
1615 } else {
1616 $this->db->rollback();
1617 return -1;
1618 }
1619 } else {
1620 return 0;
1621 }
1622 }
1623
1624
1632 public function delete($user, $notrigger = 0)
1633 {
1634 $this->db->begin();
1635
1636 $error = 0;
1637 $resql1 = $resql2 = $resql3 = $resql4 = 0;
1638
1639 if (!$notrigger) {
1640 $triggername = 'DIRECT_DEBIT_ORDER_DELETE';
1641 if ($this->type == 'bank-transfer') {
1642 $triggername = 'PAYMENTBYBANKTRANFER_DELETE';
1643 }
1644 // Call trigger
1645 $result = $this->call_trigger($triggername, $user);
1646 if ($result < 0) {
1647 $error++;
1648 }
1649 // End call triggers
1650 }
1651
1652 if (!$error) {
1653 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prelevement WHERE fk_prelevement_lignes IN (SELECT rowid FROM " . MAIN_DB_PREFIX . "prelevement_lignes WHERE fk_prelevement_bons = " . ((int) $this->id) . ")";
1654 $resql1 = $this->db->query($sql);
1655 if (!$resql1) {
1656 dol_print_error($this->db);
1657 }
1658 }
1659
1660 if (!$error) {
1661 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prelevement_lignes WHERE fk_prelevement_bons = " . ((int) $this->id);
1662 $resql2 = $this->db->query($sql);
1663 if (!$resql2) {
1664 dol_print_error($this->db);
1665 }
1666 }
1667
1668 if (!$error) {
1669 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prelevement_bons WHERE rowid = " . ((int) $this->id);
1670 $resql3 = $this->db->query($sql);
1671 if (!$resql3) {
1672 dol_print_error($this->db);
1673 }
1674 }
1675
1676 if (!$error) {
1677 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_demande SET fk_prelevement_bons = NULL, traite = 0 WHERE fk_prelevement_bons = " . ((int) $this->id);
1678 $resql4 = $this->db->query($sql);
1679 if (!$resql4) {
1680 dol_print_error($this->db);
1681 }
1682 }
1683
1684 if ($resql1 && $resql2 && $resql3 && $resql4 && !$error) {
1685 $this->db->commit();
1686 return 1;
1687 } else {
1688 $this->db->rollback();
1689 return -1;
1690 }
1691 }
1692
1693
1704 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1705 {
1706 global $conf, $langs, $hookmanager;
1707
1708 if (!empty($conf->dol_no_mouse_hover)) {
1709 $notooltip = 1; // Force disable tooltips
1710 }
1711
1712 $result = '';
1713
1714 $labeltoshow = 'PaymentByDirectDebit';
1715 $labelforclosedate = 'CreditDate';
1716 if (!empty($this->type) && $this->type == 'bank-transfer') {
1717 $labeltoshow = 'PaymentByBankTransfer';
1718 $labelforclosedate = 'ClosedOn';
1719 }
1720
1721 $label = img_picto('', $this->picto) . ' <u>' . $langs->trans($labeltoshow) . '</u> ' . $this->getLibStatut(5);
1722 $label .= '<br>';
1723 $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1724 if (isset($this->amount)) {
1725 $label .= '<br><b>' . $langs->trans("Amount") . ':</b> <span class="amount">' . price($this->amount).'</span>';
1726 }
1727 if (isset($this->date_creation)) {
1728 $label .= '<br><b>'.$langs->trans("DateCreation").":</b> ".dol_print_date($this->date_creation, 'dayhour', 'tzuserrel');
1729 }
1730 if (isset($this->date_trans)) {
1731 $label .= '<br><b>' . $langs->trans("TransData") . ":</b> " . dol_print_date($this->date_trans, 'dayhour', 'tzuserrel');
1732 }
1733 if (isset($this->date_credit)) {
1734 $label .= '<br><b>'.$langs->trans($labelforclosedate).":</b> ".dol_print_date($this->date_credit, 'dayhour', 'tzuserrel');
1735 }
1736
1737 $url = DOL_URL_ROOT . '/compta/prelevement/card.php?id=' . $this->id;
1738 if (!empty($this->type) && $this->type == 'bank-transfer') {
1739 $url = DOL_URL_ROOT . '/compta/prelevement/card.php?id=' . $this->id;
1740 }
1741
1742 if ($option != 'nolink') {
1743 // Add param to save lastsearch_values or not
1744 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1745 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1746 $add_save_lastsearch_values = 1;
1747 }
1748 if ($add_save_lastsearch_values) {
1749 $url .= '&save_lastsearch_values=1';
1750 }
1751 }
1752
1753 $linkclose = '';
1754 if (empty($notooltip)) {
1755 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1756 $label = $langs->trans("ShowMyObject");
1757 $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1758 }
1759 $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1760 $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
1761 } else {
1762 $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1763 }
1764
1765 $linkstart = '<a href="' . $url . '"';
1766 $linkstart .= $linkclose . '>';
1767 $linkend = '</a>';
1768
1769 $result .= $linkstart;
1770 if ($withpicto) {
1771 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1772 }
1773 if ($withpicto != 2) {
1774 $result .= $this->ref;
1775 }
1776 $result .= $linkend;
1777
1778 global $action, $hookmanager;
1779 $hookmanager->initHooks(array('banktransferdao'));
1780 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1781 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1782 if ($reshook > 0) {
1783 $result = $hookmanager->resPrint;
1784 } else {
1785 $result .= $hookmanager->resPrint;
1786 }
1787
1788 return $result;
1789 }
1790
1791
1798 public function deleteNotificationById($rowid)
1799 {
1800 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "notify_def";
1801 $sql .= " WHERE rowid = " . ((int) $rowid);
1802
1803 if ($this->db->query($sql)) {
1804 return 0;
1805 } else {
1806 return -1;
1807 }
1808 }
1809
1817 public function deleteNotification($user, $action)
1818 {
1819 if (is_object($user)) {
1820 $userid = $user->id;
1821 } else { // If user is an id
1822 $userid = $user;
1823 }
1824
1825 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "notify_def";
1826 $sql .= " WHERE fk_user = " . ((int) $userid) . " AND fk_action = " . ((int) $action);
1827
1828 if ($this->db->query($sql)) {
1829 return 0;
1830 } else {
1831 return -1;
1832 }
1833 }
1834
1835 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1844 public function addNotification($db, $user, $action)
1845 {
1846 // phpcs:enable
1847 $result = 0;
1848
1849 if (is_object($user)) {
1850 $userid = $user->id;
1851 } else { // If user is an id
1852 $userid = $user;
1853 }
1854
1855 if ($this->deleteNotification($user, $action) == 0) {
1856 $now = dol_now();
1857
1858 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "notify_def (datec, fk_user, fk_soc, fk_contact, fk_action)";
1859 $sql .= " VALUES ('" . $this->db->idate($now) . "', " . ((int) $userid) . ", NULL, NULL, " . ((int) $action) . ")";
1860
1861 dol_syslog("adnotiff: " . $sql);
1862 if ($this->db->query($sql)) {
1863 $result = 0;
1864 } else {
1865 $result = -1;
1866 dol_syslog(get_class($this) . "::addNotification Error $result");
1867 }
1868 }
1869
1870 return $result;
1871 }
1872
1873
1889 public function generate(string $format = 'ALL', int $executiondate = 0, string $type = 'direct-debit', int $fk_bank_account = 0, int $forsalary = 0, int $thirdpartyBANId = 0)
1890 {
1891 global $conf, $langs, $mysoc;
1892
1893 //TODO: Optimize code to read lines in a single function
1894
1895 // Clean params
1896 if (empty($fk_bank_account)) {
1897 $fk_bank_account = ($type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
1898 }
1899
1900 $result = 0;
1901
1902 dol_syslog(get_class($this) . "::generate build file=" . $this->filename . " type=" . $type);
1903
1904 $this->file = fopen($this->filename, "w");
1905 if ($this->file == false) {
1906 $this->error = $langs->trans('ErrorFailedToOpenFile', $this->filename);
1907 return -1;
1908 }
1909
1910 $found = 0;
1911 $this->total = 0;
1912
1913 // Build file for European countries
1914 if ($mysoc->isInSEPA()) {
1915 $found++;
1916
1917 if ($type != 'bank-transfer') {
1921 // SEPA Initialisation
1922 $CrLf = "\n";
1923
1924 $now = dol_now();
1925
1926 $dateTime_ECMA = dol_print_date($now, '%Y-%m-%dT%H:%M:%S');
1927
1928 $date_actu = $now;
1929 if (!empty($executiondate)) {
1930 $date_actu = $executiondate;
1931 }
1932
1933 $dateTime_YMD = dol_print_date($date_actu, '%Y%m%d');
1934 $dateTime_YMDHMS = dol_print_date($date_actu, '%Y%m%d%H%M%S');
1935 $fileDebiteurSection = '';
1936 $fileEmetteurSection = '';
1937 $i = 0;
1938
1939 /*
1940 * Section Debitor (sepa Debiteurs block lines)
1941 */
1942
1943 $sql = "SELECT soc.rowid as socid, soc.code_client as code, soc.address, soc.zip, soc.town, c.code as country_code,";
1944 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
1945 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
1946 $sql .= " f.ref as reffac,";
1947 $sql .= " p.fk_facture as idfac, p.rowid as pid,";
1948 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, rib.rum, rib.date_rum";
1949 $sql .= " FROM";
1950 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
1951 $sql .= " " . MAIN_DB_PREFIX . "facture as f,";
1952 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
1953 $sql .= " " . MAIN_DB_PREFIX . "societe as soc,";
1954 $sql .= " " . MAIN_DB_PREFIX . "c_country as c,";
1955 $sql .= " " . MAIN_DB_PREFIX . "societe_rib as rib";
1956 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
1957 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
1958 $sql .= " AND p.fk_facture = f.rowid";
1959 $sql .= " AND f.fk_soc = soc.rowid";
1960 $sql .= " AND soc.fk_pays = c.rowid";
1961 $sql .= " AND rib.fk_soc = f.fk_soc";
1962 if (!empty($thirdpartyBANId)) {
1963 $sql .= " AND rib.rowid = " . ((int) $thirdpartyBANId);
1964 } else {
1965 $sql .= " AND rib.default_rib = 1";
1966 }
1967 $sql .= " AND rib.type = 'ban'";
1968
1969 // Define $fileDebiteurSection. One section DrctDbtTxInf per invoice.
1970 $resql = $this->db->query($sql);
1971 $nbtotalDrctDbtTxInf = -1;
1972 if ($resql) {
1973 $cachearraytotestduplicate = array();
1974
1975 $num = $this->db->num_rows($resql);
1976 while ($i < $num) {
1977 $obj = $this->db->fetch_object($resql);
1978
1979 if (!empty($cachearraytotestduplicate[$obj->idfac])) {
1980 $this->error = $langs->trans('ErrorCompanyHasDuplicateDefaultBAN', $obj->socid);
1981 $this->invoice_in_error[$obj->idfac] = $this->error;
1982 $result = -2;
1983 break;
1984 }
1985 $cachearraytotestduplicate[$obj->idfac] = $obj->rowid;
1986
1987 // Get the default value
1988 $daterum = (!empty($obj->date_rum)) ? $this->db->jdate($obj->date_rum) : $this->db->jdate($obj->datec);
1989 $iban = dolDecrypt($obj->iban);
1990 $bic = $obj->bic;
1991 $drum = $obj->drum;
1992 $rum = $obj->rum;
1993
1994 // But if a force bank account is defined, we use it instead
1995 if (!empty($obj->fk_prelevement_demande)) {
1996 $companybankaccountid = 0;
1997
1998 $sqltmp = "SELECT fk_societe_rib FROM ".MAIN_DB_PREFIX."prelevement_demande";
1999 $sqltmp .= " WHERE rowid = ".((int) $obj->fk_prelevement_demande);
2000
2001 $resqltmp = $this->db->query($sqltmp);
2002
2003 $objtmp = $this->db->fetch_object($resqltmp);
2004 if ($objtmp) {
2005 $companybankaccountid = (int) $objtmp->fk_societe_rib;
2006 }
2007
2008 $bankaccount = new CompanyBankAccount($this->db);
2009 $bankaccount->fetch($companybankaccountid);
2010 if ($bankaccount->id > 0) {
2011 $daterum = $bankaccount->date_rum;
2012 $iban = $bankaccount->iban;
2013 $bic = $bankaccount->bic;
2014 $drum = $bankaccount->id;
2015 $rum = $bankaccount->rum;
2016 }
2017 }
2018
2019 $fileDebiteurSection .= $this->EnregDestinataireSEPA($obj->code, $obj->nom, $obj->address, $obj->zip, $obj->town, $obj->country_code, '', '', '', $obj->somme, $obj->reffac, $obj->idfac, $iban, $bic, $daterum, (string) $drum, $rum, $type);
2020
2021 $this->total += $obj->somme;
2022 $i++;
2023 }
2024 $nbtotalDrctDbtTxInf = $i;
2025 } else {
2026 $this->error = $this->db->lasterror();
2027 fwrite($this->file, 'ERROR DEBITOR ' . $sql . $CrLf); // DEBITOR = Customers
2028 $result = -2;
2029 }
2030
2031 // Define $fileEmetteurSection. Start of block PmtInf. Will contains all $nbtotalDrctDbtTxInf
2032 if ($result != -2) {
2033 $fileEmetteurSection .= $this->EnregEmetteurSEPA($conf, $date_actu, $nbtotalDrctDbtTxInf, $this->total, $CrLf, $format, $type, $fk_bank_account);
2034 }
2035
2036 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2037 $this->total = number_format((float) price2num($this->total, 'MT'), 2, ".", "");
2038 }
2039
2043 // SEPA File Header
2044 fwrite($this->file, '<' . '?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>' . $CrLf);
2045 fwrite($this->file, '<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . $CrLf);
2046 fwrite($this->file, ' <CstmrDrctDbtInitn>' . $CrLf);
2047 // SEPA Group header
2048 fwrite($this->file, ' <GrpHdr>' . $CrLf);
2049 fwrite($this->file, ' <MsgId>' . ('DOL/' . $dateTime_YMD . '/DD' . $this->id) . '</MsgId>' . $CrLf);
2050 fwrite($this->file, ' <CreDtTm>' . $dateTime_ECMA . '</CreDtTm>' . $CrLf);
2051 fwrite($this->file, ' <NbOfTxs>' . $i . '</NbOfTxs>' . $CrLf);
2052 fwrite($this->file, ' <CtrlSum>' . $this->total . '</CtrlSum>' . $CrLf);
2053 fwrite($this->file, ' <InitgPty>' . $CrLf);
2054 fwrite($this->file, ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf);
2055 fwrite($this->file, ' <Id>' . $CrLf);
2056 fwrite($this->file, ' <PrvtId>' . $CrLf);
2057 fwrite($this->file, ' <Othr>' . $CrLf);
2058 fwrite($this->file, ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf);
2059 fwrite($this->file, ' </Othr>' . $CrLf);
2060 fwrite($this->file, ' </PrvtId>' . $CrLf);
2061 fwrite($this->file, ' </Id>' . $CrLf);
2062 fwrite($this->file, ' </InitgPty>' . $CrLf);
2063 fwrite($this->file, ' </GrpHdr>' . $CrLf);
2064 // SEPA File Emetteur
2065 if ($result != -2) {
2066 fwrite($this->file, $fileEmetteurSection);
2067 }
2068 // SEPA File Debiteurs
2069 if ($result != -2) {
2070 fwrite($this->file, $fileDebiteurSection);
2071 }
2072 // SEPA FILE FOOTER
2073 fwrite($this->file, ' </PmtInf>' . $CrLf);
2074 fwrite($this->file, ' </CstmrDrctDbtInitn>' . $CrLf);
2075 fwrite($this->file, '</Document>' . $CrLf);
2076 } else {
2080 // SEPA Initialisation
2081 $CrLf = "\n";
2082
2083 $now = dol_now();
2084
2085 $dateTime_ECMA = dol_print_date($now, '%Y-%m-%dT%H:%M:%S');
2086
2087 $date_actu = $now;
2088 if (!empty($executiondate)) {
2089 $date_actu = $executiondate;
2090 }
2091
2092 $dateTime_YMD = dol_print_date($date_actu, '%Y%m%d');
2093 $dateTime_YMDHMS = dol_print_date($date_actu, '%Y%m%d%H%M%S');
2094 $fileCrediteurSection = '';
2095 $fileEmetteurSection = '';
2096 $i = 0;
2097
2098 /*
2099 * Section Creditor (sepa Crediteurs block lines)
2100 */
2101 if (!empty($forsalary)) {
2102 $sql = "SELECT u.rowid as userId, u.address, u.zip, u.town, c.code as country_code, CONCAT(u.firstname,' ',u.lastname) as nom,";
2103 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
2104 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
2105 $sql .= " s.ref as reffac,";
2106 $sql .= " p.fk_salary as idfac,";
2107 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, '' as rum, '' as date_rum";
2108 $sql .= " FROM";
2109 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2110 $sql .= " " . MAIN_DB_PREFIX . "salary as s,";
2111 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
2112 $sql .= " " . MAIN_DB_PREFIX . "user as u";
2113 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON u.fk_country = c.rowid,";
2114 $sql .= " " . MAIN_DB_PREFIX . "user_rib as rib";
2115 $sql .= " WHERE pl.fk_prelevement_bons=" . ((int) $this->id);
2116 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2117 $sql .= " AND p.fk_salary = s.rowid";
2118 $sql .= " AND s.fk_user = u.rowid";
2119 $sql .= " AND rib.fk_user = s.fk_user";
2120 } else {
2121 $sql = "SELECT soc.rowid as socid, soc.code_client as code, soc.address, soc.zip, soc.town, c.code as country_code,";
2122 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
2123 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
2124 $sql .= " f.ref as reffac, f.ref_supplier as fac_ref_supplier,";
2125 $sql .= " p.fk_facture_fourn as idfac,";
2126 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, rib.rum, rib.date_rum";
2127 $sql .= " FROM";
2128 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2129 $sql .= " " . MAIN_DB_PREFIX . "facture_fourn as f,";
2130 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
2131 $sql .= " " . MAIN_DB_PREFIX . "societe as soc";
2132 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON soc.fk_pays = c.rowid,";
2133 $sql .= " " . MAIN_DB_PREFIX . "societe_rib as rib";
2134 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2135 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2136 $sql .= " AND p.fk_facture_fourn = f.rowid";
2137 $sql .= " AND f.fk_soc = soc.rowid";
2138 $sql .= " AND rib.fk_soc = f.fk_soc";
2139 if (!empty($thirdpartyBANId)) {
2140 $sql .= " AND rib.rowid = " . ((int) $thirdpartyBANId);
2141 } else {
2142 $sql .= " AND rib.default_rib = 1";
2143 }
2144 $sql .= " AND rib.type = 'ban'";
2145 }
2146 // Define $fileCrediteurSection. One section DrctDbtTxInf per invoice.
2147 $nbtotalDrctDbtTxInf = -1;
2148
2149 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
2150
2151 $resql = $this->db->query($sql);
2152 if ($resql) {
2153 $cachearraytotestduplicate = array();
2154
2155 $num = $this->db->num_rows($resql);
2156 while ($i < $num) {
2157 $obj = $this->db->fetch_object($resql);
2158
2159 // Test to avoid duplicate default IBAN, to ask user to clean its data
2160 if (!empty($cachearraytotestduplicate[$obj->idfac])) {
2161 $this->error = $langs->trans('ErrorCompanyHasDuplicateDefaultBAN', $obj->socid);
2162 $this->invoice_in_error[$obj->idfac] = $this->error;
2163 $result = -2;
2164 break;
2165 }
2166
2167 $cachearraytotestduplicate[$obj->idfac] = $obj->rowid;
2168
2169 // Get the default value
2170 $daterum = (!empty($obj->date_rum)) ? $this->db->jdate($obj->date_rum) : $this->db->jdate($obj->datec);
2171 $iban = dolDecrypt($obj->iban);
2172 $bic = $obj->bic;
2173 $drum = $obj->drum;
2174 $rum = $obj->rum;
2175
2176 // But if a force bank account is defined, we use it instead
2177 if (!empty($obj->fk_prelevement_demande)) {
2178 $companybankaccountid = 0;
2179
2180 $sqltmp = "SELECT fk_societe_rib FROM ".MAIN_DB_PREFIX."prelevement_demande";
2181 $sqltmp .= " WHERE rowid = ".((int) $obj->fk_prelevement_demande);
2182
2183 $resqltmp = $this->db->query($sqltmp);
2184
2185 $objtmp = $this->db->fetch_object($resqltmp);
2186 if ($objtmp) {
2187 $companybankaccountid = (int) $objtmp->fk_societe_rib;
2188 }
2189
2190 $bankaccount = new CompanyBankAccount($this->db);
2191 $bankaccount->fetch($companybankaccountid);
2192 if ($bankaccount->id > 0) {
2193 $daterum = $bankaccount->date_rum;
2194 $iban = $bankaccount->iban;
2195 $bic = $bankaccount->bic;
2196 $drum = $bankaccount->id;
2197 $rum = $bankaccount->rum;
2198 }
2199 }
2200
2201 $refobj = $obj->reffac;
2202 if (empty($refobj) && !empty($forsalary)) { // If ref of salary not defined, we force a value
2203 $refobj = "SAL" . $obj->idfac;
2204 }
2205
2206 $fileCrediteurSection .= $this->EnregDestinataireSEPA($obj->code, $obj->nom, $obj->address, $obj->zip, $obj->town, $obj->country_code, '', '', '', $obj->somme, $refobj, $obj->idfac, $iban, $bic, $daterum, (string) $drum, $rum, $type, $obj->fac_ref_supplier);
2207
2208 $this->total += $obj->somme;
2209 $i++;
2210 }
2211 $nbtotalDrctDbtTxInf = $i;
2212 } else {
2213 $this->error = $this->db->lasterror();
2214 fwrite($this->file, 'ERROR CREDITOR ' . $sql . $CrLf); // CREDITORS = Suppliers
2215 $result = -2;
2216 }
2217 // Define $fileEmetteurSection. Start of block PmtInf. Will contains all $nbtotalDrctDbtTxInf
2218 if ($result != -2) {
2219 $fileEmetteurSection .= $this->EnregEmetteurSEPA($conf, $date_actu, $nbtotalDrctDbtTxInf, $this->total, $CrLf, $format, $type);
2220 }
2221
2222 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2223 $this->total = number_format((float) price2num($this->total, 'MT'), 2, ".", "");
2224 }
2225
2229 // SEPA File Header
2230 fwrite($this->file, '<' . '?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>' . $CrLf);
2231 fwrite($this->file, '<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . $CrLf);
2232 fwrite($this->file, ' <CstmrCdtTrfInitn>' . $CrLf);
2233 // SEPA Group header
2234 fwrite($this->file, ' <GrpHdr>' . $CrLf);
2235 fwrite($this->file, ' <MsgId>' . ('DOL/' . $dateTime_YMD . '/CT' . $this->id) . '</MsgId>' . $CrLf);
2236 fwrite($this->file, ' <CreDtTm>' . $dateTime_ECMA . '</CreDtTm>' . $CrLf);
2237 fwrite($this->file, ' <NbOfTxs>' . $i . '</NbOfTxs>' . $CrLf);
2238 fwrite($this->file, ' <CtrlSum>' . $this->total . '</CtrlSum>' . $CrLf);
2239 fwrite($this->file, ' <InitgPty>' . $CrLf);
2240 fwrite($this->file, ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf);
2241 fwrite($this->file, ' <Id>' . $CrLf);
2242 fwrite($this->file, ' <PrvtId>' . $CrLf);
2243 fwrite($this->file, ' <Othr>' . $CrLf);
2244 fwrite($this->file, ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf);
2245 fwrite($this->file, ' </Othr>' . $CrLf);
2246 fwrite($this->file, ' </PrvtId>' . $CrLf);
2247 fwrite($this->file, ' </Id>' . $CrLf);
2248 fwrite($this->file, ' </InitgPty>' . $CrLf);
2249 fwrite($this->file, ' </GrpHdr>' . $CrLf);
2250 // SEPA File Emetteur (mycompany)
2251 if ($result != -2) {
2252 fwrite($this->file, $fileEmetteurSection);
2253 }
2254 // SEPA File Creditors
2255 if ($result != -2) {
2256 fwrite($this->file, $fileCrediteurSection);
2257 }
2258 // SEPA FILE FOOTER
2259 fwrite($this->file, ' </PmtInf>' . $CrLf);
2260 fwrite($this->file, ' </CstmrCdtTrfInitn>' . $CrLf);
2261 fwrite($this->file, '</Document>' . $CrLf);
2262 }
2263 }
2264
2265 // Build file for Other Countries with unknown format
2266 if (!$found) {
2267 if ($type != 'bank-transfer') {
2268 $sql = "SELECT pl.amount";
2269 $sql .= " FROM";
2270 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2271 $sql .= " " . MAIN_DB_PREFIX . "facture as f,";
2272 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p";
2273 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2274 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2275 $sql .= " AND p.fk_facture = f.rowid";
2276
2277 // Lines
2278 $i = 0;
2279 $resql = $this->db->query($sql);
2280 if ($resql) {
2281 $num = $this->db->num_rows($resql);
2282
2283 while ($i < $num) {
2284 $obj = $this->db->fetch_object($resql);
2285 $this->total += $obj->amount;
2286
2287 // TODO Write record into file
2288 $i++;
2289 }
2290 } else {
2291 $result = -2;
2292 }
2293 } else {
2294 $sql = "SELECT pl.amount";
2295 $sql .= " FROM";
2296 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2297 $sql .= " " . MAIN_DB_PREFIX . "facture_fourn as f,";
2298 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p";
2299 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2300 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2301 $sql .= " AND p.fk_facture_fourn = f.rowid";
2302 // Lines
2303 $i = 0;
2304 $resql = $this->db->query($sql);
2305 if ($resql) {
2306 $num = $this->db->num_rows($resql);
2307
2308 while ($i < $num) {
2309 $obj = $this->db->fetch_object($resql);
2310 $this->total += $obj->amount;
2311
2312 // TODO Write record into file
2313 $i++;
2314 }
2315 } else {
2316 $result = -2;
2317 }
2318 }
2319
2320 $langs->load('withdrawals');
2321
2322 // TODO Add here code to generate a generic file
2323 fwrite($this->file, $langs->transnoentitiesnoconv('WithdrawalFileNotCapable', $mysoc->country_code));
2324 }
2325
2326 fclose($this->file);
2327 dolChmod($this->filename);
2328
2329 return $result;
2330 }
2331
2332
2341 public static function buildRumNumber($row_code_client, $row_datec, $row_drum)
2342 {
2343 global $langs;
2344
2345 $pre = substr(dol_string_nospecial(dol_string_unaccent($langs->transnoentitiesnoconv('RUM'))), 0, 3); // Must always be on 3 char ('RUM' or 'UMR'. This is a protection against bad translation)
2346
2347 // 3 char + '-' + 10 (yymmddHHMM) + '-' + id + '-' + code. Must be under 32 (SEPA char limit for MndtId is however 35).
2348 return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc((string) $row_drum . ($row_code_client ? '-' . $row_code_client : ''), 17, 'right', 'UTF-8', 1);
2349 }
2350
2351
2352 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2369 public function EnregDestinataire($rowid, $client_nom, $rib_banque, $rib_guichet, $rib_number, $amount, $ref, $facid, $rib_dom = '', $type = 'direct-debit')
2370 {
2371 // phpcs:enable
2372 fwrite($this->file, "06");
2373 fwrite($this->file, "08"); // Prelevement ordinaire
2374
2375 fwrite($this->file, " "); // Zone Reservee B2
2376
2377 fwrite($this->file, $this->emetteur_ics); // ICS
2378
2379 // Date d'echeance C1
2380
2381 fwrite($this->file, " ");
2382 fwrite($this->file, dol_print_date($this->date_echeance, "%d%m", 'gmt'));
2383 fwrite($this->file, substr(dol_print_date($this->date_echeance, "%y", 'gmt'), 1));
2384
2385 // Raison Sociale Destinataire C2
2386
2387 fwrite($this->file, substr(strtoupper($client_nom) . " ", 0, 24));
2388
2389 // Address optional D1
2390 $address = strtr($rib_dom, array(" " => "-", chr(13) => " ", chr(10) => ""));
2391 fwrite($this->file, substr($address . " ", 0, 24));
2392
2393 // Zone Reservee D2
2394
2395 fwrite($this->file, substr(" ", 0, 8));
2396
2397 // Code Guichet D3
2398
2399 fwrite($this->file, $rib_guichet);
2400
2401 // Numero de compte D4
2402
2403 fwrite($this->file, substr("000000000000000" . $rib_number, -11));
2404
2405 // Zone E Montant
2406
2407 $montant = (round($amount, 2) * 100);
2408
2409 fwrite($this->file, substr("000000000000000" . $montant, -16));
2410
2411 // Label F
2412
2413 fwrite($this->file, substr("*_" . $ref . "_RDVnet" . $rowid . " ", 0, 31));
2414
2415 // Code etablissement G1
2416
2417 fwrite($this->file, $rib_banque);
2418
2419 // Zone Reservee G2
2420
2421 fwrite($this->file, substr(" ", 0, 5));
2422
2423 fwrite($this->file, "\n");
2424 }
2425
2426
2427 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2453 public function EnregDestinataireSEPA($row_code_client, $row_nom, $row_address, $row_zip, $row_town, $row_country_code, $row_cb, $row_cg, $row_cc, $row_somme, $row_ref, $row_idfac, $row_iban, $row_bic, $row_datec, $row_drum, $row_rum, $type = 'direct-debit', $row_comment = '')
2454 {
2455 // phpcs:enable
2456 global $conf, $mysoc, $hookmanager;
2457
2458 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2459 $row_somme = number_format((float) price2num($row_somme, 'MT'), 2, ".", "");
2460 } else {
2461 $row_somme = round((float) $row_somme, 2);
2462 }
2463
2464 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
2465
2466 $CrLf = "\n";
2467 $Rowing = sprintf("%010d", $row_idfac);
2468
2469 // Define value for RUM
2470 // Example: RUM-CustomerCode-CustomerBankAccountId-01424448606 (note: Date is the timestamp of the date of creation of CustomerBankAccountId)
2471 $Rum = (empty($row_rum) ? $this->buildRumNumber($row_code_client, $row_datec, $row_drum) : $row_rum);
2472
2473 // Define date of RUM signature
2474 $DtOfSgntr = dol_print_date($row_datec, '%Y-%m-%d');
2475 $XML_RESULT = '';
2476
2477 if (!is_object($hookmanager)) {
2478 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2479 $hookmanager = new HookManager($this->db);
2480 }
2481 $hookmanager->initHooks(array('bonprelevementdao'));
2482 $parameters = array(
2483 'row_code_client' => &$row_code_client, 'row_nom' => &$row_nom, 'row_address' => &$row_address, 'row_zip' => &$row_zip, 'row_town' => &$row_town,
2484 'row_country_code' => &$row_country_code, 'row_cb' => &$row_cb, 'row_cg' => &$row_cg, 'row_cc' => &$row_cc, 'row_somme' => &$row_somme,
2485 'row_ref' => &$row_ref, 'row_idfac' => &$row_idfac, 'row_iban' => &$row_iban, 'row_bic' => &$row_bic, 'row_datec' => &$row_datec,
2486 'row_drum' => &$row_drum, 'row_rum' => &$row_rum, 'type' => &$type, 'row_comment' => &$row_comment,
2487 'crlf' => &$CrLf, 'rowing' => &$Rowing, 'rum' => &$Rum, 'dtofsgntr' => &$DtOfSgntr,
2488 );
2489 $reshook = $hookmanager->executeHooks('enregDestinataireSEPA', $parameters, $this); // Note that $action and $object may have been modified by some hooks
2490 if (empty($reshook)) {
2491 if ($type != 'bank-transfer') {
2492 // SEPA Paiement Information of buyer for Direct Debit
2493 $XML_DEBITOR = '';
2494 $XML_DEBITOR .= ' <DrctDbtTxInf>' . $CrLf;
2495 $XML_DEBITOR .= ' <PmtId>' . $CrLf;
2496 // Add EndToEndId. Must be a unique ID for each payment (for example by including bank, buyer or seller, date, checksum)
2497 $XML_DEBITOR .= ' <EndToEndId>' . ((getDolGlobalString('PRELEVEMENT_END_TO_END') != "") ? $conf->global->PRELEVEMENT_END_TO_END : ('DD-' . dol_trunc($row_idfac . '-' . $row_ref, 20, 'right', 'UTF-8', 1)) . '-' . $Rowing) . '</EndToEndId>' . $CrLf; // ISO20022 states that EndToEndId has a MaxLength of 35 characters
2498 $XML_DEBITOR .= ' </PmtId>' . $CrLf;
2499 $XML_DEBITOR .= ' <InstdAmt Ccy="EUR">' . $row_somme . '</InstdAmt>' . $CrLf;
2500 $XML_DEBITOR .= ' <DrctDbtTx>' . $CrLf;
2501 $XML_DEBITOR .= ' <MndtRltdInf>' . $CrLf;
2502 $XML_DEBITOR .= ' <MndtId>' . $Rum . '</MndtId>' . $CrLf;
2503 $XML_DEBITOR .= ' <DtOfSgntr>' . $DtOfSgntr . '</DtOfSgntr>' . $CrLf;
2504 $XML_DEBITOR .= ' <AmdmntInd>false</AmdmntInd>' . $CrLf;
2505 $XML_DEBITOR .= ' </MndtRltdInf>' . $CrLf;
2506 $XML_DEBITOR .= ' </DrctDbtTx>' . $CrLf;
2507 $XML_DEBITOR .= ' <DbtrAgt>' . $CrLf;
2508 $XML_DEBITOR .= ' <FinInstnId>' . $CrLf;
2509 if (getDolGlobalInt('WITHDRAWAL_WITHOUT_BIC') == 0) {
2510 $XML_DEBITOR .= ' <BIC>' . $row_bic . '</BIC>' . $CrLf;
2511 }
2512 $XML_DEBITOR .= ' </FinInstnId>' . $CrLf;
2513 $XML_DEBITOR .= ' </DbtrAgt>' . $CrLf;
2514 $XML_DEBITOR .= ' <Dbtr>' . $CrLf;
2515 $XML_DEBITOR .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($row_nom), ' '))) . '</Nm>' . $CrLf;
2516 $XML_DEBITOR .= ' <PstlAdr>' . $CrLf;
2517 $XML_DEBITOR .= ' <Ctry>' . $row_country_code . '</Ctry>' . $CrLf;
2518 $addressline1 = strtr($row_address, array(chr(13) => ", ", chr(10) => ""));
2519 $addressline2 = strtr($row_zip . (($row_zip && $row_town) ? ' ' : '') . (string) $row_town, array(chr(13) => ", ", chr(10) => ""));
2520 if (trim($addressline1)) {
2521 $XML_DEBITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2522 }
2523 if (trim($addressline2)) {
2524 $XML_DEBITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2525 }
2526 $XML_DEBITOR .= ' </PstlAdr>' . $CrLf;
2527 $XML_DEBITOR .= ' </Dbtr>' . $CrLf;
2528 $XML_DEBITOR .= ' <DbtrAcct>' . $CrLf;
2529 $XML_DEBITOR .= ' <Id>' . $CrLf;
2530 $XML_DEBITOR .= ' <IBAN>' . preg_replace('/\s/', '', $row_iban) . '</IBAN>' . $CrLf;
2531 $XML_DEBITOR .= ' </Id>' . $CrLf;
2532 $XML_DEBITOR .= ' </DbtrAcct>' . $CrLf;
2533 $XML_DEBITOR .= ' <RmtInf>' . $CrLf;
2534
2535 // Structured data for Belgium
2536 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $mysoc->country_code == 'BE') {
2537 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
2538
2539 $invoicestatic = new Facture($this->db);
2540 $invoicestatic->fetch($row_idfac);
2541
2542 $invoicePaymentKey = dolBECalculateStructuredCommunication($invoicestatic->ref, $invoicestatic->type);
2543 $XML_DEBITOR .= ' <strd>' . $invoicePaymentKey . '</strd>' . $CrLf;
2544 } else {
2545 // A string with some information on payment - 140 max
2546 $XML_DEBITOR .= ' <Ustrd>' . getDolGlobalString('PRELEVEMENT_USTRD', dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($row_ref . ($row_comment ? ' - ' . $row_comment : '')), '', '', '', 1), 135, 'right', 'UTF-8', 1))) . '</Ustrd>' . $CrLf; // Free unstuctured data - 140 max
2547 }
2548 $XML_DEBITOR .= ' </RmtInf>' . $CrLf;
2549 $XML_DEBITOR .= ' </DrctDbtTxInf>' . $CrLf;
2550
2551 $XML_RESULT = $XML_DEBITOR;
2552 } else {
2553 // SEPA Payment Information of seller for Credit Transfer
2554 $XML_CREDITOR = '';
2555 $XML_CREDITOR .= ' <CdtTrfTxInf>' . $CrLf;
2556 $XML_CREDITOR .= ' <PmtId>' . $CrLf;
2557 // Add EndToEndId. Must be a unique ID for each payment (for example by including bank, buyer or seller, date, checksum)
2558 $XML_CREDITOR .= ' <EndToEndId>' . ((getDolGlobalString('PRELEVEMENT_END_TO_END') != "") ? getDolGlobalString("PRELEVEMENT_END_TO_END") : ('CT-' . dol_trunc($row_idfac . '-' . $row_ref, 20, 'right', 'UTF-8', 1)) . '-' . $Rowing) . '</EndToEndId>' . $CrLf; // ISO20022 states that EndToEndId has a MaxLength of 35 characters
2559 $XML_CREDITOR .= ' </PmtId>' . $CrLf;
2560 if (!empty($this->sepa_xml_pti_in_ctti)) {
2561 $XML_CREDITOR .= ' <PmtTpInf>' . $CrLf;
2562
2563 // Can be 'NORM' for normal or 'HIGH' for high priority level
2564 if (getDolGlobalString('PAYMENTBYBANKTRANSFER_FORCE_HIGH_PRIORITY')) {
2565 $instrprty = 'HIGH';
2566 } else {
2567 $instrprty = 'NORM';
2568 }
2569
2570 // Set $categoryPurpose: CORE, TREA, SUPP, ...
2571 $categoryPurpose = getDolGlobalString('PAYMENTBYBANKTRANSFER_CUSTOM_CATEGORY_PURPOSE', 'CORE');
2572
2573 $XML_CREDITOR .= ' <InstrPrty>' . $instrprty . '</InstrPrty>' . $CrLf;
2574 $XML_CREDITOR .= ' <SvcLvl>' . $CrLf;
2575 $XML_CREDITOR .= ' <Cd>SEPA</Cd>' . $CrLf;
2576 $XML_CREDITOR .= ' </SvcLvl>' . $CrLf;
2577 $XML_CREDITOR .= ' <CtgyPurp>' . $CrLf;
2578 $XML_CREDITOR .= ' <Cd>' . $categoryPurpose . '</Cd>' . $CrLf;
2579 $XML_CREDITOR .= ' </CtgyPurp>' . $CrLf;
2580 $XML_CREDITOR .= ' </PmtTpInf>' . $CrLf;
2581 }
2582 $XML_CREDITOR .= ' <Amt>' . $CrLf;
2583 $XML_CREDITOR .= ' <InstdAmt Ccy="EUR">' . round((float) $row_somme, 2) . '</InstdAmt>' . $CrLf;
2584 $XML_CREDITOR .= ' </Amt>' . $CrLf;
2585 /*
2586 $XML_CREDITOR .= ' <DrctDbtTx>'.$CrLf;
2587 $XML_CREDITOR .= ' <MndtRltdInf>'.$CrLf;
2588 $XML_CREDITOR .= ' <MndtId>'.$Rum.'</MndtId>'.$CrLf;
2589 $XML_CREDITOR .= ' <DtOfSgntr>'.$DtOfSgntr.'</DtOfSgntr>'.$CrLf;
2590 $XML_CREDITOR .= ' <AmdmntInd>false</AmdmntInd>'.$CrLf;
2591 $XML_CREDITOR .= ' </MndtRltdInf>'.$CrLf;
2592 $XML_CREDITOR .= ' </DrctDbtTx>'.$CrLf;
2593 */
2594 //$XML_CREDITOR .= ' <ChrgBr>SLEV</ChrgBr>'.$CrLf;
2595 $XML_CREDITOR .= ' <CdtrAgt>' . $CrLf;
2596 $XML_CREDITOR .= ' <FinInstnId>' . $CrLf;
2597 $XML_CREDITOR .= ' <BIC>' . $row_bic . '</BIC>' . $CrLf;
2598 $XML_CREDITOR .= ' </FinInstnId>' . $CrLf;
2599 $XML_CREDITOR .= ' </CdtrAgt>' . $CrLf;
2600 $XML_CREDITOR .= ' <Cdtr>' . $CrLf;
2601 $XML_CREDITOR .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($row_nom), ' '))) . '</Nm>' . $CrLf;
2602 $XML_CREDITOR .= ' <PstlAdr>' . $CrLf;
2603 $XML_CREDITOR .= ' <Ctry>' . $row_country_code . '</Ctry>' . $CrLf;
2604 $addressline1 = strtr($row_address, array(chr(13) => ", ", chr(10) => ""));
2605 $addressline2 = strtr($row_zip . (($row_zip && $row_town) ? ' ' : '') . (string) $row_town, array(chr(13) => ", ", chr(10) => ""));
2606 if (trim($addressline1)) {
2607 $XML_CREDITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2608 }
2609 if (trim($addressline2)) {
2610 $XML_CREDITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2611 }
2612 $XML_CREDITOR .= ' </PstlAdr>' . $CrLf;
2613 $XML_CREDITOR .= ' </Cdtr>' . $CrLf;
2614 $XML_CREDITOR .= ' <CdtrAcct>' . $CrLf;
2615 $XML_CREDITOR .= ' <Id>' . $CrLf;
2616 $XML_CREDITOR .= ' <IBAN>' . preg_replace('/\s/', '', $row_iban) . '</IBAN>' . $CrLf;
2617 $XML_CREDITOR .= ' </Id>' . $CrLf;
2618 $XML_CREDITOR .= ' </CdtrAcct>' . $CrLf;
2619 $XML_CREDITOR .= ' <RmtInf>' . $CrLf;
2620 // A string with some information on payment - 140 max
2621 $XML_CREDITOR .= ' <Ustrd>' . getDolGlobalString('CREDITTRANSFER_USTRD', dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($row_ref . ($row_comment ? ' - ' . $row_comment : '')), '', '', '', 1), 135, 'right', 'UTF-8', 1))) . '</Ustrd>' . $CrLf; // Free unstructured data - 140 max
2622 $XML_CREDITOR .= ' </RmtInf>' . $CrLf;
2623 $XML_CREDITOR .= ' </CdtTrfTxInf>' . $CrLf;
2624
2625 $XML_RESULT = $XML_CREDITOR;
2626 }
2627 } elseif ($reshook > 0) {
2628 $XML_RESULT = $hookmanager->resPrint;
2629 }
2630 $XML_RESULT .= $hookmanager->resPrint;
2631
2632 return $XML_RESULT;
2633 }
2634
2635
2636 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2645 public function EnregEmetteur($type = 'direct-debit')
2646 {
2647 // phpcs:enable
2648 fwrite($this->file, "03");
2649 fwrite($this->file, "08"); // Prelevement ordinaire
2650
2651 fwrite($this->file, " "); // Zone Reservee B2
2652
2653 fwrite($this->file, $this->emetteur_ics); // ICS
2654
2655 // Date d'echeance C1
2656
2657 fwrite($this->file, " ");
2658 fwrite($this->file, dol_print_date($this->date_echeance, "%d%m", 'gmt'));
2659 fwrite($this->file, substr(dol_print_date($this->date_echeance, "%y", 'gmt'), 1));
2660
2661 // Raison Sociale C2
2662
2663 fwrite($this->file, substr($this->raison_sociale . " ", 0, 24));
2664
2665 // Ref of thirdparty on 7 characters
2666
2667 fwrite($this->file, substr($this->reference_remise . " ", 0, 7));
2668
2669 // Zone Reservee D1-2
2670
2671 fwrite($this->file, substr(" ", 0, 17));
2672
2673 // Zone Reservee D2
2674
2675 fwrite($this->file, substr(" ", 0, 2));
2676 fwrite($this->file, "E");
2677 fwrite($this->file, substr(" ", 0, 5));
2678
2679 // Code Guichet D3
2680
2681 fwrite($this->file, $this->emetteur_code_guichet);
2682
2683 // Numero de compte D4
2684
2685 fwrite($this->file, substr("000000000000000" . $this->emetteur_numero_compte, -11));
2686
2687 // Zone Reservee E
2688
2689 fwrite($this->file, substr(" ", 0, 16));
2690
2691 // Zone Reservee F
2692
2693 fwrite($this->file, substr(" ", 0, 31));
2694
2695 // Code etablissement
2696
2697 fwrite($this->file, $this->emetteur_code_banque);
2698
2699 // Zone Reservee G
2700
2701 fwrite($this->file, substr(" ", 0, 5));
2702
2703 fwrite($this->file, "\n");
2704 }
2705
2706
2707 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2722 public function EnregEmetteurSEPA($configuration, $ladate, $nombre, $total, $CrLf = '\n', $format = 'FRST', $type = 'direct-debit', $fk_bank_account = 0)
2723 {
2724 // phpcs:enable
2725
2726 // Clean parameters
2727 $dateTime_YMD = dol_print_date($ladate, '%Y%m%d');
2728 $dateTime_ETAD = dol_print_date($ladate, '%Y-%m-%d');
2729 $dateTime_YMDHMS = dol_print_date($ladate, '%Y-%m-%dT%H:%M:%S');
2730
2731 // Clean params
2732 if (empty($fk_bank_account)) {
2733 $fk_bank_account = ($type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
2734 }
2735
2736 // Get data of bank account
2737 $account = new Account($this->db);
2738 if ($account->fetch($fk_bank_account) > 0) {
2739 $this->emetteur_code_banque = $account->code_banque;
2740 $this->emetteur_code_guichet = $account->code_guichet;
2741 $this->emetteur_numero_compte = $account->number;
2742 $this->emetteur_number_key = $account->cle_rib;
2743 $this->sepa_xml_pti_in_ctti = (bool) $account->pti_in_ctti;
2744 $this->emetteur_iban = $account->iban;
2745 $this->emetteur_bic = $account->bic;
2746
2747 $this->emetteur_ics = (($type == 'bank-transfer' && getDolGlobalString("SEPA_USE_IDS")) ? $account->ics_transfer : $account->ics); // Ex: PRELEVEMENT_ICS = "FR78ZZZ123456";
2748
2749 $this->raison_sociale = $account->owner_name;
2750 }
2751
2752 // Get pending payments
2753 $sql = "SELECT rowid, ref";
2754 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as pb";
2755 $sql .= " WHERE pb.rowid = " . ((int) $this->id);
2756
2757 $resql = $this->db->query($sql);
2758 if ($resql) {
2759 $obj = $this->db->fetch_object($resql);
2760
2761 $country = explode(':', getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY'));
2762 $IdBon = sprintf("%05d", $obj->rowid);
2763 $RefBon = $obj->ref;
2764 $localInstrument = getDolGlobalString('PAYMENTBYBANKTRANSFER_CUSTOM_LOCAL_INSTRUMENT', 'CORE');
2765
2766 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2767 $total = number_format((float) price2num($total, 'MT'), 2, ".", "");
2768 }
2769
2770 if ($type != 'bank-transfer') {
2771 // SEPA Paiement Information of my company for Direct Debit
2772 $XML_SEPA_INFO = '';
2773 $XML_SEPA_INFO .= ' <PmtInf>' . $CrLf;
2774 $XML_SEPA_INFO .= ' <PmtInfId>' . ('DOL/' . $dateTime_YMD . '/DD' . $IdBon . '-' . $RefBon) . '</PmtInfId>' . $CrLf;
2775 $XML_SEPA_INFO .= ' <PmtMtd>DD</PmtMtd>' . $CrLf;
2776 $XML_SEPA_INFO .= ' <NbOfTxs>' . $nombre . '</NbOfTxs>' . $CrLf;
2777 $XML_SEPA_INFO .= ' <CtrlSum>' . $total . '</CtrlSum>' . $CrLf;
2778 $XML_SEPA_INFO .= ' <PmtTpInf>' . $CrLf;
2779 $XML_SEPA_INFO .= ' <SvcLvl>' . $CrLf;
2780 $XML_SEPA_INFO .= ' <Cd>SEPA</Cd>' . $CrLf;
2781 $XML_SEPA_INFO .= ' </SvcLvl>' . $CrLf;
2782 $XML_SEPA_INFO .= ' <LclInstrm>' . $CrLf;
2783 $XML_SEPA_INFO .= ' <Cd>' . $localInstrument . '</Cd>' . $CrLf;
2784 $XML_SEPA_INFO .= ' </LclInstrm>' . $CrLf;
2785 $XML_SEPA_INFO .= ' <SeqTp>' . $format . '</SeqTp>' . $CrLf;
2786 $XML_SEPA_INFO .= ' </PmtTpInf>' . $CrLf;
2787 $XML_SEPA_INFO .= ' <ReqdColltnDt>' . $dateTime_ETAD . '</ReqdColltnDt>' . $CrLf;
2788 $XML_SEPA_INFO .= ' <Cdtr>' . $CrLf;
2789 $XML_SEPA_INFO .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf;
2790 $XML_SEPA_INFO .= ' <PstlAdr>' . $CrLf;
2791 $XML_SEPA_INFO .= ' <Ctry>' . $country[1] . '</Ctry>' . $CrLf;
2792 $addressline1 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS'), array(chr(13) => ", ", chr(10) => ""));
2793 $addressline2 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP') . ((getDolGlobalString('MAIN_INFO_SOCIETE_ZIP') || ' ' . getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')) ? ' ' : '') . getDolGlobalString('MAIN_INFO_SOCIETE_TOWN'), array(chr(13) => ", ", chr(10) => ""));
2794 if ($addressline1) {
2795 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2796 }
2797 if ($addressline2) {
2798 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2799 }
2800 $XML_SEPA_INFO .= ' </PstlAdr>' . $CrLf;
2801 $XML_SEPA_INFO .= ' </Cdtr>' . $CrLf;
2802 $XML_SEPA_INFO .= ' <CdtrAcct>' . $CrLf;
2803 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2804 $XML_SEPA_INFO .= ' <IBAN>' . preg_replace('/\s/', '', $this->emetteur_iban) . '</IBAN>' . $CrLf;
2805 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2806 $XML_SEPA_INFO .= ' </CdtrAcct>' . $CrLf;
2807 $XML_SEPA_INFO .= ' <CdtrAgt>' . $CrLf;
2808 $XML_SEPA_INFO .= ' <FinInstnId>' . $CrLf;
2809 $XML_SEPA_INFO .= ' <BIC>' . $this->emetteur_bic . '</BIC>' . $CrLf;
2810 $XML_SEPA_INFO .= ' </FinInstnId>' . $CrLf;
2811 $XML_SEPA_INFO .= ' </CdtrAgt>' . $CrLf;
2812 /* $XML_SEPA_INFO .= ' <UltmtCdtr>'.$CrLf;
2813 $XML_SEPA_INFO .= ' <Nm>'.dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))).'</Nm>'.$CrLf;
2814 $XML_SEPA_INFO .= ' <PstlAdr>'.$CrLf;
2815 $XML_SEPA_INFO .= ' <Ctry>'.$country[1].'</Ctry>'.$CrLf;
2816 $XML_SEPA_INFO .= ' <AdrLine>'.dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS')), ' '), 70, 'right', 'UTF-8', 1)).'</AdrLine>'.$CrLf;
2817 $XML_SEPA_INFO .= ' <AdrLine>'.dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP').' '.getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')), ' '), 70, 'right', 'UTF-8', 1)).'</AdrLine>'.$CrLf;
2818 $XML_SEPA_INFO .= ' </PstlAdr>'.$CrLf;
2819 $XML_SEPA_INFO .= ' </UltmtCdtr>'.$CrLf;*/
2820 $XML_SEPA_INFO .= ' <ChrgBr>SLEV</ChrgBr>' . $CrLf; // Field "Responsible of fees". Must be SLEV
2821 $XML_SEPA_INFO .= ' <CdtrSchmeId>' . $CrLf;
2822 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2823 $XML_SEPA_INFO .= ' <PrvtId>' . $CrLf;
2824 $XML_SEPA_INFO .= ' <Othr>' . $CrLf;
2825 $XML_SEPA_INFO .= ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf;
2826 $XML_SEPA_INFO .= ' <SchmeNm>' . $CrLf;
2827 $XML_SEPA_INFO .= ' <Prtry>SEPA</Prtry>' . $CrLf;
2828 $XML_SEPA_INFO .= ' </SchmeNm>' . $CrLf;
2829 $XML_SEPA_INFO .= ' </Othr>' . $CrLf;
2830 $XML_SEPA_INFO .= ' </PrvtId>' . $CrLf;
2831 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2832 $XML_SEPA_INFO .= ' </CdtrSchmeId>' . $CrLf;
2833 } else {
2834 // SEPA Paiement Information of my company for Credit Transfer
2835 $XML_SEPA_INFO = '';
2836 $XML_SEPA_INFO .= ' <PmtInf>' . $CrLf;
2837 $XML_SEPA_INFO .= ' <PmtInfId>' . ('DOL/' . $dateTime_YMD . '/CT' . $IdBon . '-' . $RefBon) . '</PmtInfId>' . $CrLf;
2838 $XML_SEPA_INFO .= ' <PmtMtd>TRF</PmtMtd>' . $CrLf;
2839 //$XML_SEPA_INFO .= ' <BtchBookg>False</BtchBookg>'.$CrLf;
2840 $XML_SEPA_INFO .= ' <NbOfTxs>' . $nombre . '</NbOfTxs>' . $CrLf;
2841 $XML_SEPA_INFO .= ' <CtrlSum>' . $total . '</CtrlSum>' . $CrLf;
2842 if (!empty($this->sepa_xml_pti_in_ctti) && !empty($format)) { // @TODO Using $format (FRST ou RCUR) in a section for a Credit Transfer looks strange.
2843 $XML_SEPA_INFO .= ' <PmtTpInf>' . $CrLf;
2844 $XML_SEPA_INFO .= ' <SvcLvl>' . $CrLf;
2845 $XML_SEPA_INFO .= ' <Cd>SEPA</Cd>' . $CrLf;
2846 $XML_SEPA_INFO .= ' </SvcLvl>' . $CrLf;
2847 $XML_SEPA_INFO .= ' <LclInstrm>' . $CrLf;
2848 $XML_SEPA_INFO .= ' <Cd>' . $localInstrument . '</Cd>' . $CrLf;
2849 $XML_SEPA_INFO .= ' </LclInstrm>' . $CrLf;
2850 $XML_SEPA_INFO .= ' <SeqTp>' . $format . '</SeqTp>' . $CrLf;
2851 $XML_SEPA_INFO .= ' </PmtTpInf>' . $CrLf;
2852 }
2853 $XML_SEPA_INFO .= ' <ReqdExctnDt>' . dol_print_date($dateTime_ETAD, 'dayrfc') . '</ReqdExctnDt>' . $CrLf;
2854 $XML_SEPA_INFO .= ' <Dbtr>' . $CrLf;
2855 $XML_SEPA_INFO .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf;
2856 $XML_SEPA_INFO .= ' <PstlAdr>' . $CrLf;
2857 $XML_SEPA_INFO .= ' <Ctry>' . $country[1] . '</Ctry>' . $CrLf;
2858 $addressline1 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS'), array(chr(13) => ", ", chr(10) => ""));
2859 $addressline2 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP') . ((getDolGlobalString('MAIN_INFO_SOCIETE_ZIP') || ' ' . getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')) ? ' ' : '') . getDolGlobalString('MAIN_INFO_SOCIETE_TOWN'), array(chr(13) => ", ", chr(10) => ""));
2860 if ($addressline1) {
2861 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2862 }
2863 if ($addressline2) {
2864 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2865 }
2866 $XML_SEPA_INFO .= ' </PstlAdr>' . $CrLf;
2867 $XML_SEPA_INFO .= ' </Dbtr>' . $CrLf;
2868 $XML_SEPA_INFO .= ' <DbtrAcct>' . $CrLf;
2869 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2870 $XML_SEPA_INFO .= ' <IBAN>' . preg_replace('/\s/', '', $this->emetteur_iban) . '</IBAN>' . $CrLf;
2871 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2872 $XML_SEPA_INFO .= ' </DbtrAcct>' . $CrLf;
2873 $XML_SEPA_INFO .= ' <DbtrAgt>' . $CrLf;
2874 $XML_SEPA_INFO .= ' <FinInstnId>' . $CrLf;
2875 $XML_SEPA_INFO .= ' <BIC>' . $this->emetteur_bic . '</BIC>' . $CrLf;
2876 $XML_SEPA_INFO .= ' </FinInstnId>' . $CrLf;
2877 $XML_SEPA_INFO .= ' </DbtrAgt>' . $CrLf;
2878 /* $XML_SEPA_INFO .= ' <UltmtCdtr>'.$CrLf;
2879 $XML_SEPA_INFO .= ' <Nm>'.dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))).'</Nm>'.$CrLf;
2880 $XML_SEPA_INFO .= ' <PstlAdr>'.$CrLf;
2881 $XML_SEPA_INFO .= ' <Ctry>'.$country[1].'</Ctry>'.$CrLf;
2882 $XML_SEPA_INFO .= ' <AdrLine>'.dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS')), ' '), 70, 'right', 'UTF-8', 1)).'</AdrLine>'.$CrLf;
2883 $XML_SEPA_INFO .= ' <AdrLine>'.dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP').' '.getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')), ' '), 70, 'right', 'UTF-8', 1)).'</AdrLine>'.$CrLf;
2884 $XML_SEPA_INFO .= ' </PstlAdr>'.$CrLf;
2885 $XML_SEPA_INFO .= ' </UltmtCdtr>'.$CrLf;*/
2886 $XML_SEPA_INFO .= ' <ChrgBr>SLEV</ChrgBr>' . $CrLf; // Field "Responsible of fees". Must be SLEV
2887 /*$XML_SEPA_INFO .= ' <CdtrSchmeId>'.$CrLf;
2888 $XML_SEPA_INFO .= ' <Id>'.$CrLf;
2889 $XML_SEPA_INFO .= ' <PrvtId>'.$CrLf;
2890 $XML_SEPA_INFO .= ' <Othr>'.$CrLf;
2891 $XML_SEPA_INFO .= ' <Id>'.$this->emetteur_ics.'</Id>'.$CrLf;
2892 $XML_SEPA_INFO .= ' <SchmeNm>'.$CrLf;
2893 $XML_SEPA_INFO .= ' <Prtry>SEPA</Prtry>'.$CrLf;
2894 $XML_SEPA_INFO .= ' </SchmeNm>'.$CrLf;
2895 $XML_SEPA_INFO .= ' </Othr>'.$CrLf;
2896 $XML_SEPA_INFO .= ' </PrvtId>'.$CrLf;
2897 $XML_SEPA_INFO .= ' </Id>'.$CrLf;
2898 $XML_SEPA_INFO .= ' </CdtrSchmeId>'.$CrLf;*/
2899 }
2900 } else {
2901 fwrite($this->file, 'INCORRECT EMETTEUR ' . $this->raison_sociale . $CrLf);
2902 $XML_SEPA_INFO = '';
2903 }
2904 return $XML_SEPA_INFO;
2905 }
2906
2907 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2914 public function EnregTotal($total)
2915 {
2916 // phpcs:enable
2917 fwrite($this->file, "08");
2918 fwrite($this->file, "08"); // Prelevement ordinaire
2919
2920 fwrite($this->file, " "); // Zone Reservee B2
2921
2922 fwrite($this->file, $this->emetteur_ics); // ICS
2923
2924 // Reserve C1
2925
2926 fwrite($this->file, substr(" ", 0, 12));
2927
2928
2929 // Raison Sociale C2
2930
2931 fwrite($this->file, substr(" ", 0, 24));
2932
2933 // D1
2934
2935 fwrite($this->file, substr(" ", 0, 24));
2936
2937 // Zone Reservee D2
2938
2939 fwrite($this->file, substr(" ", 0, 8));
2940
2941 // Code Guichet D3
2942
2943 fwrite($this->file, substr(" ", 0, 5));
2944
2945 // Numero de compte D4
2946
2947 fwrite($this->file, substr(" ", 0, 11));
2948
2949 // Zone E Montant
2950
2951 $montant = ($total * 100);
2952
2953 fwrite($this->file, substr("000000000000000" . $montant, -16));
2954
2955 // Zone Reservee F
2956
2957 fwrite($this->file, substr(" ", 0, 31));
2958
2959 // Code etablissement
2960
2961 fwrite($this->file, substr(" ", 0, 5));
2962
2963 // Zone Reservee F
2964
2965 fwrite($this->file, substr(" ", 0, 5));
2966
2967 fwrite($this->file, "\n");
2968 }
2969
2976 public function getLibStatut($mode = 0)
2977 {
2978 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2979 }
2980
2981 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2989 public function LibStatut($status, $mode = 0)
2990 {
2991 // phpcs:enable
2992 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2993 global $langs;
2994 //$langs->load("mymodule");
2995 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusWaiting');
2996 $this->labelStatus[self::STATUS_TRANSFERED] = $langs->transnoentitiesnoconv('StatusTrans');
2997 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusWaiting');
2998 $this->labelStatusShort[self::STATUS_TRANSFERED] = $langs->transnoentitiesnoconv('StatusTrans');
2999 if ($this->type == 'bank-transfer') {
3000 $this->labelStatus[self::STATUS_DEBITED] = $langs->transnoentitiesnoconv('StatusDebited');
3001 $this->labelStatusShort[self::STATUS_DEBITED] = $langs->transnoentitiesnoconv('StatusDebited');
3002 } else {
3003 $this->labelStatus[self::STATUS_CREDITED] = $langs->transnoentitiesnoconv('StatusCredited');
3004 $this->labelStatusShort[self::STATUS_CREDITED] = $langs->transnoentitiesnoconv('StatusCredited');
3005 }
3006 $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
3007 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
3008 }
3009
3010 $statusType = 'status1';
3011 if ($status == self::STATUS_TRANSFERED) {
3012 $statusType = 'status3';
3013 }
3014 if ($status == self::STATUS_CREDITED || $status == self::STATUS_DEBITED) {
3015 $statusType = 'status6';
3016 }
3017 if ($status == self::STATUS_CANCELED) {
3018 $statusType = 'status9';
3019 }
3020
3021 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3022 }
3023
3024 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3032 public function load_board($user, $mode)
3033 {
3034 // phpcs:enable
3035 global $conf, $langs;
3036
3037 if ($user->socid) {
3038 return -1; // Protection to prevent calls by external users
3039 }
3040
3041 if ($mode == 'direct-debit') {
3042 $sql = "SELECT p.rowid, p.date_trans as date_trans, p.date_credit as date_credit";
3043 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as p";
3044 $sql .= " WHERE p.entity IN (" . getEntity('prelevement_bons') . ")";
3045 $sql .= " AND (p.type = 'debit-order' OR p.type = 'direct-debit')"; // direct debit
3046 $sql .= " AND p.statut < ".((int) BonPrelevement::STATUS_DEBITED);
3047 } else {
3048 $sql = "SELECT p.rowid, p.date_trans as date_trans, p.date_credit as date_credit";
3049 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as p";
3050 $sql .= " WHERE p.entity IN (" . getEntity('prelevement_bons') . ")";
3051 $sql .= " AND (p.type = 'bank-transfer' OR p.type = 'credit-transfer')"; // credit transfer
3052 $sql .= " AND p.statut < ".((int) BonPrelevement::STATUS_CREDITED);
3053 }
3054
3055 $resql = $this->db->query($sql);
3056 if ($resql) {
3057 $langs->load("banks");
3058 $now = dol_now();
3059
3060 $response = new WorkboardResponse();
3061 if ($mode == 'direct-debit') {
3062 $response->warning_delay = $conf->warning_delays['bank_direct_debit'] / 60 / 60 / 24;
3063 $response->label = $langs->trans("PendingDirectDebitToComplete");
3064 $response->labelShort = $langs->trans("PendingDirectDebitToCompleteShort");
3065 $response->url = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&search_status=0,1';
3066 $response->url_late = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&search_status=0,1';
3067 } else {
3068 $response->warning_delay = $conf->warning_delays['bank_credit_transfer'] / 60 / 60 / 24;
3069 $response->label = $langs->trans("PendingCreditTransferToComplete");
3070 $response->labelShort = $langs->trans("PendingCreditTransferToCompleteShort");
3071 $response->url = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&type=bank-transfer&search_status=0,1';
3072 $response->url_late = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&type=bank-transfer&search_status=0,1';
3073 }
3074 $response->img = img_object('', "payment");
3075
3076 $response->nbtodo = 0;
3077 $response->nbtodolate = 0;
3078
3079 while ($obj = $this->db->fetch_object($resql)) {
3080 $response->nbtodo++;
3081
3082 if ($this->db->jdate($obj->date_trans) < ($now - $response->warning_delay)) {
3083 $response->nbtodolate++;
3084 }
3085 }
3086
3087 // Return workboard only if quantity is not 0
3088 //if ($response->nbtodo) {
3089 return $response;
3090 //} else {
3091 // return 0;
3092 //}
3093 } else {
3094 dol_print_error($this->db);
3095 $this->error = $this->db->error();
3096 return -1;
3097 }
3098 }
3099
3107 public function getKanbanView($option = '', $arraydata = null)
3108 {
3109 global $langs;
3110
3111 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3112
3113 $return = '<div class="box-flex-item box-flex-grow-zero">';
3114 $return .= '<div class="info-box info-box-sm">';
3115 $return .= '<span class="info-box-icon bg-infobox-action">';
3116 $return .= img_picto('', $this->picto);
3117 $return .= '</span>';
3118 $return .= '<div class="info-box-content">';
3119 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3120 if ($selected >= 0) {
3121 $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3122 }
3123 /*
3124 if (isset($this->date_trans)) {
3125 $return .= '<br><span class="opacitymedium">' . $langs->trans("TransData") . '</span> : <span class="info-box-label">' . dol_print_date($this->db->jdate($this->date_tans), 'day') . '</span>';
3126 }
3127 if (isset($this->date_credit)) {
3128 $return .= '<br><span class="opacitymedium">' . $langs->trans("CreditDate") . '</span> : <span class="info-box-label">' . dol_print_date($this->db->jdate($this->date_credit), 'day') . '</span>';
3129 }
3130 if (isset($this->date_echeance)) {
3131 $return .= '<br><span class="opacitymedium">' . $langs->trans("Date") . '</span> : <span class="info-box-label">' . dol_print_date($this->db->jdate($this->date_echeance), 'day') . '</span>';
3132 }
3133 */
3134 if (isset($this->amount)) {
3135 $return .= '<br><span class="opacitymedium">' . $langs->trans("Amount") . '</span> : <span class="amount">' . price($this->amount) . '</span>';
3136 }
3137 $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3138 $return .= '</div>';
3139 $return .= '</div>';
3140 $return .= '</div>';
3141 return $return;
3142 }
3143
3150 {
3151 if (!empty($this->id)) {
3152 $id = $this->id;
3153 } else {
3154 return 0;
3155 }
3156 $sql = "SELECT COUNT(*) AS nb FROM " . MAIN_DB_PREFIX . "prelevement_lignes";
3157 $sql .= " WHERE fk_prelevement_bons = " . ((int) $id);
3158 $sql .= " AND fk_soc = 0"; // fk_soc can't be NULL
3159 $sql .= " AND fk_user IS NOT NULL";
3160
3161 $num = 0;
3162 $resql = $this->db->query($sql);
3163 if ($resql) {
3164 $obj = $this->db->fetch_object($resql);
3165 $num = $obj->nb;
3166 } else {
3167 dol_print_error($this->db);
3168 }
3169 if ($num > 0) {
3170 return 1;
3171 }
3172
3173 return 0;
3174 }
3175}
checkIbanForAccount($account=null, $ibantocheck=null)
Check IBAN number information for a bank account.
Definition bank.lib.php:390
checkSwiftForAccount($account=null, $swift=null)
Check SWIFT information for a bank account.
Definition bank.lib.php:369
$object ref
Definition info.php:90
Class to manage bank accounts.
Class to manage withdrawal receipts.
addWithdrawDetail($invoice_id, $client_id, $client_nom, $amount, $code_banque, $code_guichet, $number, $number_key, $type='debit-order', $sourcetype='', $bic='', $iban='', $rum='', $id_prelevement_demande=0)
Add invoice to withdrawal.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
SommeAPrelever($mode='direct-debit', $type='')
Returns amount waiting for direct debit payment or credit transfer payment.
static buildRumNumber($row_code_client, $row_datec, $row_drum)
Generate dynamically a RUM number for a customer bank account.
EnregEmetteur($type='direct-debit')
Write sender of request (me).
checkIfSalaryBonPrelevement()
Check if is bon prelevement for salary invoice.
EnregTotal($total)
Write end.
NbFactureAPrelever($type='direct-debit', $forsalary=0)
Get number of invoices to pay.
fetch($rowid, $ref='')
Get object and lines from database.
addline(&$line_id, $client_id, $client_nom, $amount, $code_banque, $code_guichet, $number, $number_key, $sourcetype='', $bic='', $iban='', $rum='', $id_prelevement_demande=0)
Add line to withdrawal.
getListInvoices($amounts=0, $type='')
Get invoice or salary list (with amount or not)
deleteNotificationById($rowid)
Delete a notification def by id.
__construct($db)
Constructor.
EnregDestinataire($rowid, $client_nom, $rib_banque, $rib_guichet, $rib_number, $amount, $ref, $facid, $rib_dom='', $type='direct-debit')
Write recipient of request (customer)
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Returns clickable name (with picto)
LibStatut($status, $mode=0)
Return status label for a status.
update(User $user, $notrigger=0)
Update object into database.
set_infotrans($user, $date, $method)
Set withdrawal to transmitted status.
getErrorString($error)
Return error string.
EnregEmetteurSEPA($configuration, $ladate, $nombre, $total, $CrLf='\n', $format='FRST', $type='direct-debit', $fk_bank_account=0)
Write sender of request (me).
set_infocredit($user, $date, $type='')
Set direct debit or credit transfer order to "paid" status.
getLibStatut($mode=0)
Return status label of object.
EnregDestinataireSEPA($row_code_client, $row_nom, $row_address, $row_zip, $row_town, $row_country_code, $row_cb, $row_cg, $row_cc, $row_somme, $row_ref, $row_idfac, $row_iban, $row_bic, $row_datec, $row_drum, $row_rum, $type='direct-debit', $row_comment='')
Write recipient (thirdparty concerned by request)
addNotification($db, $user, $action)
Add a notification.
nbOfInvoiceToPay($mode='direct-debit', $type='')
Get number of invoices waiting for payment.
generate(string $format='ALL', int $executiondate=0, string $type='direct-debit', int $fk_bank_account=0, int $forsalary=0, int $thirdpartyBANId=0)
Generate a direct debit or credit transfer file.
deleteNotification($user, $action)
Delete a notification.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
updateCommon(User $user, $notrigger=0)
Update object into database.
Class to manage bank accounts description of third parties.
Class to manage suppliers invoices.
Class to manage invoices.
const STATUS_VALIDATED
Validated (need to be paid)
Class to manage hooks.
Class to manage payments for supplier invoices.
Class to manage payments of customer invoices.
Class to manage payments of salaries.
Class to manage salary payments.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_now($mode='gmt')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
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.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
dolBECalculateStructuredCommunication($invoice_number, $invoice_type)
Calculate Structured Communication / BE Bank payment reference number.
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as p p num_paiement as f pf amount as amount
Definition receipt.php:489
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.