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