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