dolibarr 24.0.0-beta
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 .= $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=".$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 $entities = $type != 'bank-transfer' ? getEntity('invoice', 1) : getEntity('supplier_invoice', 1); // Return a list of entities
1132 $sqlTable = $type != 'bank-transfer' ? "facture" : "facture_fourn";
1133 $socOrUser = 'fk_soc';
1134 $societeOrUser = 'societe';
1135 } else {
1136 $entities = getEntity('salary', 1); // Return a list of entities
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 (".$this->db->escape($entities).')';
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 if ($verif || (empty($fac[10]) && getDolGlobalInt("WITHDRAWAL_WITHOUT_BIC"))) {
1323 dol_syslog(__METHOD__." now call checkIbanForAccount(null, ".$baninloop.")");
1324 $verif = checkIbanForAccount(null, $baninloop);
1325 }
1326
1327 if ($verif) {
1328 $factures_prev[$i] = $fac;
1329 /* second array necessary for BonPrelevement */
1330 $factures_prev_id[$i] = $fac[0];
1331 $i++;
1332 //dol_syslog(__METHOD__."::RIB is ok", LOG_DEBUG);
1333 } else {
1334 if ($type != 'bank-transfer') {
1335 $tmpsoc->id = (int) $fac[2];
1336 $tmpsoc->name = $fac[8];
1337 $invoice_url = "<a href='" . DOL_URL_ROOT . '/compta/facture/card.php?facid=' . $fac[0] . "'>" . $fac[9] . "</a>";
1338 $this->invoice_in_error[$fac[0]] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1339 $this->thirdparty_in_error[$tmpsoc->id] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1340 $error++;
1341 }
1342 if ($type == 'bank-transfer' && $sourcetype != 'salary') {
1343 $tmpsoc->id = (int) $fac[2];
1344 $tmpsoc->name = $fac[8];
1345 $invoice_url = "<a href='" . DOL_URL_ROOT . '/fourn/facture/card.php?facid=' . $fac[0] . "'>" . $fac[9] . "</a>";
1346 $this->invoice_in_error[$fac[0]] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1347 $this->thirdparty_in_error[$tmpsoc->id] = "Error on default bank number IBAN/BIC for invoice " . $invoice_url . " for thirdparty " . $tmpsoc->getNomUrl(0);
1348 $error++;
1349 }
1350 if ($type == 'bank-transfer' && $sourcetype == 'salary') {
1351 $tmpuser->id = (int) $fac[2];
1352 $tmpuser->firstname = $fac[8];
1353 $salary_url = "<a href='" . DOL_URL_ROOT . '/salaries/card.php?id=' . $fac[0] . "'>" . $fac[0] . "</a>";
1354 $this->invoice_in_error[$fac[0]] = "Error on default bank number IBAN/BIC for salary " . $salary_url . " for employee " . $tmpuser->getNomUrl(0);
1355 $this->thirdparty_in_error[$tmpuser->id] = "Error on default bank number IBAN/BIC for salary " . $salary_url . " for employee " . $tmpuser->getNomUrl(0);
1356 $error++;
1357 }
1358 dol_syslog(__METHOD__ . " Check BAN Error on default bank number IBAN/BIC reported by verif(): " . implode(', ', $fac), LOG_WARNING);
1359 }
1360 } else {
1361 dol_syslog(__METHOD__ . " Check BAN Failed to read company", LOG_WARNING);
1362 }
1363 /*
1364 } else {
1365 dol_syslog(__METHOD__." Check BAN Failed to read invoice", LOG_WARNING);
1366 }
1367 */
1368 }
1369 } else {
1370 dol_syslog(__METHOD__ . " Check BAN No invoice to process", LOG_WARNING);
1371 }
1372 }
1373
1374 $ok = 0;
1375
1376 // Withdraw invoices in factures_prev array
1377 $out = count($factures_prev) . " invoices or salaries will be included.";
1378 dol_syslog($out);
1379
1380 //var_dump($factures_prev, $this->invoice_in_error, $this->thirdparty_in_error);exit;
1381
1382 if (count($factures_prev) > 0) {
1383 if ($mode == 'real') {
1384 $ok = 1;
1385 } else {
1386 print $langs->trans("ModeWarning"); // "Option for real mode was not set, we stop after this simulation\n";
1387 }
1388 }
1389 if ($ok) {
1390 /*
1391 * We are in real mode.
1392 * We create order and build file into disk
1393 */
1394 $this->db->begin();
1395
1396 $now = dol_now();
1397 $ref = '';
1398
1399 /*
1400 * Process order generation
1401 */
1402 if (!$error) {
1403 $ref = substr($year, -2) . $month;
1404
1405 $prefixt = "T";
1406
1407 // Get next free number for the ref of bon prelevement
1408 $sql = "SELECT SUBSTRING(ref, 6) AS refnumber"; // To suppress "TYYMM" from "TYYMMXXX"
1409 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons";
1410 $sql .= " WHERE ref LIKE '" . $this->db->escape($prefixt . $ref) . "%'";
1411 $sql .= " AND entity = " . ((int) $conf->entity);
1412 $sql .= " ORDER BY LENGTH(ref) DESC, ref DESC";
1413 $sql .= " LIMIT 1";
1414
1415 dol_syslog(get_class($this) . " get next free number", LOG_DEBUG);
1416
1417 $resql = $this->db->query($sql);
1418
1419 if ($resql) {
1420 $row = $this->db->fetch_row($resql);
1421
1422 // Build the new ref
1423 $ref = $prefixt . $ref . sprintf("%03d", (intval($row[0]) + 1));
1424
1425 // $conf->abc->dir_output may be:
1426 // /home/ldestailleur/git/dolibarr_15.0/documents/abc/
1427 // or
1428 // /home/ldestailleur/git/dolibarr_15.0/documents/X/abc with X >= 2 with multicompany.
1429 if ($type != 'bank-transfer') {
1430 $dir = $conf->prelevement->dir_output . '/receipts';
1431 } else {
1432 $dir = $conf->paymentbybanktransfer->dir_output . '/receipts';
1433 }
1434 if (!is_dir($dir)) {
1435 dol_mkdir($dir);
1436 }
1437
1438 if (isModEnabled('multicompany')) {
1439 $labelentity = $conf->entity;
1440 $this->filename = $dir . '/' . $ref . '-' . $labelentity . '.xml';
1441 } else {
1442 $this->filename = $dir . '/' . $ref . '.xml';
1443 }
1444
1445 // Create withdraw order in database
1446 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_bons (";
1447 $sql .= "ref, entity, datec, type, fk_bank_account";
1448 $sql .= ") VALUES (";
1449 $sql .= "'" . $this->db->escape($ref) . "'";
1450 $sql .= ", " . ((int) $conf->entity);
1451 $sql .= ", '" . $this->db->idate($now) . "'";
1452 $sql .= ", '" . ($type == 'bank-transfer' ? 'bank-transfer' : 'debit-order') . "'";
1453 $sql .= ", " . ((int) $fk_bank_account);
1454 $sql .= ")";
1455
1456 $resql = $this->db->query($sql);
1457
1458 if ($resql) {
1459 $prev_id = $this->db->last_insert_id(MAIN_DB_PREFIX . "prelevement_bons");
1460 $this->id = $prev_id;
1461 $this->ref = $ref;
1462 } else {
1463 $error++;
1464 $this->errors[] = $this->db->lasterror();
1465 dol_syslog(__METHOD__ . " Create withdraw receipt " . $this->db->lasterror(), LOG_ERR);
1466 }
1467 } else {
1468 $error++;
1469 $this->errors[] = $this->db->lasterror();
1470 dol_syslog(__METHOD__ . " Get last withdraw receipt " . $this->db->lasterror(), LOG_ERR);
1471 }
1472 }
1473
1474 if (!$error) {
1475 dol_syslog(__METHOD__ . " Now loop on each document to insert them in llx_prelevement_demande");
1476
1477 // Add lines for the direct debit or crdit transfer
1478 if (count($factures_prev) > 0) {
1479 foreach ($factures_prev as $fac) { // Add a link in database for each invoice or salary
1480 /*
1481 * Add standing order. This add record into llx_prelevement_lignes and llx_prelevement
1482 * The llx_prelevement_ligne will then be read to build the XML SEPA file.
1483 *
1484 * $fac[0] : invoice or salary id
1485 * $fac[1] : id of payment request (rowid in llx_prelevement_demande)
1486 * $fac[2] : third party id
1487 * $fac[3] : banque
1488 * $fac[4] : guichet
1489 * $fac[5] : number
1490 * $fac[6] : cle rib
1491 * $fac[7] : amount
1492 * $fac[8] : client nom
1493 * $fac[9] : Invoice ref
1494 * $fac[10] : BIC
1495 * $fac[11] : IBAN
1496 * $fac[12] : frstrcur
1497 * $fac[13] : default_rib (0 or 1)
1498 * $fac[14] : rum
1499 * $fac[15] : soc_rib_id (id bank account preselected for direct debit or credit transfer)
1500 */
1501 $ri = $this->addWithdrawDetail(
1502 $fac[0],
1503 $fac[2],
1504 $fac[8],
1505 $fac[7],
1506 $fac[3],
1507 $fac[4],
1508 $fac[5],
1509 $fac[6],
1510 $type,
1511 $sourcetype,
1512 $fac[10],
1513 $fac[11],
1514 $fac[14],
1515 $fac[1]
1516 );
1517
1518 if ($ri != 0) {
1519 $error++;
1520 }
1521
1522 // Update invoice requests as done
1523 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_demande";
1524 $sql .= " SET traite = 1";
1525 $sql .= ", date_traite = '" . $this->db->idate($now) . "'";
1526 $sql .= ", fk_prelevement_bons = " . ((int) $this->id);
1527 $sql .= " WHERE rowid = " . ((int) $fac[1]);
1528
1529 $resql = $this->db->query($sql);
1530 if (!$resql) {
1531 $error++;
1532 $this->errors[] = $this->db->lasterror();
1533 dol_syslog(__METHOD__ . " Update Error=" . $this->db->lasterror(), LOG_ERR);
1534 }
1535 }
1536 }
1537 }
1538
1539 if (!$error) {
1540 /*
1541 * Create file of type='direct-debit' for direct debit order or type='bank-transfer' for credit transfer into a XML file
1542 */
1543
1544 dol_syslog(__METHOD__ . " Init direct debit or credit transfer file for " . count($factures_prev) . " invoices", LOG_DEBUG);
1545
1546 if (count($factures_prev) > 0) {
1547 $this->date_echeance = $datetimeprev;
1548 $this->reference_remise = $ref;
1549
1550 $account = new Account($this->db);
1551 if ($account->fetch($fk_bank_account) > 0) {
1552 $this->emetteur_code_banque = $account->code_banque;
1553 $this->emetteur_code_guichet = $account->code_guichet;
1554 $this->emetteur_numero_compte = $account->number;
1555 $this->emetteur_number_key = $account->cle_rib;
1556 $this->sepa_xml_pti_in_ctti = (bool) $account->pti_in_ctti;
1557 $this->emetteur_iban = $account->iban;
1558 $this->emetteur_bic = $account->bic;
1559
1560 $this->emetteur_ics = (($type == 'bank-transfer' && getDolGlobalString("SEPA_USE_IDS")) ? $account->ics_transfer : $account->ics); // Example "FR78ZZZ123456"
1561
1562 $this->raison_sociale = $account->owner_name;
1563 }
1564 $this->factures = $factures_prev_id;
1565 $this->context['factures_prev'] = $factures_prev;
1566 // Generation of direct debit or credit transfer file $this->filename (May be a SEPA file for european countries)
1567 // This also set the property $this->total with amount that is included into file
1568 $userid = 0;
1569 if ($sourcetype == 'salary') {
1570 $userid = $this->context['factures_prev'][0][2];
1571 }
1572 $result = $this->generate($format, $executiondate, $type, $fk_bank_account, $userid, 0);
1573 if ($result < 0) {
1574 //var_dump($this->error);
1575 //var_dump($this->invoice_in_error);
1576 $error++;
1577 }
1578 }
1579 dol_syslog(__METHOD__ . " Bank order file has been generated under filename " . $this->filename, LOG_DEBUG);
1580 }
1581
1582
1583 /*
1584 * Update total defined after generation of file
1585 */
1586 if (!$error) {
1587 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_bons";
1588 $sql .= " SET amount = " . price2num($this->total);
1589 $sql .= " WHERE rowid = " . ((int) $this->id);
1590 $sql .= " AND entity = " . ((int) $conf->entity);
1591 $resql = $this->db->query($sql);
1592
1593 if (!$resql) {
1594 $error++;
1595 dol_syslog(__METHOD__ . " Error update total: " . $this->db->error(), LOG_ERR);
1596 }
1597 }
1598
1599 if (!$error && !$notrigger) {
1600 $triggerName = 'DIRECT_DEBIT_ORDER_CREATE';
1601 if ($type != 'bank-transfer') {
1602 $triggerName = 'CREDIT_TRANSFER_ORDER_CREATE';
1603 }
1604
1605 // Call trigger
1606 $result = $this->call_trigger($triggerName, $user);
1607 if ($result < 0) {
1608 $error++;
1609 }
1610 // End call triggers
1611 }
1612
1613 if (!$error) {
1614 $this->db->commit();
1615 return count($factures_prev); // The error of failed lines are into $this->invoice_in_error and $this->thirdparty_in_error
1616 } else {
1617 $this->db->rollback();
1618 return -1;
1619 }
1620 } else {
1621 return 0;
1622 }
1623 }
1624
1625
1633 public function delete($user, $notrigger = 0)
1634 {
1635 $this->db->begin();
1636
1637 $error = 0;
1638 $resql1 = $resql2 = $resql3 = $resql4 = 0;
1639
1640 if (!$notrigger) {
1641 $triggername = 'DIRECT_DEBIT_ORDER_DELETE';
1642 if ($this->type == 'bank-transfer') {
1643 $triggername = 'PAYMENTBYBANKTRANFER_DELETE';
1644 }
1645 // Call trigger
1646 $result = $this->call_trigger($triggername, $user);
1647 if ($result < 0) {
1648 $error++;
1649 }
1650 // End call triggers
1651 }
1652
1653 if (!$error) {
1654 $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) . ")";
1655 $resql1 = $this->db->query($sql);
1656 if (!$resql1) {
1657 dol_print_error($this->db);
1658 }
1659 }
1660
1661 if (!$error) {
1662 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prelevement_lignes WHERE fk_prelevement_bons = " . ((int) $this->id);
1663 $resql2 = $this->db->query($sql);
1664 if (!$resql2) {
1665 dol_print_error($this->db);
1666 }
1667 }
1668
1669 if (!$error) {
1670 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prelevement_bons WHERE rowid = " . ((int) $this->id);
1671 $resql3 = $this->db->query($sql);
1672 if (!$resql3) {
1673 dol_print_error($this->db);
1674 }
1675 }
1676
1677 if (!$error) {
1678 $sql = "UPDATE " . MAIN_DB_PREFIX . "prelevement_demande SET fk_prelevement_bons = NULL, traite = 0 WHERE fk_prelevement_bons = " . ((int) $this->id);
1679 $resql4 = $this->db->query($sql);
1680 if (!$resql4) {
1681 dol_print_error($this->db);
1682 }
1683 }
1684
1685 if ($resql1 && $resql2 && $resql3 && $resql4 && !$error) {
1686 $this->db->commit();
1687 return 1;
1688 } else {
1689 $this->db->rollback();
1690 return -1;
1691 }
1692 }
1693
1694
1705 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1706 {
1707 global $conf, $langs, $hookmanager;
1708
1709 if (!empty($conf->dol_no_mouse_hover)) {
1710 $notooltip = 1; // Force disable tooltips
1711 }
1712
1713 $result = '';
1714
1715 $labeltoshow = 'PaymentByDirectDebit';
1716 $labelforclosedate = 'CreditDate';
1717 if (!empty($this->type) && $this->type == 'bank-transfer') {
1718 $labeltoshow = 'PaymentByBankTransfer';
1719 $labelforclosedate = 'ClosedOn';
1720 }
1721
1722 $label = img_picto('', $this->picto) . ' <u>' . $langs->trans($labeltoshow) . '</u> ' . $this->getLibStatut(5);
1723 $label .= '<br>';
1724 $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1725 if (isset($this->amount)) {
1726 $label .= '<br><b>' . $langs->trans("Amount") . ':</b> <span class="amount">' . price($this->amount).'</span>';
1727 }
1728 if (isset($this->date_creation)) {
1729 $label .= '<br><b>'.$langs->trans("DateCreation").":</b> ".dol_print_date($this->date_creation, 'dayhour', 'tzuserrel');
1730 }
1731 if (isset($this->date_trans)) {
1732 $label .= '<br><b>' . $langs->trans("TransData") . ":</b> " . dol_print_date($this->date_trans, 'dayhour', 'tzuserrel');
1733 }
1734 if (isset($this->date_credit)) {
1735 $label .= '<br><b>'.$langs->trans($labelforclosedate).":</b> ".dol_print_date($this->date_credit, 'dayhour', 'tzuserrel');
1736 }
1737
1738 $url = DOL_URL_ROOT . '/compta/prelevement/card.php?id=' . $this->id;
1739 if (!empty($this->type) && $this->type == 'bank-transfer') {
1740 $url = DOL_URL_ROOT . '/compta/prelevement/card.php?id=' . $this->id;
1741 }
1742
1743 if ($option != 'nolink') {
1744 // Add param to save lastsearch_values or not
1745 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1746 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1747 $add_save_lastsearch_values = 1;
1748 }
1749 if ($add_save_lastsearch_values) {
1750 $url .= '&save_lastsearch_values=1';
1751 }
1752 }
1753
1754 $linkclose = '';
1755 if (empty($notooltip)) {
1756 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1757 $label = $langs->trans("ShowMyObject");
1758 $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1759 }
1760 $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1761 $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
1762 } else {
1763 $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1764 }
1765
1766 $linkstart = '<a href="' . $url . '"';
1767 $linkstart .= $linkclose . '>';
1768 $linkend = '</a>';
1769
1770 $result .= $linkstart;
1771 if ($withpicto) {
1772 $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);
1773 }
1774 if ($withpicto != 2) {
1775 $result .= $this->ref;
1776 }
1777 $result .= $linkend;
1778
1779 global $action, $hookmanager;
1780 $hookmanager->initHooks(array('banktransferdao'));
1781 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1782 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1783 if ($reshook > 0) {
1784 $result = $hookmanager->resPrint;
1785 } else {
1786 $result .= $hookmanager->resPrint;
1787 }
1788
1789 return $result;
1790 }
1791
1792
1799 public function deleteNotificationById($rowid)
1800 {
1801 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "notify_def";
1802 $sql .= " WHERE rowid = " . ((int) $rowid);
1803
1804 if ($this->db->query($sql)) {
1805 return 0;
1806 } else {
1807 return -1;
1808 }
1809 }
1810
1818 public function deleteNotification($user, $action)
1819 {
1820 if (is_object($user)) {
1821 $userid = $user->id;
1822 } else { // If user is an id
1823 $userid = $user;
1824 }
1825
1826 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "notify_def";
1827 $sql .= " WHERE fk_user=" . ((int) $userid) . " AND fk_action='" . $this->db->escape($action) . "'";
1828
1829 if ($this->db->query($sql)) {
1830 return 0;
1831 } else {
1832 return -1;
1833 }
1834 }
1835
1836 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1845 public function addNotification($db, $user, $action)
1846 {
1847 // phpcs:enable
1848 $result = 0;
1849
1850 if (is_object($user)) {
1851 $userid = $user->id;
1852 } else { // If user is an id
1853 $userid = $user;
1854 }
1855
1856 if ($this->deleteNotification($user, $action) == 0) {
1857 $now = dol_now();
1858
1859 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "notify_def (datec,fk_user, fk_soc, fk_contact, fk_action)";
1860 $sql .= " VALUES ('" . $this->db->idate($now) . "', " . ((int) $userid) . ", 'NULL', 'NULL', '" . $this->db->escape($action) . "')";
1861
1862 dol_syslog("adnotiff: " . $sql);
1863 if ($this->db->query($sql)) {
1864 $result = 0;
1865 } else {
1866 $result = -1;
1867 dol_syslog(get_class($this) . "::addNotification Error $result");
1868 }
1869 }
1870
1871 return $result;
1872 }
1873
1874
1890 public function generate(string $format = 'ALL', int $executiondate = 0, string $type = 'direct-debit', int $fk_bank_account = 0, int $forsalary = 0, int $thirdpartyBANId = 0)
1891 {
1892 global $conf, $langs, $mysoc;
1893
1894 //TODO: Optimize code to read lines in a single function
1895
1896 // Clean params
1897 if (empty($fk_bank_account)) {
1898 $fk_bank_account = ($type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
1899 }
1900
1901 $result = 0;
1902
1903 dol_syslog(get_class($this) . "::generate build file=" . $this->filename . " type=" . $type);
1904
1905 $this->file = fopen($this->filename, "w");
1906 if ($this->file == false) {
1907 $this->error = $langs->trans('ErrorFailedToOpenFile', $this->filename);
1908 return -1;
1909 }
1910
1911 $found = 0;
1912 $this->total = 0;
1913
1914 // Build file for European countries
1915 if ($mysoc->isInSEPA()) {
1916 $found++;
1917
1918 if ($type != 'bank-transfer') {
1922 // SEPA Initialisation
1923 $CrLf = "\n";
1924
1925 $now = dol_now();
1926
1927 $dateTime_ECMA = dol_print_date($now, '%Y-%m-%dT%H:%M:%S');
1928
1929 $date_actu = $now;
1930 if (!empty($executiondate)) {
1931 $date_actu = $executiondate;
1932 }
1933
1934 $dateTime_YMD = dol_print_date($date_actu, '%Y%m%d');
1935 $dateTime_YMDHMS = dol_print_date($date_actu, '%Y%m%d%H%M%S');
1936 $fileDebiteurSection = '';
1937 $fileEmetteurSection = '';
1938 $i = 0;
1939
1940 /*
1941 * Section Debitor (sepa Debiteurs block lines)
1942 */
1943
1944 $sql = "SELECT soc.rowid as socid, soc.code_client as code, soc.address, soc.zip, soc.town, c.code as country_code,";
1945 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
1946 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
1947 $sql .= " f.ref as reffac,";
1948 $sql .= " p.fk_facture as idfac, p.rowid as pid,";
1949 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, rib.rum, rib.date_rum";
1950 $sql .= " FROM";
1951 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
1952 $sql .= " " . MAIN_DB_PREFIX . "facture as f,";
1953 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
1954 $sql .= " " . MAIN_DB_PREFIX . "societe as soc,";
1955 $sql .= " " . MAIN_DB_PREFIX . "c_country as c,";
1956 $sql .= " " . MAIN_DB_PREFIX . "societe_rib as rib";
1957 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
1958 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
1959 $sql .= " AND p.fk_facture = f.rowid";
1960 $sql .= " AND f.fk_soc = soc.rowid";
1961 $sql .= " AND soc.fk_pays = c.rowid";
1962 $sql .= " AND rib.fk_soc = f.fk_soc";
1963 if (!empty($thirdpartyBANId)) {
1964 $sql .= " AND rib.rowid = " . ((int) $thirdpartyBANId);
1965 } else {
1966 $sql .= " AND rib.default_rib = 1";
1967 }
1968 $sql .= " AND rib.type = 'ban'";
1969
1970 // Define $fileDebiteurSection. One section DrctDbtTxInf per invoice.
1971 $resql = $this->db->query($sql);
1972 $nbtotalDrctDbtTxInf = -1;
1973 if ($resql) {
1974 $cachearraytotestduplicate = array();
1975
1976 $num = $this->db->num_rows($resql);
1977 while ($i < $num) {
1978 $obj = $this->db->fetch_object($resql);
1979
1980 if (!empty($cachearraytotestduplicate[$obj->idfac])) {
1981 $this->error = $langs->trans('ErrorCompanyHasDuplicateDefaultBAN', $obj->socid);
1982 $this->invoice_in_error[$obj->idfac] = $this->error;
1983 $result = -2;
1984 break;
1985 }
1986 $cachearraytotestduplicate[$obj->idfac] = $obj->rowid;
1987
1988 $daterum = (!empty($obj->date_rum)) ? $this->db->jdate($obj->date_rum) : $this->db->jdate($obj->datec);
1989 $iban = dolDecrypt($obj->iban);
1990
1991 $fileDebiteurSection .= $this->EnregDestinataireSEPA($obj->code, $obj->nom, $obj->address, $obj->zip, $obj->town, $obj->country_code, '', '', '', $obj->somme, $obj->reffac, $obj->idfac, $iban, $obj->bic, $daterum, (string) $obj->drum, $obj->rum, $type);
1992
1993 $this->total += $obj->somme;
1994 $i++;
1995 }
1996 $nbtotalDrctDbtTxInf = $i;
1997 } else {
1998 $this->error = $this->db->lasterror();
1999 fwrite($this->file, 'ERROR DEBITOR ' . $sql . $CrLf); // DEBITOR = Customers
2000 $result = -2;
2001 }
2002
2003 // Define $fileEmetteurSection. Start of block PmtInf. Will contains all $nbtotalDrctDbtTxInf
2004 if ($result != -2) {
2005 $fileEmetteurSection .= $this->EnregEmetteurSEPA($conf, $date_actu, $nbtotalDrctDbtTxInf, $this->total, $CrLf, $format, $type, $fk_bank_account);
2006 }
2007
2008 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2009 $this->total = number_format((float) price2num($this->total, 'MT'), 2, ".", "");
2010 }
2011
2015 // SEPA File Header
2016 fwrite($this->file, '<' . '?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>' . $CrLf);
2017 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);
2018 fwrite($this->file, ' <CstmrDrctDbtInitn>' . $CrLf);
2019 // SEPA Group header
2020 fwrite($this->file, ' <GrpHdr>' . $CrLf);
2021 fwrite($this->file, ' <MsgId>' . ('DOL/' . $dateTime_YMD . '/DD' . $this->id) . '</MsgId>' . $CrLf);
2022 fwrite($this->file, ' <CreDtTm>' . $dateTime_ECMA . '</CreDtTm>' . $CrLf);
2023 fwrite($this->file, ' <NbOfTxs>' . $i . '</NbOfTxs>' . $CrLf);
2024 fwrite($this->file, ' <CtrlSum>' . $this->total . '</CtrlSum>' . $CrLf);
2025 fwrite($this->file, ' <InitgPty>' . $CrLf);
2026 fwrite($this->file, ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf);
2027 fwrite($this->file, ' <Id>' . $CrLf);
2028 fwrite($this->file, ' <PrvtId>' . $CrLf);
2029 fwrite($this->file, ' <Othr>' . $CrLf);
2030 fwrite($this->file, ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf);
2031 fwrite($this->file, ' </Othr>' . $CrLf);
2032 fwrite($this->file, ' </PrvtId>' . $CrLf);
2033 fwrite($this->file, ' </Id>' . $CrLf);
2034 fwrite($this->file, ' </InitgPty>' . $CrLf);
2035 fwrite($this->file, ' </GrpHdr>' . $CrLf);
2036 // SEPA File Emetteur
2037 if ($result != -2) {
2038 fwrite($this->file, $fileEmetteurSection);
2039 }
2040 // SEPA File Debiteurs
2041 if ($result != -2) {
2042 fwrite($this->file, $fileDebiteurSection);
2043 }
2044 // SEPA FILE FOOTER
2045 fwrite($this->file, ' </PmtInf>' . $CrLf);
2046 fwrite($this->file, ' </CstmrDrctDbtInitn>' . $CrLf);
2047 fwrite($this->file, '</Document>' . $CrLf);
2048 } else {
2052 // SEPA Initialisation
2053 $CrLf = "\n";
2054
2055 $now = dol_now();
2056
2057 $dateTime_ECMA = dol_print_date($now, '%Y-%m-%dT%H:%M:%S');
2058
2059 $date_actu = $now;
2060 if (!empty($executiondate)) {
2061 $date_actu = $executiondate;
2062 }
2063
2064 $dateTime_YMD = dol_print_date($date_actu, '%Y%m%d');
2065 $dateTime_YMDHMS = dol_print_date($date_actu, '%Y%m%d%H%M%S');
2066 $fileCrediteurSection = '';
2067 $fileEmetteurSection = '';
2068 $i = 0;
2069
2070 /*
2071 * Section Creditor (sepa Crediteurs block lines)
2072 */
2073 if (!empty($forsalary)) {
2074 $sql = "SELECT u.rowid as userId, u.address, u.zip, u.town, c.code as country_code, CONCAT(u.firstname,' ',u.lastname) as nom,";
2075 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
2076 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
2077 $sql .= " s.ref as reffac,";
2078 $sql .= " p.fk_salary as idfac,";
2079 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, '' as rum, '' as date_rum";
2080 $sql .= " FROM";
2081 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2082 $sql .= " " . MAIN_DB_PREFIX . "salary as s,";
2083 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
2084 $sql .= " " . MAIN_DB_PREFIX . "user as u";
2085 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON u.fk_country = c.rowid,";
2086 $sql .= " " . MAIN_DB_PREFIX . "user_rib as rib";
2087 $sql .= " WHERE pl.fk_prelevement_bons=" . ((int) $this->id);
2088 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2089 $sql .= " AND p.fk_salary = s.rowid";
2090 $sql .= " AND s.fk_user = u.rowid";
2091 $sql .= " AND rib.fk_user = s.fk_user";
2092 } else {
2093 $sql = "SELECT soc.rowid as socid, soc.code_client as code, soc.address, soc.zip, soc.town, c.code as country_code,";
2094 //$sql .= " pl.code_banque as cb, pl.code_guichet as cg, pl.number as cc, ";
2095 $sql .= " pl.client_nom as nom, pl.amount as somme, pl.fk_prelevement_demande,";
2096 $sql .= " f.ref as reffac, f.ref_supplier as fac_ref_supplier,";
2097 $sql .= " p.fk_facture_fourn as idfac,";
2098 $sql .= " rib.rowid, rib.datec, rib.iban_prefix as iban, rib.bic as bic, rib.rowid as drum, rib.rum, rib.date_rum";
2099 $sql .= " FROM";
2100 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2101 $sql .= " " . MAIN_DB_PREFIX . "facture_fourn as f,";
2102 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p,";
2103 $sql .= " " . MAIN_DB_PREFIX . "societe as soc";
2104 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON soc.fk_pays = c.rowid,";
2105 $sql .= " " . MAIN_DB_PREFIX . "societe_rib as rib";
2106 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2107 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2108 $sql .= " AND p.fk_facture_fourn = f.rowid";
2109 $sql .= " AND f.fk_soc = soc.rowid";
2110 $sql .= " AND rib.fk_soc = f.fk_soc";
2111 if (!empty($thirdpartyBANId)) {
2112 $sql .= " AND rib.rowid = " . ((int) $thirdpartyBANId);
2113 } else {
2114 $sql .= " AND rib.default_rib = 1";
2115 }
2116 $sql .= " AND rib.type = 'ban'";
2117 }
2118 // Define $fileCrediteurSection. One section DrctDbtTxInf per invoice.
2119 $nbtotalDrctDbtTxInf = -1;
2120
2121 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
2122
2123 $resql = $this->db->query($sql);
2124 if ($resql) {
2125 $cachearraytotestduplicate = array();
2126
2127 $num = $this->db->num_rows($resql);
2128 while ($i < $num) {
2129 $obj = $this->db->fetch_object($resql);
2130
2131 // Test to avoid duplicate default IBAN, to ask user to clean its data
2132 if (!empty($cachearraytotestduplicate[$obj->idfac])) {
2133 $this->error = $langs->trans('ErrorCompanyHasDuplicateDefaultBAN', $obj->socid);
2134 $this->invoice_in_error[$obj->idfac] = $this->error;
2135 $result = -2;
2136 break;
2137 }
2138
2139 $cachearraytotestduplicate[$obj->idfac] = $obj->rowid;
2140
2141 // Get the default value
2142 $daterum = (!empty($obj->date_rum)) ? $this->db->jdate($obj->date_rum) : $this->db->jdate($obj->datec);
2143 $iban = dolDecrypt($obj->iban);
2144 $bic = $obj->bic;
2145 $drum = $obj->drum;
2146 $rum = $obj->rum;
2147
2148 // But if a force bank account is defined, we use it instead
2149 if (!empty($obj->fk_prelevement_demande)) {
2150 $companybankaccountid = 0;
2151
2152 $sqltmp = "SELECT fk_societe_rib FROM ".MAIN_DB_PREFIX."prelevement_demande";
2153 $sqltmp .= " WHERE rowid = ".((int) $obj->fk_prelevement_demande);
2154
2155 $resqltmp = $this->db->query($sqltmp);
2156
2157 $objtmp = $this->db->fetch_object($resqltmp);
2158 if ($objtmp) {
2159 $companybankaccountid = (int) $objtmp->fk_societe_rib;
2160 }
2161
2162 $bankaccount = new CompanyBankAccount($this->db);
2163 $bankaccount->fetch($companybankaccountid);
2164 if ($bankaccount->id > 0) {
2165 $daterum = $bankaccount->date_rum;
2166 $iban = $bankaccount->iban;
2167 $bic = $bankaccount->bic;
2168 $drum = $bankaccount->id;
2169 $rum = $bankaccount->rum;
2170 }
2171 }
2172
2173 $refobj = $obj->reffac;
2174 if (empty($refobj) && !empty($forsalary)) { // If ref of salary not defined, we force a value
2175 $refobj = "SAL" . $obj->idfac;
2176 }
2177
2178 $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);
2179
2180 $this->total += $obj->somme;
2181 $i++;
2182 }
2183 $nbtotalDrctDbtTxInf = $i;
2184 } else {
2185 $this->error = $this->db->lasterror();
2186 fwrite($this->file, 'ERROR CREDITOR ' . $sql . $CrLf); // CREDITORS = Suppliers
2187 $result = -2;
2188 }
2189 // Define $fileEmetteurSection. Start of block PmtInf. Will contains all $nbtotalDrctDbtTxInf
2190 if ($result != -2) {
2191 $fileEmetteurSection .= $this->EnregEmetteurSEPA($conf, $date_actu, $nbtotalDrctDbtTxInf, $this->total, $CrLf, $format, $type);
2192 }
2193
2194 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2195 $this->total = number_format((float) price2num($this->total, 'MT'), 2, ".", "");
2196 }
2197
2201 // SEPA File Header
2202 fwrite($this->file, '<' . '?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>' . $CrLf);
2203 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);
2204 fwrite($this->file, ' <CstmrCdtTrfInitn>' . $CrLf);
2205 // SEPA Group header
2206 fwrite($this->file, ' <GrpHdr>' . $CrLf);
2207 fwrite($this->file, ' <MsgId>' . ('DOL/' . $dateTime_YMD . '/CT' . $this->id) . '</MsgId>' . $CrLf);
2208 fwrite($this->file, ' <CreDtTm>' . $dateTime_ECMA . '</CreDtTm>' . $CrLf);
2209 fwrite($this->file, ' <NbOfTxs>' . $i . '</NbOfTxs>' . $CrLf);
2210 fwrite($this->file, ' <CtrlSum>' . $this->total . '</CtrlSum>' . $CrLf);
2211 fwrite($this->file, ' <InitgPty>' . $CrLf);
2212 fwrite($this->file, ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf);
2213 fwrite($this->file, ' <Id>' . $CrLf);
2214 fwrite($this->file, ' <PrvtId>' . $CrLf);
2215 fwrite($this->file, ' <Othr>' . $CrLf);
2216 fwrite($this->file, ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf);
2217 fwrite($this->file, ' </Othr>' . $CrLf);
2218 fwrite($this->file, ' </PrvtId>' . $CrLf);
2219 fwrite($this->file, ' </Id>' . $CrLf);
2220 fwrite($this->file, ' </InitgPty>' . $CrLf);
2221 fwrite($this->file, ' </GrpHdr>' . $CrLf);
2222 // SEPA File Emetteur (mycompany)
2223 if ($result != -2) {
2224 fwrite($this->file, $fileEmetteurSection);
2225 }
2226 // SEPA File Creditors
2227 if ($result != -2) {
2228 fwrite($this->file, $fileCrediteurSection);
2229 }
2230 // SEPA FILE FOOTER
2231 fwrite($this->file, ' </PmtInf>' . $CrLf);
2232 fwrite($this->file, ' </CstmrCdtTrfInitn>' . $CrLf);
2233 fwrite($this->file, '</Document>' . $CrLf);
2234 }
2235 }
2236
2237 // Build file for Other Countries with unknown format
2238 if (!$found) {
2239 if ($type != 'bank-transfer') {
2240 $sql = "SELECT pl.amount";
2241 $sql .= " FROM";
2242 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2243 $sql .= " " . MAIN_DB_PREFIX . "facture as f,";
2244 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p";
2245 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2246 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2247 $sql .= " AND p.fk_facture = f.rowid";
2248
2249 // Lines
2250 $i = 0;
2251 $resql = $this->db->query($sql);
2252 if ($resql) {
2253 $num = $this->db->num_rows($resql);
2254
2255 while ($i < $num) {
2256 $obj = $this->db->fetch_object($resql);
2257 $this->total += $obj->amount;
2258
2259 // TODO Write record into file
2260 $i++;
2261 }
2262 } else {
2263 $result = -2;
2264 }
2265 } else {
2266 $sql = "SELECT pl.amount";
2267 $sql .= " FROM";
2268 $sql .= " " . MAIN_DB_PREFIX . "prelevement_lignes as pl,";
2269 $sql .= " " . MAIN_DB_PREFIX . "facture_fourn as f,";
2270 $sql .= " " . MAIN_DB_PREFIX . "prelevement as p";
2271 $sql .= " WHERE pl.fk_prelevement_bons = " . ((int) $this->id);
2272 $sql .= " AND pl.rowid = p.fk_prelevement_lignes";
2273 $sql .= " AND p.fk_facture_fourn = f.rowid";
2274 // Lines
2275 $i = 0;
2276 $resql = $this->db->query($sql);
2277 if ($resql) {
2278 $num = $this->db->num_rows($resql);
2279
2280 while ($i < $num) {
2281 $obj = $this->db->fetch_object($resql);
2282 $this->total += $obj->amount;
2283
2284 // TODO Write record into file
2285 $i++;
2286 }
2287 } else {
2288 $result = -2;
2289 }
2290 }
2291
2292 $langs->load('withdrawals');
2293
2294 // TODO Add here code to generate a generic file
2295 fwrite($this->file, $langs->transnoentitiesnoconv('WithdrawalFileNotCapable', $mysoc->country_code));
2296 }
2297
2298 fclose($this->file);
2299 dolChmod($this->filename);
2300
2301 return $result;
2302 }
2303
2304
2313 public static function buildRumNumber($row_code_client, $row_datec, $row_drum)
2314 {
2315 global $langs;
2316
2317 $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)
2318
2319 // 3 char + '-' + 10 (yymmddHHMM) + '-' + id + '-' + code. Must be under 32 (SEPA char limit for MndtId is however 35).
2320 return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc((string) $row_drum . ($row_code_client ? '-' . $row_code_client : ''), 17, 'right', 'UTF-8', 1);
2321 }
2322
2323
2324 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2341 public function EnregDestinataire($rowid, $client_nom, $rib_banque, $rib_guichet, $rib_number, $amount, $ref, $facid, $rib_dom = '', $type = 'direct-debit')
2342 {
2343 // phpcs:enable
2344 fwrite($this->file, "06");
2345 fwrite($this->file, "08"); // Prelevement ordinaire
2346
2347 fwrite($this->file, " "); // Zone Reservee B2
2348
2349 fwrite($this->file, $this->emetteur_ics); // ICS
2350
2351 // Date d'echeance C1
2352
2353 fwrite($this->file, " ");
2354 fwrite($this->file, dol_print_date($this->date_echeance, "%d%m", 'gmt'));
2355 fwrite($this->file, substr(dol_print_date($this->date_echeance, "%y", 'gmt'), 1));
2356
2357 // Raison Sociale Destinataire C2
2358
2359 fwrite($this->file, substr(strtoupper($client_nom) . " ", 0, 24));
2360
2361 // Address optional D1
2362 $address = strtr($rib_dom, array(" " => "-", chr(13) => " ", chr(10) => ""));
2363 fwrite($this->file, substr($address . " ", 0, 24));
2364
2365 // Zone Reservee D2
2366
2367 fwrite($this->file, substr(" ", 0, 8));
2368
2369 // Code Guichet D3
2370
2371 fwrite($this->file, $rib_guichet);
2372
2373 // Numero de compte D4
2374
2375 fwrite($this->file, substr("000000000000000" . $rib_number, -11));
2376
2377 // Zone E Montant
2378
2379 $montant = (round($amount, 2) * 100);
2380
2381 fwrite($this->file, substr("000000000000000" . $montant, -16));
2382
2383 // Label F
2384
2385 fwrite($this->file, substr("*_" . $ref . "_RDVnet" . $rowid . " ", 0, 31));
2386
2387 // Code etablissement G1
2388
2389 fwrite($this->file, $rib_banque);
2390
2391 // Zone Reservee G2
2392
2393 fwrite($this->file, substr(" ", 0, 5));
2394
2395 fwrite($this->file, "\n");
2396 }
2397
2398
2399 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2425 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 = '')
2426 {
2427 // phpcs:enable
2428 global $conf, $mysoc, $hookmanager;
2429
2430 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2431 $row_somme = number_format((float) price2num($row_somme, 'MT'), 2, ".", "");
2432 } else {
2433 $row_somme = round((float) $row_somme, 2);
2434 }
2435
2436 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
2437
2438 $CrLf = "\n";
2439 $Rowing = sprintf("%010d", $row_idfac);
2440
2441 // Define value for RUM
2442 // Example: RUM-CustomerCode-CustomerBankAccountId-01424448606 (note: Date is the timestamp of the date of creation of CustomerBankAccountId)
2443 $Rum = (empty($row_rum) ? $this->buildRumNumber($row_code_client, $row_datec, $row_drum) : $row_rum);
2444
2445 // Define date of RUM signature
2446 $DtOfSgntr = dol_print_date($row_datec, '%Y-%m-%d');
2447 $XML_RESULT = '';
2448
2449 if (!is_object($hookmanager)) {
2450 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2451 $hookmanager = new HookManager($this->db);
2452 }
2453 $hookmanager->initHooks(array('bonprelevementdao'));
2454 $parameters = array(
2455 'row_code_client' => &$row_code_client, 'row_nom' => &$row_nom, 'row_address' => &$row_address, 'row_zip' => &$row_zip, 'row_town' => &$row_town,
2456 'row_country_code' => &$row_country_code, 'row_cb' => &$row_cb, 'row_cg' => &$row_cg, 'row_cc' => &$row_cc, 'row_somme' => &$row_somme,
2457 'row_ref' => &$row_ref, 'row_idfac' => &$row_idfac, 'row_iban' => &$row_iban, 'row_bic' => &$row_bic, 'row_datec' => &$row_datec,
2458 'row_drum' => &$row_drum, 'row_rum' => &$row_rum, 'type' => &$type, 'row_comment' => &$row_comment,
2459 'crlf' => &$CrLf, 'rowing' => &$Rowing, 'rum' => &$Rum, 'dtofsgntr' => &$DtOfSgntr,
2460 );
2461 $reshook = $hookmanager->executeHooks('enregDestinataireSEPA', $parameters, $this); // Note that $action and $object may have been modified by some hooks
2462 if (empty($reshook)) {
2463 if ($type != 'bank-transfer') {
2464 // SEPA Paiement Information of buyer for Direct Debit
2465 $XML_DEBITOR = '';
2466 $XML_DEBITOR .= ' <DrctDbtTxInf>' . $CrLf;
2467 $XML_DEBITOR .= ' <PmtId>' . $CrLf;
2468 // Add EndToEndId. Must be a unique ID for each payment (for example by including bank, buyer or seller, date, checksum)
2469 $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
2470 $XML_DEBITOR .= ' </PmtId>' . $CrLf;
2471 $XML_DEBITOR .= ' <InstdAmt Ccy="EUR">' . $row_somme . '</InstdAmt>' . $CrLf;
2472 $XML_DEBITOR .= ' <DrctDbtTx>' . $CrLf;
2473 $XML_DEBITOR .= ' <MndtRltdInf>' . $CrLf;
2474 $XML_DEBITOR .= ' <MndtId>' . $Rum . '</MndtId>' . $CrLf;
2475 $XML_DEBITOR .= ' <DtOfSgntr>' . $DtOfSgntr . '</DtOfSgntr>' . $CrLf;
2476 $XML_DEBITOR .= ' <AmdmntInd>false</AmdmntInd>' . $CrLf;
2477 $XML_DEBITOR .= ' </MndtRltdInf>' . $CrLf;
2478 $XML_DEBITOR .= ' </DrctDbtTx>' . $CrLf;
2479 $XML_DEBITOR .= ' <DbtrAgt>' . $CrLf;
2480 $XML_DEBITOR .= ' <FinInstnId>' . $CrLf;
2481 if (getDolGlobalInt('WITHDRAWAL_WITHOUT_BIC') == 0) {
2482 $XML_DEBITOR .= ' <BIC>' . $row_bic . '</BIC>' . $CrLf;
2483 }
2484 $XML_DEBITOR .= ' </FinInstnId>' . $CrLf;
2485 $XML_DEBITOR .= ' </DbtrAgt>' . $CrLf;
2486 $XML_DEBITOR .= ' <Dbtr>' . $CrLf;
2487 $XML_DEBITOR .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($row_nom), ' '))) . '</Nm>' . $CrLf;
2488 $XML_DEBITOR .= ' <PstlAdr>' . $CrLf;
2489 $XML_DEBITOR .= ' <Ctry>' . $row_country_code . '</Ctry>' . $CrLf;
2490 $addressline1 = strtr($row_address, array(chr(13) => ", ", chr(10) => ""));
2491 $addressline2 = strtr($row_zip . (($row_zip && $row_town) ? ' ' : '') . (string) $row_town, array(chr(13) => ", ", chr(10) => ""));
2492 if (trim($addressline1)) {
2493 $XML_DEBITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2494 }
2495 if (trim($addressline2)) {
2496 $XML_DEBITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2497 }
2498 $XML_DEBITOR .= ' </PstlAdr>' . $CrLf;
2499 $XML_DEBITOR .= ' </Dbtr>' . $CrLf;
2500 $XML_DEBITOR .= ' <DbtrAcct>' . $CrLf;
2501 $XML_DEBITOR .= ' <Id>' . $CrLf;
2502 $XML_DEBITOR .= ' <IBAN>' . preg_replace('/\s/', '', $row_iban) . '</IBAN>' . $CrLf;
2503 $XML_DEBITOR .= ' </Id>' . $CrLf;
2504 $XML_DEBITOR .= ' </DbtrAcct>' . $CrLf;
2505 $XML_DEBITOR .= ' <RmtInf>' . $CrLf;
2506
2507 // Structured data for Belgium
2508 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $mysoc->country_code == 'BE') {
2509 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
2510
2511 $invoicestatic = new Facture($this->db);
2512 $invoicestatic->fetch($row_idfac);
2513
2514 $invoicePaymentKey = dolBECalculateStructuredCommunication($invoicestatic->ref, $invoicestatic->type);
2515 $XML_DEBITOR .= ' <strd>' . $invoicePaymentKey . '</strd>' . $CrLf;
2516 } else {
2517 // A string with some information on payment - 140 max
2518 $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
2519 }
2520 $XML_DEBITOR .= ' </RmtInf>' . $CrLf;
2521 $XML_DEBITOR .= ' </DrctDbtTxInf>' . $CrLf;
2522
2523 $XML_RESULT = $XML_DEBITOR;
2524 } else {
2525 // SEPA Payment Information of seller for Credit Transfer
2526 $XML_CREDITOR = '';
2527 $XML_CREDITOR .= ' <CdtTrfTxInf>' . $CrLf;
2528 $XML_CREDITOR .= ' <PmtId>' . $CrLf;
2529 // Add EndToEndId. Must be a unique ID for each payment (for example by including bank, buyer or seller, date, checksum)
2530 $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
2531 $XML_CREDITOR .= ' </PmtId>' . $CrLf;
2532 if (!empty($this->sepa_xml_pti_in_ctti)) {
2533 $XML_CREDITOR .= ' <PmtTpInf>' . $CrLf;
2534
2535 // Can be 'NORM' for normal or 'HIGH' for high priority level
2536 if (getDolGlobalString('PAYMENTBYBANKTRANSFER_FORCE_HIGH_PRIORITY')) {
2537 $instrprty = 'HIGH';
2538 } else {
2539 $instrprty = 'NORM';
2540 }
2541
2542 // Set $categoryPurpose: CORE, TREA, SUPP, ...
2543 $categoryPurpose = getDolGlobalString('PAYMENTBYBANKTRANSFER_CUSTOM_CATEGORY_PURPOSE', 'CORE');
2544
2545 $XML_CREDITOR .= ' <InstrPrty>' . $instrprty . '</InstrPrty>' . $CrLf;
2546 $XML_CREDITOR .= ' <SvcLvl>' . $CrLf;
2547 $XML_CREDITOR .= ' <Cd>SEPA</Cd>' . $CrLf;
2548 $XML_CREDITOR .= ' </SvcLvl>' . $CrLf;
2549 $XML_CREDITOR .= ' <CtgyPurp>' . $CrLf;
2550 $XML_CREDITOR .= ' <Cd>' . $categoryPurpose . '</Cd>' . $CrLf;
2551 $XML_CREDITOR .= ' </CtgyPurp>' . $CrLf;
2552 $XML_CREDITOR .= ' </PmtTpInf>' . $CrLf;
2553 }
2554 $XML_CREDITOR .= ' <Amt>' . $CrLf;
2555 $XML_CREDITOR .= ' <InstdAmt Ccy="EUR">' . round((float) $row_somme, 2) . '</InstdAmt>' . $CrLf;
2556 $XML_CREDITOR .= ' </Amt>' . $CrLf;
2557 /*
2558 $XML_CREDITOR .= ' <DrctDbtTx>'.$CrLf;
2559 $XML_CREDITOR .= ' <MndtRltdInf>'.$CrLf;
2560 $XML_CREDITOR .= ' <MndtId>'.$Rum.'</MndtId>'.$CrLf;
2561 $XML_CREDITOR .= ' <DtOfSgntr>'.$DtOfSgntr.'</DtOfSgntr>'.$CrLf;
2562 $XML_CREDITOR .= ' <AmdmntInd>false</AmdmntInd>'.$CrLf;
2563 $XML_CREDITOR .= ' </MndtRltdInf>'.$CrLf;
2564 $XML_CREDITOR .= ' </DrctDbtTx>'.$CrLf;
2565 */
2566 //$XML_CREDITOR .= ' <ChrgBr>SLEV</ChrgBr>'.$CrLf;
2567 $XML_CREDITOR .= ' <CdtrAgt>' . $CrLf;
2568 $XML_CREDITOR .= ' <FinInstnId>' . $CrLf;
2569 $XML_CREDITOR .= ' <BIC>' . $row_bic . '</BIC>' . $CrLf;
2570 $XML_CREDITOR .= ' </FinInstnId>' . $CrLf;
2571 $XML_CREDITOR .= ' </CdtrAgt>' . $CrLf;
2572 $XML_CREDITOR .= ' <Cdtr>' . $CrLf;
2573 $XML_CREDITOR .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($row_nom), ' '))) . '</Nm>' . $CrLf;
2574 $XML_CREDITOR .= ' <PstlAdr>' . $CrLf;
2575 $XML_CREDITOR .= ' <Ctry>' . $row_country_code . '</Ctry>' . $CrLf;
2576 $addressline1 = strtr($row_address, array(chr(13) => ", ", chr(10) => ""));
2577 $addressline2 = strtr($row_zip . (($row_zip && $row_town) ? ' ' : '') . (string) $row_town, array(chr(13) => ", ", chr(10) => ""));
2578 if (trim($addressline1)) {
2579 $XML_CREDITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2580 }
2581 if (trim($addressline2)) {
2582 $XML_CREDITOR .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2583 }
2584 $XML_CREDITOR .= ' </PstlAdr>' . $CrLf;
2585 $XML_CREDITOR .= ' </Cdtr>' . $CrLf;
2586 $XML_CREDITOR .= ' <CdtrAcct>' . $CrLf;
2587 $XML_CREDITOR .= ' <Id>' . $CrLf;
2588 $XML_CREDITOR .= ' <IBAN>' . preg_replace('/\s/', '', $row_iban) . '</IBAN>' . $CrLf;
2589 $XML_CREDITOR .= ' </Id>' . $CrLf;
2590 $XML_CREDITOR .= ' </CdtrAcct>' . $CrLf;
2591 $XML_CREDITOR .= ' <RmtInf>' . $CrLf;
2592 // A string with some information on payment - 140 max
2593 $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
2594 $XML_CREDITOR .= ' </RmtInf>' . $CrLf;
2595 $XML_CREDITOR .= ' </CdtTrfTxInf>' . $CrLf;
2596
2597 $XML_RESULT = $XML_CREDITOR;
2598 }
2599 } elseif ($reshook > 0) {
2600 $XML_RESULT = $hookmanager->resPrint;
2601 }
2602 $XML_RESULT .= $hookmanager->resPrint;
2603
2604 return $XML_RESULT;
2605 }
2606
2607
2608 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2617 public function EnregEmetteur($type = 'direct-debit')
2618 {
2619 // phpcs:enable
2620 fwrite($this->file, "03");
2621 fwrite($this->file, "08"); // Prelevement ordinaire
2622
2623 fwrite($this->file, " "); // Zone Reservee B2
2624
2625 fwrite($this->file, $this->emetteur_ics); // ICS
2626
2627 // Date d'echeance C1
2628
2629 fwrite($this->file, " ");
2630 fwrite($this->file, dol_print_date($this->date_echeance, "%d%m", 'gmt'));
2631 fwrite($this->file, substr(dol_print_date($this->date_echeance, "%y", 'gmt'), 1));
2632
2633 // Raison Sociale C2
2634
2635 fwrite($this->file, substr($this->raison_sociale . " ", 0, 24));
2636
2637 // Ref of thirdparty on 7 characters
2638
2639 fwrite($this->file, substr($this->reference_remise . " ", 0, 7));
2640
2641 // Zone Reservee D1-2
2642
2643 fwrite($this->file, substr(" ", 0, 17));
2644
2645 // Zone Reservee D2
2646
2647 fwrite($this->file, substr(" ", 0, 2));
2648 fwrite($this->file, "E");
2649 fwrite($this->file, substr(" ", 0, 5));
2650
2651 // Code Guichet D3
2652
2653 fwrite($this->file, $this->emetteur_code_guichet);
2654
2655 // Numero de compte D4
2656
2657 fwrite($this->file, substr("000000000000000" . $this->emetteur_numero_compte, -11));
2658
2659 // Zone Reservee E
2660
2661 fwrite($this->file, substr(" ", 0, 16));
2662
2663 // Zone Reservee F
2664
2665 fwrite($this->file, substr(" ", 0, 31));
2666
2667 // Code etablissement
2668
2669 fwrite($this->file, $this->emetteur_code_banque);
2670
2671 // Zone Reservee G
2672
2673 fwrite($this->file, substr(" ", 0, 5));
2674
2675 fwrite($this->file, "\n");
2676 }
2677
2678
2679 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2694 public function EnregEmetteurSEPA($configuration, $ladate, $nombre, $total, $CrLf = '\n', $format = 'FRST', $type = 'direct-debit', $fk_bank_account = 0)
2695 {
2696 // phpcs:enable
2697
2698 // Clean parameters
2699 $dateTime_YMD = dol_print_date($ladate, '%Y%m%d');
2700 $dateTime_ETAD = dol_print_date($ladate, '%Y-%m-%d');
2701 $dateTime_YMDHMS = dol_print_date($ladate, '%Y-%m-%dT%H:%M:%S');
2702
2703 // Clean params
2704 if (empty($fk_bank_account)) {
2705 $fk_bank_account = ($type == 'bank-transfer' ? getDolGlobalInt('PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT') : getDolGlobalInt('PRELEVEMENT_ID_BANKACCOUNT'));
2706 }
2707
2708 // Get data of bank account
2709 $account = new Account($this->db);
2710 if ($account->fetch($fk_bank_account) > 0) {
2711 $this->emetteur_code_banque = $account->code_banque;
2712 $this->emetteur_code_guichet = $account->code_guichet;
2713 $this->emetteur_numero_compte = $account->number;
2714 $this->emetteur_number_key = $account->cle_rib;
2715 $this->sepa_xml_pti_in_ctti = (bool) $account->pti_in_ctti;
2716 $this->emetteur_iban = $account->iban;
2717 $this->emetteur_bic = $account->bic;
2718
2719 $this->emetteur_ics = (($type == 'bank-transfer' && getDolGlobalString("SEPA_USE_IDS")) ? $account->ics_transfer : $account->ics); // Ex: PRELEVEMENT_ICS = "FR78ZZZ123456";
2720
2721 $this->raison_sociale = $account->owner_name;
2722 }
2723
2724 // Get pending payments
2725 $sql = "SELECT rowid, ref";
2726 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as pb";
2727 $sql .= " WHERE pb.rowid = " . ((int) $this->id);
2728
2729 $resql = $this->db->query($sql);
2730 if ($resql) {
2731 $obj = $this->db->fetch_object($resql);
2732
2733 $country = explode(':', getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY'));
2734 $IdBon = sprintf("%05d", $obj->rowid);
2735 $RefBon = $obj->ref;
2736 $localInstrument = getDolGlobalString('PAYMENTBYBANKTRANSFER_CUSTOM_LOCAL_INSTRUMENT', 'CORE');
2737
2738 if (getDolGlobalString('SEPA_FORCE_TWO_DECIMAL')) {
2739 $total = number_format((float) price2num($total, 'MT'), 2, ".", "");
2740 }
2741
2742 if ($type != 'bank-transfer') {
2743 // SEPA Paiement Information of my company for Direct Debit
2744 $XML_SEPA_INFO = '';
2745 $XML_SEPA_INFO .= ' <PmtInf>' . $CrLf;
2746 $XML_SEPA_INFO .= ' <PmtInfId>' . ('DOL/' . $dateTime_YMD . '/DD' . $IdBon . '-' . $RefBon) . '</PmtInfId>' . $CrLf;
2747 $XML_SEPA_INFO .= ' <PmtMtd>DD</PmtMtd>' . $CrLf;
2748 $XML_SEPA_INFO .= ' <NbOfTxs>' . $nombre . '</NbOfTxs>' . $CrLf;
2749 $XML_SEPA_INFO .= ' <CtrlSum>' . $total . '</CtrlSum>' . $CrLf;
2750 $XML_SEPA_INFO .= ' <PmtTpInf>' . $CrLf;
2751 $XML_SEPA_INFO .= ' <SvcLvl>' . $CrLf;
2752 $XML_SEPA_INFO .= ' <Cd>SEPA</Cd>' . $CrLf;
2753 $XML_SEPA_INFO .= ' </SvcLvl>' . $CrLf;
2754 $XML_SEPA_INFO .= ' <LclInstrm>' . $CrLf;
2755 $XML_SEPA_INFO .= ' <Cd>' . $localInstrument . '</Cd>' . $CrLf;
2756 $XML_SEPA_INFO .= ' </LclInstrm>' . $CrLf;
2757 $XML_SEPA_INFO .= ' <SeqTp>' . $format . '</SeqTp>' . $CrLf;
2758 $XML_SEPA_INFO .= ' </PmtTpInf>' . $CrLf;
2759 $XML_SEPA_INFO .= ' <ReqdColltnDt>' . $dateTime_ETAD . '</ReqdColltnDt>' . $CrLf;
2760 $XML_SEPA_INFO .= ' <Cdtr>' . $CrLf;
2761 $XML_SEPA_INFO .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf;
2762 $XML_SEPA_INFO .= ' <PstlAdr>' . $CrLf;
2763 $XML_SEPA_INFO .= ' <Ctry>' . $country[1] . '</Ctry>' . $CrLf;
2764 $addressline1 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS'), array(chr(13) => ", ", chr(10) => ""));
2765 $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) => ""));
2766 if ($addressline1) {
2767 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2768 }
2769 if ($addressline2) {
2770 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2771 }
2772 $XML_SEPA_INFO .= ' </PstlAdr>' . $CrLf;
2773 $XML_SEPA_INFO .= ' </Cdtr>' . $CrLf;
2774 $XML_SEPA_INFO .= ' <CdtrAcct>' . $CrLf;
2775 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2776 $XML_SEPA_INFO .= ' <IBAN>' . preg_replace('/\s/', '', $this->emetteur_iban) . '</IBAN>' . $CrLf;
2777 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2778 $XML_SEPA_INFO .= ' </CdtrAcct>' . $CrLf;
2779 $XML_SEPA_INFO .= ' <CdtrAgt>' . $CrLf;
2780 $XML_SEPA_INFO .= ' <FinInstnId>' . $CrLf;
2781 $XML_SEPA_INFO .= ' <BIC>' . $this->emetteur_bic . '</BIC>' . $CrLf;
2782 $XML_SEPA_INFO .= ' </FinInstnId>' . $CrLf;
2783 $XML_SEPA_INFO .= ' </CdtrAgt>' . $CrLf;
2784 /* $XML_SEPA_INFO .= ' <UltmtCdtr>'.$CrLf;
2785 $XML_SEPA_INFO .= ' <Nm>'.dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))).'</Nm>'.$CrLf;
2786 $XML_SEPA_INFO .= ' <PstlAdr>'.$CrLf;
2787 $XML_SEPA_INFO .= ' <Ctry>'.$country[1].'</Ctry>'.$CrLf;
2788 $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;
2789 $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;
2790 $XML_SEPA_INFO .= ' </PstlAdr>'.$CrLf;
2791 $XML_SEPA_INFO .= ' </UltmtCdtr>'.$CrLf;*/
2792 $XML_SEPA_INFO .= ' <ChrgBr>SLEV</ChrgBr>' . $CrLf; // Field "Responsible of fees". Must be SLEV
2793 $XML_SEPA_INFO .= ' <CdtrSchmeId>' . $CrLf;
2794 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2795 $XML_SEPA_INFO .= ' <PrvtId>' . $CrLf;
2796 $XML_SEPA_INFO .= ' <Othr>' . $CrLf;
2797 $XML_SEPA_INFO .= ' <Id>' . $this->emetteur_ics . '</Id>' . $CrLf;
2798 $XML_SEPA_INFO .= ' <SchmeNm>' . $CrLf;
2799 $XML_SEPA_INFO .= ' <Prtry>SEPA</Prtry>' . $CrLf;
2800 $XML_SEPA_INFO .= ' </SchmeNm>' . $CrLf;
2801 $XML_SEPA_INFO .= ' </Othr>' . $CrLf;
2802 $XML_SEPA_INFO .= ' </PrvtId>' . $CrLf;
2803 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2804 $XML_SEPA_INFO .= ' </CdtrSchmeId>' . $CrLf;
2805 } else {
2806 // SEPA Paiement Information of my company for Credit Transfer
2807 $XML_SEPA_INFO = '';
2808 $XML_SEPA_INFO .= ' <PmtInf>' . $CrLf;
2809 $XML_SEPA_INFO .= ' <PmtInfId>' . ('DOL/' . $dateTime_YMD . '/CT' . $IdBon . '-' . $RefBon) . '</PmtInfId>' . $CrLf;
2810 $XML_SEPA_INFO .= ' <PmtMtd>TRF</PmtMtd>' . $CrLf;
2811 //$XML_SEPA_INFO .= ' <BtchBookg>False</BtchBookg>'.$CrLf;
2812 $XML_SEPA_INFO .= ' <NbOfTxs>' . $nombre . '</NbOfTxs>' . $CrLf;
2813 $XML_SEPA_INFO .= ' <CtrlSum>' . $total . '</CtrlSum>' . $CrLf;
2814 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.
2815 $XML_SEPA_INFO .= ' <PmtTpInf>' . $CrLf;
2816 $XML_SEPA_INFO .= ' <SvcLvl>' . $CrLf;
2817 $XML_SEPA_INFO .= ' <Cd>SEPA</Cd>' . $CrLf;
2818 $XML_SEPA_INFO .= ' </SvcLvl>' . $CrLf;
2819 $XML_SEPA_INFO .= ' <LclInstrm>' . $CrLf;
2820 $XML_SEPA_INFO .= ' <Cd>' . $localInstrument . '</Cd>' . $CrLf;
2821 $XML_SEPA_INFO .= ' </LclInstrm>' . $CrLf;
2822 $XML_SEPA_INFO .= ' <SeqTp>' . $format . '</SeqTp>' . $CrLf;
2823 $XML_SEPA_INFO .= ' </PmtTpInf>' . $CrLf;
2824 }
2825 $XML_SEPA_INFO .= ' <ReqdExctnDt>' . dol_print_date($dateTime_ETAD, 'dayrfc') . '</ReqdExctnDt>' . $CrLf;
2826 $XML_SEPA_INFO .= ' <Dbtr>' . $CrLf;
2827 $XML_SEPA_INFO .= ' <Nm>' . dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))) . '</Nm>' . $CrLf;
2828 $XML_SEPA_INFO .= ' <PstlAdr>' . $CrLf;
2829 $XML_SEPA_INFO .= ' <Ctry>' . $country[1] . '</Ctry>' . $CrLf;
2830 $addressline1 = strtr(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS'), array(chr(13) => ", ", chr(10) => ""));
2831 $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) => ""));
2832 if ($addressline1) {
2833 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline1), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2834 }
2835 if ($addressline2) {
2836 $XML_SEPA_INFO .= ' <AdrLine>' . dolEscapeXML(dol_trunc(dol_string_nospecial(dol_string_unaccent($addressline2), ' '), 70, 'right', 'UTF-8', 1)) . '</AdrLine>' . $CrLf;
2837 }
2838 $XML_SEPA_INFO .= ' </PstlAdr>' . $CrLf;
2839 $XML_SEPA_INFO .= ' </Dbtr>' . $CrLf;
2840 $XML_SEPA_INFO .= ' <DbtrAcct>' . $CrLf;
2841 $XML_SEPA_INFO .= ' <Id>' . $CrLf;
2842 $XML_SEPA_INFO .= ' <IBAN>' . preg_replace('/\s/', '', $this->emetteur_iban) . '</IBAN>' . $CrLf;
2843 $XML_SEPA_INFO .= ' </Id>' . $CrLf;
2844 $XML_SEPA_INFO .= ' </DbtrAcct>' . $CrLf;
2845 $XML_SEPA_INFO .= ' <DbtrAgt>' . $CrLf;
2846 $XML_SEPA_INFO .= ' <FinInstnId>' . $CrLf;
2847 $XML_SEPA_INFO .= ' <BIC>' . $this->emetteur_bic . '</BIC>' . $CrLf;
2848 $XML_SEPA_INFO .= ' </FinInstnId>' . $CrLf;
2849 $XML_SEPA_INFO .= ' </DbtrAgt>' . $CrLf;
2850 /* $XML_SEPA_INFO .= ' <UltmtCdtr>'.$CrLf;
2851 $XML_SEPA_INFO .= ' <Nm>'.dolEscapeXML(strtoupper(dol_string_nospecial(dol_string_unaccent($this->raison_sociale), ' '))).'</Nm>'.$CrLf;
2852 $XML_SEPA_INFO .= ' <PstlAdr>'.$CrLf;
2853 $XML_SEPA_INFO .= ' <Ctry>'.$country[1].'</Ctry>'.$CrLf;
2854 $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;
2855 $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;
2856 $XML_SEPA_INFO .= ' </PstlAdr>'.$CrLf;
2857 $XML_SEPA_INFO .= ' </UltmtCdtr>'.$CrLf;*/
2858 $XML_SEPA_INFO .= ' <ChrgBr>SLEV</ChrgBr>' . $CrLf; // Field "Responsible of fees". Must be SLEV
2859 /*$XML_SEPA_INFO .= ' <CdtrSchmeId>'.$CrLf;
2860 $XML_SEPA_INFO .= ' <Id>'.$CrLf;
2861 $XML_SEPA_INFO .= ' <PrvtId>'.$CrLf;
2862 $XML_SEPA_INFO .= ' <Othr>'.$CrLf;
2863 $XML_SEPA_INFO .= ' <Id>'.$this->emetteur_ics.'</Id>'.$CrLf;
2864 $XML_SEPA_INFO .= ' <SchmeNm>'.$CrLf;
2865 $XML_SEPA_INFO .= ' <Prtry>SEPA</Prtry>'.$CrLf;
2866 $XML_SEPA_INFO .= ' </SchmeNm>'.$CrLf;
2867 $XML_SEPA_INFO .= ' </Othr>'.$CrLf;
2868 $XML_SEPA_INFO .= ' </PrvtId>'.$CrLf;
2869 $XML_SEPA_INFO .= ' </Id>'.$CrLf;
2870 $XML_SEPA_INFO .= ' </CdtrSchmeId>'.$CrLf;*/
2871 }
2872 } else {
2873 fwrite($this->file, 'INCORRECT EMETTEUR ' . $this->raison_sociale . $CrLf);
2874 $XML_SEPA_INFO = '';
2875 }
2876 return $XML_SEPA_INFO;
2877 }
2878
2879 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2886 public function EnregTotal($total)
2887 {
2888 // phpcs:enable
2889 fwrite($this->file, "08");
2890 fwrite($this->file, "08"); // Prelevement ordinaire
2891
2892 fwrite($this->file, " "); // Zone Reservee B2
2893
2894 fwrite($this->file, $this->emetteur_ics); // ICS
2895
2896 // Reserve C1
2897
2898 fwrite($this->file, substr(" ", 0, 12));
2899
2900
2901 // Raison Sociale C2
2902
2903 fwrite($this->file, substr(" ", 0, 24));
2904
2905 // D1
2906
2907 fwrite($this->file, substr(" ", 0, 24));
2908
2909 // Zone Reservee D2
2910
2911 fwrite($this->file, substr(" ", 0, 8));
2912
2913 // Code Guichet D3
2914
2915 fwrite($this->file, substr(" ", 0, 5));
2916
2917 // Numero de compte D4
2918
2919 fwrite($this->file, substr(" ", 0, 11));
2920
2921 // Zone E Montant
2922
2923 $montant = ($total * 100);
2924
2925 fwrite($this->file, substr("000000000000000" . $montant, -16));
2926
2927 // Zone Reservee F
2928
2929 fwrite($this->file, substr(" ", 0, 31));
2930
2931 // Code etablissement
2932
2933 fwrite($this->file, substr(" ", 0, 5));
2934
2935 // Zone Reservee F
2936
2937 fwrite($this->file, substr(" ", 0, 5));
2938
2939 fwrite($this->file, "\n");
2940 }
2941
2948 public function getLibStatut($mode = 0)
2949 {
2950 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2951 }
2952
2953 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2961 public function LibStatut($status, $mode = 0)
2962 {
2963 // phpcs:enable
2964 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2965 global $langs;
2966 //$langs->load("mymodule");
2967 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusWaiting');
2968 $this->labelStatus[self::STATUS_TRANSFERED] = $langs->transnoentitiesnoconv('StatusTrans');
2969 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusWaiting');
2970 $this->labelStatusShort[self::STATUS_TRANSFERED] = $langs->transnoentitiesnoconv('StatusTrans');
2971 if ($this->type == 'bank-transfer') {
2972 $this->labelStatus[self::STATUS_DEBITED] = $langs->transnoentitiesnoconv('StatusDebited');
2973 $this->labelStatusShort[self::STATUS_DEBITED] = $langs->transnoentitiesnoconv('StatusDebited');
2974 } else {
2975 $this->labelStatus[self::STATUS_CREDITED] = $langs->transnoentitiesnoconv('StatusCredited');
2976 $this->labelStatusShort[self::STATUS_CREDITED] = $langs->transnoentitiesnoconv('StatusCredited');
2977 }
2978 $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
2979 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
2980 }
2981
2982 $statusType = 'status1';
2983 if ($status == self::STATUS_TRANSFERED) {
2984 $statusType = 'status3';
2985 }
2986 if ($status == self::STATUS_CREDITED || $status == self::STATUS_DEBITED) {
2987 $statusType = 'status6';
2988 }
2989 if ($status == self::STATUS_CANCELED) {
2990 $statusType = 'status9';
2991 }
2992
2993 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
2994 }
2995
2996 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3004 public function load_board($user, $mode)
3005 {
3006 // phpcs:enable
3007 global $conf, $langs;
3008
3009 if ($user->socid) {
3010 return -1; // Protection to prevent calls by external users
3011 }
3012
3013 if ($mode == 'direct-debit') {
3014 $sql = "SELECT p.rowid, p.date_trans as date_trans, p.date_credit as date_credit";
3015 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as p";
3016 $sql .= " WHERE p.entity IN (" . getEntity('prelevement_bons') . ")";
3017 $sql .= " AND (p.type = 'debit-order' OR p.type = 'direct-debit')"; // direct debit
3018 $sql .= " AND p.statut < ".((int) BonPrelevement::STATUS_DEBITED);
3019 } else {
3020 $sql = "SELECT p.rowid, p.date_trans as date_trans, p.date_credit as date_credit";
3021 $sql .= " FROM " . MAIN_DB_PREFIX . "prelevement_bons as p";
3022 $sql .= " WHERE p.entity IN (" . getEntity('prelevement_bons') . ")";
3023 $sql .= " AND (p.type = 'bank-transfer' OR p.type = 'credit-transfer')"; // credit transfer
3024 $sql .= " AND p.statut < ".((int) BonPrelevement::STATUS_CREDITED);
3025 }
3026
3027 $resql = $this->db->query($sql);
3028 if ($resql) {
3029 $langs->load("banks");
3030 $now = dol_now();
3031
3032 $response = new WorkboardResponse();
3033 if ($mode == 'direct-debit') {
3034 $response->warning_delay = $conf->warning_delays['bank_direct_debit'] / 60 / 60 / 24;
3035 $response->label = $langs->trans("PendingDirectDebitToComplete");
3036 $response->labelShort = $langs->trans("PendingDirectDebitToCompleteShort");
3037 $response->url = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&search_status=0,1';
3038 $response->url_late = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&search_status=0,1';
3039 } else {
3040 $response->warning_delay = $conf->warning_delays['bank_credit_transfer'] / 60 / 60 / 24;
3041 $response->label = $langs->trans("PendingCreditTransferToComplete");
3042 $response->labelShort = $langs->trans("PendingCreditTransferToCompleteShort");
3043 $response->url = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&type=bank-transfer&search_status=0,1';
3044 $response->url_late = DOL_URL_ROOT . '/compta/prelevement/orders_list.php?leftmenu=checks&mainmenu=bank&type=bank-transfer&search_status=0,1';
3045 }
3046 $response->img = img_object('', "payment");
3047
3048 $response->nbtodo = 0;
3049 $response->nbtodolate = 0;
3050
3051 while ($obj = $this->db->fetch_object($resql)) {
3052 $response->nbtodo++;
3053
3054 if ($this->db->jdate($obj->date_trans) < ($now - $response->warning_delay)) {
3055 $response->nbtodolate++;
3056 }
3057 }
3058
3059 // Return workboard only if quantity is not 0
3060 //if ($response->nbtodo) {
3061 return $response;
3062 //} else {
3063 // return 0;
3064 //}
3065 } else {
3066 dol_print_error($this->db);
3067 $this->error = $this->db->error();
3068 return -1;
3069 }
3070 }
3071
3079 public function getKanbanView($option = '', $arraydata = null)
3080 {
3081 global $langs;
3082
3083 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3084
3085 $return = '<div class="box-flex-item box-flex-grow-zero">';
3086 $return .= '<div class="info-box info-box-sm">';
3087 $return .= '<span class="info-box-icon bg-infobox-action">';
3088 $return .= img_picto('', $this->picto);
3089 $return .= '</span>';
3090 $return .= '<div class="info-box-content">';
3091 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3092 if ($selected >= 0) {
3093 $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3094 }
3095 /*
3096 if (isset($this->date_trans)) {
3097 $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>';
3098 }
3099 if (isset($this->date_credit)) {
3100 $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>';
3101 }
3102 if (isset($this->date_echeance)) {
3103 $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>';
3104 }
3105 */
3106 if (isset($this->amount)) {
3107 $return .= '<br><span class="opacitymedium">' . $langs->trans("Amount") . '</span> : <span class="amount">' . price($this->amount) . '</span>';
3108 }
3109 $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3110 $return .= '</div>';
3111 $return .= '</div>';
3112 $return .= '</div>';
3113 return $return;
3114 }
3115
3122 {
3123 if (!empty($this->id)) {
3124 $id = $this->id;
3125 } else {
3126 return 0;
3127 }
3128 $sql = "SELECT COUNT(*) AS nb FROM " . MAIN_DB_PREFIX . "prelevement_lignes";
3129 $sql .= " WHERE fk_prelevement_bons = " . ((int) $id);
3130 $sql .= " AND fk_soc = 0"; // fk_soc can't be NULL
3131 $sql .= " AND fk_user IS NOT NULL";
3132
3133 $num = 0;
3134 $resql = $this->db->query($sql);
3135 if ($resql) {
3136 $obj = $this->db->fetch_object($resql);
3137 $num = $obj->nb;
3138 } else {
3139 dol_print_error($this->db);
3140 }
3141 if ($num > 0) {
3142 return 1;
3143 }
3144
3145 return 0;
3146 }
3147}
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:168
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.
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.