* Copyright (C) 2004-2011 Laurent Destailleur * Copyright (C) 2004 Eric Seigne * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2014 Marcos GarcĂ­a * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * or see https://www.gnu.org/ */ /** * \file htdocs/core/modules/facture/modules_facture.php * \ingroup invoice * \brief File that contains parent class for invoices models * and parent class for invoices numbering models */ require_once DOL_DOCUMENT_ROOT.'/core/class/commondocgenerator.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/commonnumrefgenerator.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; // Required because used in classes that inherit // For the experimental feature using swiss QR invoice generated by composer lib sparin/swiss-qr-bill use Sprain\SwissQrBill; /** * Parent class of invoice document generators */ abstract class ModelePDFFactures extends CommonDocGenerator { public $posxpicture; public $posxtva; public $posxup; public $posxqty; public $posxunit; public $posxdesc; public $posxdiscount; public $postotalht; public $tva; public $tva_array; public $localtax1; public $localtax2; public $atleastonediscount = 0; public $atleastoneratenotnull = 0; // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return list of active generation modules * * @param DoliDB $db Database handler * @param int<0,max> $maxfilenamelength Max length of value to show * @return string[]|int<-1,0> List of templates */ public static function liste_modeles($db, $maxfilenamelength = 0) { // phpcs:enable $type = 'invoice'; $list = array(); include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; $list = getListOfModels($db, $type, $maxfilenamelength); return $list; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Function to build pdf onto disk * * @param Facture $object Object to generate * @param Translate $outputlangs Lang output object * @param string $srctemplatepath Full path of source filename for generator using a template file * @param int<0,1> $hidedetails Do not show line details * @param int<0,1> $hidedesc Do not show desc * @param int<0,1> $hideref Do not show ref * @return int<-1,1> 1=OK, <=0=KO */ abstract public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0); // phpcs:enable /** * Get the SwissQR object, including validation * * @param Facture $object Invoice object * @param Translate $langs Translation object * @return SwissQrBill\QrBill|bool The valid SwissQR object, or false */ private function getSwissQrBill(Facture $object, Translate $langs) { global $conf; if (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') != 'bottom') { return false; } if ($object->mode_reglement_code != 'VIR') { $this->error = $langs->transnoentities("SwissQrOnlyVIR"); return false; } if (empty($object->fk_account)) { $this->error = 'Bank account must be defined to use this experimental feature'; return false; } // Load the autoload file generated by composer if (file_exists(DOL_DOCUMENT_ROOT.'/includes/sprain/swiss-qr-bill/autoload.php')) { require_once DOL_DOCUMENT_ROOT.'/includes/sprain/swiss-qr-bill/autoload.php'; } elseif (file_exists(DOL_DOCUMENT_ROOT.'/includes/autoload.php')) { require_once DOL_DOCUMENT_ROOT.'/includes/autoload.php'; } else { $this->error = 'PHP library sprain/swiss-qr-bill was not found. Please install it with:
cd '.dirname(DOL_DOCUMENT_ROOT).'; cp composer.json.disabled composer.json; composer require sprain/swiss-qr-bill;'; return false; } // Create a new instance of SwissQrBill, containing default headers with fixed values $qrBill = SwissQrBill\QrBill::create(); // First, set creditor address $address = SwissQrBill\DataGroup\Element\CombinedAddress::create( $this->emetteur->name, $this->emetteur->address, $this->emetteur->zip . " " . $this->emetteur->town, $this->emetteur->country_code ); if (!$address->isValid()) { $this->error = $langs->transnoentities("SwissQrCreditorAddressInvalid", (string) $address->getViolations()); return false; } $qrBill->setCreditor($address); // Get IBAN from account. $account = new Account($this->db); $account->fetch($object->fk_account); $creditorInformation = SwissQrBill\DataGroup\Element\CreditorInformation::create($account->iban); if (!$creditorInformation->isValid()) { $langs->load("errors"); $this->error = $langs->transnoentities("SwissQrCreditorInformationInvalid", $account->iban, (string) $creditorInformation->getViolations()); return false; } $qrBill->setCreditorInformation($creditorInformation); if ($creditorInformation->containsQrIban()) { $this->error = $langs->transnoentities("SwissQrIbanNotImplementedYet", $account->iban); return false; } // Add payment reference CLASSIC-IBAN // This is what you will need to identify incoming payments. $qrBill->setPaymentReference( SwissQrBill\DataGroup\Element\PaymentReference::create( SwissQrBill\DataGroup\Element\PaymentReference::TYPE_NON ) ); $currencyinvoicecode = $object->multicurrency_code ? $object->multicurrency_code : $conf->currency; // Add payment amount, with currency $pai = SwissQrBill\DataGroup\Element\PaymentAmountInformation::create($currencyinvoicecode, $object->total_ttc); if (!$pai->isValid()) { $this->error = $langs->transnoentities("SwissQrPaymentInformationInvalid", $object->total_ttc, (string) $pai->getViolations()); return false; } $qrBill->setPaymentAmountInformation($pai); // Add some human-readable information about what the bill is for. $qrBill->setAdditionalInformation( SwissQrBill\DataGroup\Element\AdditionalInformation::create( $object->ref ) ); // Set debtor address; We _know_ zip&town have to be filled, so skip that if unfilled. if (!empty($object->thirdparty->zip) && !empty($object->thirdparty->town)) { $address = SwissQrBill\DataGroup\Element\CombinedAddress::create( $object->thirdparty->name, $object->thirdparty->address, $object->thirdparty->zip . " " . $object->thirdparty->town, $object->thirdparty->country_code ); if (!$address->isValid()) { $this->error = $langs->transnoentities("SwissQrDebitorAddressInvalid", (string) $address->getViolations()); return false; } $qrBill->setUltimateDebtor($address); } return $qrBill; } /** * Get the height for bottom-page QR invoice in mm, depending on the page number. * * @param int $pagenbr Page number * @param Facture $object Invoice object * @param Translate $langs Translation object * @return int Height in mm of the bottom-page QR invoice. Can be zero if not on right page; not enabled */ protected function getHeightForQRInvoice(int $pagenbr, Facture $object, Translate $langs) { if (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') == 'bottom') { // Keep it, to reset it after QRinvoice getter $error = $this->error; if (!$this->getSwissQrBill($object, $langs)) { // Reset error to previous one if exists if (!empty($error)) { $this->error = $error; } return 0; } // SWIFT's requirementis 105, but we get more room with 100 and the page number is in a nice place. return $pagenbr == 1 ? 100 : 0; } return 0; } /** * Add SwissQR invoice at bottom of page 1 * * @param TCPDF $pdf TCPDF object * @param Facture $object Invoice object * @param Translate $langs Translation object * @return bool True for for success */ public function addBottomQRInvoice(TCPDF $pdf, Facture $object, Translate $langs): bool { if (!($qrBill = $this->getSwissQrBill($object, $langs))) { return false; } try { $pdf->startTransaction(); $pdf->setPage(1); $pdf->SetTextColor(0, 0, 0); $output = new SwissQrBill\PaymentPart\Output\TcPdfOutput\TcPdfOutput($qrBill, in_array($langs->shortlang, ['de', 'fr', 'it']) ? $langs->shortlang : 'en', $pdf); $output->setPrintable(false)->getPaymentPart(); } catch (Exception $e) { $pdf->rollbackTransaction(true); return false; } return true; } } /** * Parent class of invoice reference numbering templates */ abstract class ModeleNumRefFactures extends CommonNumRefGenerator { /** * Return next value not used or last value used * * @param Societe $objsoc Object third party * @param ?Facture $invoice Object invoice * @param string $mode 'next' for next value or 'last' for last value * @return string|int<-1,0> Value if OK, <=0 if KO */ abstract public function getNextValue($objsoc, $invoice, $mode = 'next'); /** * Return an example of numbering * * @return string Example */ abstract public function getExample(); }