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