dolibarr 25.0.0-alpha
fournisseur.facture.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2010-2023 Juanjo Menent <jmenent@simnandez.es>
8 * Copyright (C) 2013-2019 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2015 Bahfir Abbes <bafbes@gmail.com>
12 * Copyright (C) 2015-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2016-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
14 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
15 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
16 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
17 * Copyright (C) 2023 Nick Fragoulis
18 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
19 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
20 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 */
35
42require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
43require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
44require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.ligne.class.php';
45require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
46require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
47
48if (isModEnabled('accounting')) {
49 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
50 require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
51}
52
57{
58 use CommonSubtotal;
59
63 public $element = 'invoice_supplier';
64
68 public $table_element = 'facture_fourn';
69
73 public $table_element_line = 'facture_fourn_det';
74
78 public $class_element_line = 'SupplierInvoiceLine';
82 public $fk_element = 'fk_facture_fourn';
83
87 public $picto = 'supplier_invoice';
88
93 public $restrictiononfksoc = 1;
94
98 protected $table_ref_field = 'ref';
99
103 public $rowid;
104
108 public $ref;
109
113 public $ref_supplier;
114
119 public $libelle;
123 public $label;
124
125 //Check constants for types
126 public $type = self::TYPE_STANDARD;
127
134 public $statut;
135
141 public $status;
142
149 public $fk_statut;
150
156 public $paye;
161 public $paid;
162
167 public $author;
168
173 public $fk_user_valid;
174
180 public $datec;
181
187 public $date_echeance;
188
193 public $amount = 0;
198 public $remise = 0;
199
204 public $tva;
205
206 // Warning: Do not set default value into property definition. it must stay null.
207 // For example to avoid to have substitution done when object is generic and not yet defined.
209 public $localtax1;
211 public $localtax2;
213 public $total_ht;
215 public $total_tva;
217 public $total_localtax1;
219 public $total_localtax2;
221 public $total_ttc;
222
228 public $note;
230 public $note_private;
232 public $note_public;
234 public $propalid;
235
239 public $fk_account; // default bank account
240
244 public $transport_mode_id;
245
249 public $vat_reverse_charge;
250
254 public $extraparams = array();
255
259 public $lines = array();
260
265 public $fournisseur;
266
268
271 public $fk_facture_source;
272
274 public $fac_rec;
276 public $fk_fac_rec_source;
277
278 public $fields = array(
279 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
280 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
281 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
282 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
283 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
284 'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
285 'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
286 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
287 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
288 'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
289 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
290 'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
291 'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
292 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
293 'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
294 'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
295 'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
296 'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
297 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
298 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
299 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
300 'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
301 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
302 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
303 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
304 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
305 'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
306 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
307 'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
308 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
309 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
310 'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
311 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
312 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
313 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
314 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
315 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
316 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
317 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
318 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
319 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
320 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
321 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
322 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
323 'date_pointoftax' => array('type' => 'date', 'label' => 'Date pointoftax', 'enabled' => 1, 'visible' => -1, 'position' => 235),
324 'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
325 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
326 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
327 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
328 );
329
330
334 const TYPE_STANDARD = 0;
335
340
345
349 const TYPE_DEPOSIT = 3;
350
354 const STATUS_DRAFT = 0;
355
360
368 const STATUS_CLOSED = 2;
369
378
379 const CLOSECODE_DISCOUNTVAT = 'discount_vat';
380 const CLOSECODE_BADCREDIT = 'badsupplier';
381 const CLOSECODE_ABANDONED = 'abandon';
382 const CLOSECODE_REPLACED = 'replaced';
383
384
390 public function __construct($db)
391 {
392 $this->db = $db;
393
394 $this->ismultientitymanaged = 1;
395 }
396
403 public function create($user)
404 {
405 global $langs, $conf, $hookmanager;
406
407 $error = 0;
408 $now = dol_now();
409
410 // Clean parameters
411 if (isset($this->ref_supplier)) {
412 $this->ref_supplier = trim($this->ref_supplier);
413 }
414 if (empty($this->type)) {
415 $this->type = self::TYPE_STANDARD;
416 }
417 if (empty($this->date)) {
418 $this->date = $now;
419 }
420
421 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
422 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
423 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
424 } else {
425 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
426 }
427 if (empty($this->fk_multicurrency)) {
428 $this->multicurrency_code = $conf->currency;
429 $this->fk_multicurrency = 0;
430 $this->multicurrency_tx = 1;
431 }
432 $this->entity = setEntity($this);
433
434 $this->db->begin();
435
436 // Defaults
437 $originaldatewhen = 0;
438 $nextdatewhen = 0;
439 $previousdaynextdatewhen = 0;
440 $_facrec = null;
441
442 // Create invoice from a template recurring invoice
443 if ($this->fac_rec > 0) {
444 $this->fk_fac_rec_source = $this->fac_rec;
445
446 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
447 $_facrec = new FactureFournisseurRec($this->db);
448 $result = $_facrec->fetch($this->fac_rec);
449 $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
450
451 // Define some dates
452 if (!empty($_facrec->frequency)) {
453 $originaldatewhen = $_facrec->date_when;
454 $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
455 $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
456 $this->socid = $_facrec->socid;
457 }
458
459 $this->entity = $_facrec->entity; // Invoice created in same entity than template
460
461 // Fields coming from GUI
462 // @TODO Value of template should be used as default value on the form on the GUI, and we should here always use the value from GUI
463 // set by posted page with $object->xxx = ... and this section should be removed.
464 $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
465 $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
466 $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
467 $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
468 $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
469 $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
470 $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
471
472 // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
473 $this->total_ht = $_facrec->total_ht;
474 $this->total_ttc = $_facrec->total_ttc;
475
476 // Fields always coming from template
477 $this->fk_incoterms = $_facrec->fk_incoterms;
478 $this->location_incoterms = $_facrec->location_incoterms;
479
480 // Clean parameters
481 if (! $this->type) {
482 $this->type = self::TYPE_STANDARD;
483 }
484 $this->note_public = trim($this->note_public);
485 $this->note_private = trim($this->note_private);
486 $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
487
488 $this->array_options = $_facrec->array_options;
489
490 if (! $this->mode_reglement_id) {
491 $this->mode_reglement_id = 0;
492 }
493 $this->status = self::STATUS_DRAFT;
494 $this->statut = self::STATUS_DRAFT; // deprecated
495
496 $this->linked_objects = $_facrec->linkedObjectsIds;
497 // We do not add link to template invoice or next invoice will be linked to all generated invoices
498 //$this->linked_objects['facturerec'][0] = $this->fac_rec;
499
500 $forceduedate = $this->calculate_date_lim_reglement();
501
502 // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
503 if ($_facrec->frequency > 0) {
504 $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
505 dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
506 if (empty($_facrec->date_when)) {
507 $_facrec->date_when = $now;
508 }
509 $next_date = $_facrec->getNextDate(); // Calculate next date
510 $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
511 //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
512 $result = $_facrec->setNextDate($next_date, 1);
513 }
514
515 // Define lang of customer
516 $outputlangs = $langs;
517 $newlang = '';
518
519 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
520 $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
521 }
522 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && property_exists($this, 'default_lang') && isset($this->default_lang)) { // @phan-suppress-current-line PhanUndeclaredProperty
523 $newlang = $this->default_lang; // for thirdparty @phan-suppress-current-line PhanUndeclaredProperty
524 }
525 if (!empty($newlang)) {
526 $outputlangs = new Translate("", $conf);
527 $outputlangs->setDefaultLang($newlang);
528 } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
529 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
530 $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
531 $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
532 $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
533 $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
534 $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
535 $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
536 $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
537 $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
538 $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
539 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
540 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
541 $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
542 $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
543 $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
544
545 complete_substitutions_array($substitutionarray, $outputlangs);
546
547 $this->note_public = make_substitutions($this->note_public, $substitutionarray);
548 $this->note_private = make_substitutions($this->note_private, $substitutionarray);
549 }
550
551 // Define due date if not already defined
552 if (!empty($forceduedate)) {
553 $this->date_echeance = $forceduedate;
554 }
555
556 $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
557 $sql .= "ref";
558 $sql .= ", ref_supplier";
559 $sql .= ", ref_ext";
560 $sql .= ", entity";
561 $sql .= ", type";
562 $sql .= ", subtype";
563 $sql .= ", libelle";
564 $sql .= ", fk_soc";
565 $sql .= ", datec";
566 $sql .= ", datef";
567 $sql .= ", vat_reverse_charge";
568 $sql .= ", fk_projet";
569 $sql .= ", fk_cond_reglement";
570 $sql .= ", fk_mode_reglement";
571 $sql .= ", fk_account";
572 $sql .= ", note_private";
573 $sql .= ", note_public";
574 $sql .= ", fk_user_author";
575 $sql .= ", date_lim_reglement";
576 $sql .= ", fk_incoterms, location_incoterms";
577 $sql .= ", fk_multicurrency";
578 $sql .= ", multicurrency_code";
579 $sql .= ", multicurrency_tx";
580 $sql .= ", fk_facture_source";
581 $sql .= ", fk_fac_rec_source";
582 $sql .= ")";
583 $sql .= " VALUES (";
584 $sql .= "'(PROV)'";
585 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
586 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
587 $sql .= ", ".((int) $this->entity);
588 $sql .= ", '".$this->db->escape((string) $this->type)."'";
589 $sql .= ", ".(isset($this->subtype) ? (int) $this->subtype : "null");
590 $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
591 $sql .= ", ".((int) $this->socid);
592 $sql .= ", '".$this->db->idate($now)."'";
593 $sql .= ", '".$this->db->idate($this->date)."'";
594 $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0);
595 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
596 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
597 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
598 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
599 $sql .= ", '".$this->db->escape($this->note_private)."'";
600 $sql .= ", '".$this->db->escape($this->note_public)."'";
601 $sql .= ", ".((int) $user->id).",";
602 $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
603 $sql .= ", ".(int) $this->fk_incoterms;
604 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
605 $sql .= ", ".(int) $this->fk_multicurrency;
606 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
607 $sql .= ", ".(float) $this->multicurrency_tx;
608 $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
609 $sql .= ", ".(isset($this->fk_fac_rec_source) ? ((int) $this->fk_fac_rec_source) : "NULL");
610 $sql .= ")";
611
612 dol_syslog(get_class($this)."::create", LOG_DEBUG);
613 $resql = $this->db->query($sql);
614 if ($resql) {
615 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
616
617 // Update ref with new one
618 $this->ref = '(PROV'.$this->id.')';
619 $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
620
621 dol_syslog(get_class($this)."::create", LOG_DEBUG);
622 $resql = $this->db->query($sql);
623 if (!$resql) {
624 $error++;
625 }
626
627 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
628 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
629 }
630
631 // Add object linked
632 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
633 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
634 if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
635 foreach ($tmp_origin_id as $origin_id) {
636 $ret = $this->add_object_linked($origin, $origin_id);
637 if (!$ret) {
638 dol_print_error($this->db);
639 $error++;
640 }
641 }
642 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
643 $origin_id = $tmp_origin_id;
644 $ret = $this->add_object_linked($origin, $origin_id);
645 if (!$ret) {
646 dol_print_error($this->db);
647 $error++;
648 }
649 }
650 }
651 }
652
653 if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
654 dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
655 foreach ($this->lines as $i => $val) {
656 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
657 $sql .= " VALUES (".((int) $this->id).", ".((int) $this->lines[$i]->special_code).", ".($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL').')';
658
659 $resql_insert = $this->db->query($sql);
660 if ($resql_insert) {
661 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
662
663 // Preserve the original entry mode of the line so the total is computed from the typed value (no rounding drift).
664 $line_price_base_type = $this->lines[$i]->getPriceBaseType();
665 $line_pu = ($line_price_base_type === 'TTC') ? (float) $this->lines[$i]->subprice_ttc : $this->lines[$i]->subprice;
666 $res = $this->updateline(
667 $idligne,
668 $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
669 $line_pu,
670 $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
671 $this->lines[$i]->localtax1_tx,
672 $this->lines[$i]->localtax2_tx,
673 $this->lines[$i]->qty,
674 $this->lines[$i]->fk_product,
675 $line_price_base_type,
676 (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
677 $this->lines[$i]->product_type,
678 $this->lines[$i]->remise_percent,
679 0,
680 $this->lines[$i]->date_start,
681 $this->lines[$i]->date_end,
682 $this->lines[$i]->array_options,
683 $this->lines[$i]->fk_unit,
684 $this->lines[$i]->multicurrency_subprice,
685 $this->lines[$i]->ref_supplier
686 );
687 } else {
688 $this->error = $this->db->lasterror();
689 $this->db->rollback();
690 return -5;
691 }
692 }
693 } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
694 dol_syslog("There is ".count($this->lines)." lines that are array lines");
695 foreach ($this->lines as $i => $val) {
696 $line = $this->lines[$i];
697
698 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
699 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
700 if (!is_object($line)) {
701 $line = (object) $line;
702 }
703
704 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
705 $sql .= " VALUES (".((int) $this->id).", ".((int) $this->lines[$i]->special_code).", ".($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL').')';
706
707 $resql_insert = $this->db->query($sql);
708 if ($resql_insert) {
709 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
710
711 $this->updateline(
712 $idligne,
713 $line->desc ? $line->desc : $line->description,
714 $line->subprice,
715 $line->tva_tx,
716 $line->localtax1_tx,
717 $line->localtax2_tx,
718 $line->qty,
719 $line->fk_product,
720 'HT',
721 (!empty($line->info_bits) ? $line->info_bits : ''),
722 $line->product_type,
723 $line->remise_percent,
724 0,
725 $line->date_start,
726 $line->date_end,
727 $line->array_options,
728 $line->fk_unit,
729 $line->multicurrency_subprice,
730 $line->ref_supplier
731 );
732 } else {
733 $this->error = $this->db->lasterror();
734 $this->db->rollback();
735 return -5;
736 }
737 }
738 }
739
740 /*
741 * Insert lines of template invoices
742 */
743 if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
744 foreach ($_facrec->lines as $i => $val) {
745 $product_type = $_facrec->lines[$i]->product_type;
746 if ($_facrec->lines[$i]->fk_product) {
747 $prod = new Product($this->db);
748 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
749 }
750
751 // For line from template invoice, we use data from template invoice
752 /*
753 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
754 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
755 if (empty($tva_tx)) $tva_npr=0;
756 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
757 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
758 */
759 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
760 $tva_npr = $_facrec->lines[$i]->info_bits;
761 if (empty($tva_tx)) {
762 $tva_npr = 0;
763 }
764 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
765 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
766
767 // $fk_product_fournisseur_price not used and does not exist on line
768 // $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
769 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
770
771 // If buyprice not defined from template invoice, we try to guess the best value
772 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
773 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
774 $producttmp = new ProductFournisseur($this->db);
775 $producttmp->fetch($_facrec->lines[$i]->fk_product);
776
777 // If margin module defined on costprice, we try the costprice
778 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
779 // else we get the best supplier price
780 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
781 $buyprice = $producttmp->cost_price;
782 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
783 $buyprice = $producttmp->pmp;
784 } else {
785 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
786 if ($producttmp->product_fourn_price_id > 0) {
787 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
788 }
789 }
790 }
791 }
792
793 $result_insert = $this->addline(
794 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
795 $_facrec->lines[$i]->pu_ht,
796 $tva_tx,
797 $localtax1_tx,
798 $localtax2_tx,
799 $_facrec->lines[$i]->qty,
800 $_facrec->lines[$i]->fk_product,
801 $_facrec->lines[$i]->remise_percent,
802 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
803 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
804 0,
805 $_facrec->lines[$i]->info_bits,
806 'HT',
807 $product_type,
808 $_facrec->lines[$i]->rang,
809 0,
810 $_facrec->lines[$i]->array_options,
811 $_facrec->lines[$i]->fk_unit,
812 0,
813 0,
814 $_facrec->lines[$i]->ref_supplier,
815 $_facrec->lines[$i]->special_code,
816 0,
817 0
818 );
819 if ($result_insert < 0) {
820 $error++;
821 $this->error = $this->db->error();
822 break;
823 }
824 }
825 }
826
827
828 // Update total price
829 $result = $this->update_price(1);
830 if ($result > 0) {
831 // Actions on extra fields
832 if (!$error) {
833 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
834 if ($result < 0) {
835 $error++;
836 }
837 }
838
839 if (!$error) {
840 // Call trigger
841 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
842 if ($result < 0) {
843 $error++;
844 }
845 // End call triggers
846 }
847
848 if (!$error) {
849 $this->db->commit();
850 return $this->id;
851 } else {
852 $this->db->rollback();
853 return -4;
854 }
855 } else {
856 $this->error = $langs->trans('FailedToUpdatePrice');
857 $this->db->rollback();
858 return -3;
859 }
860 } else {
861 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
862 $this->error = $langs->trans('ErrorRefAlreadyExists');
863 $this->db->rollback();
864 return -1;
865 } else {
866 $this->error = $this->db->lasterror();
867 $this->db->rollback();
868 return -2;
869 }
870 }
871 }
872
881 public function fetch($id = 0, $ref = '', $ref_ext = '')
882 {
883 if (empty($id) && empty($ref) && empty($ref_ext)) {
884 return -1;
885 }
886
887 $sql = "SELECT";
888 $sql .= " t.rowid,";
889 $sql .= " t.ref,";
890 $sql .= " t.ref_supplier,";
891 $sql .= " t.ref_ext,";
892 $sql .= " t.entity,";
893 $sql .= " t.type,";
894 $sql .= " t.subtype,";
895 $sql .= " t.fk_soc,";
896 $sql .= " t.datec,";
897 $sql .= " t.datef,";
898 $sql .= " t.tms as datem,";
899 $sql .= " t.libelle as label,";
900 $sql .= " t.paye as paid,";
901 $sql .= " t.close_code,";
902 $sql .= " t.close_note,";
903 $sql .= " t.tva,";
904 $sql .= " t.localtax1,";
905 $sql .= " t.localtax2,";
906 $sql .= " t.total_ht,";
907 $sql .= " t.total_tva,";
908 $sql .= " t.total_ttc,";
909 $sql .= " t.fk_statut as status,";
910 $sql .= " t.fk_user_author,";
911 $sql .= " t.fk_user_valid,";
912 $sql .= " t.fk_facture_source,";
913 $sql .= " t.vat_reverse_charge,";
914 $sql .= " t.fk_fac_rec_source,";
915 $sql .= " t.fk_projet as fk_project,";
916 $sql .= " t.fk_cond_reglement,";
917 $sql .= " t.fk_account,";
918 $sql .= " t.fk_mode_reglement,";
919 $sql .= " t.date_lim_reglement,";
920 $sql .= " t.note_private,";
921 $sql .= " t.note_public,";
922 $sql .= " t.model_pdf,";
923 $sql .= " t.last_main_doc,";
924 $sql .= " t.import_key,";
925 $sql .= " t.extraparams,";
926 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
927 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
928 $sql .= ' s.nom as socnom, s.rowid as socid,';
929 $sql .= ' t.fk_incoterms, t.location_incoterms,';
930 $sql .= " i.libelle as label_incoterms,";
931 $sql .= ' t.fk_transport_mode,';
932 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
933 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
934 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
935 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
936 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
937 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
938 if ($id) {
939 $sql .= " WHERE t.rowid = ".((int) $id);
940 } else {
941 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
942 if ($ref) {
943 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
944 }
945 if ($ref_ext) {
946 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
947 }
948 }
949
950 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
951 $resql = $this->db->query($sql);
952 if ($resql) {
953 if ($this->db->num_rows($resql)) {
954 $obj = $this->db->fetch_object($resql);
955
956 $this->id = $obj->rowid;
957 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
958
959 $this->ref_supplier = $obj->ref_supplier;
960 $this->ref_ext = $obj->ref_ext;
961 $this->entity = $obj->entity;
962 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
963 $this->subtype = $obj->subtype;
964 $this->socid = $obj->fk_soc;
965 $this->date = $this->db->jdate($obj->datef);
966 $this->date_creation = $this->db->jdate($obj->datec);
967 $this->datec = $this->db->jdate($obj->datec);
968 $this->date_modification = $this->db->jdate($obj->datem);
969 $this->tms = $this->db->jdate($obj->datem);
970 $this->libelle = $obj->label; // deprecated
971 $this->label = $obj->label;
972 $this->paye = $obj->paid;
973 $this->paid = $obj->paid;
974 $this->close_code = $obj->close_code;
975 $this->close_note = $obj->close_note;
976 $this->total_localtax1 = $obj->localtax1;
977 $this->total_localtax2 = $obj->localtax2;
978 $this->total_ht = $obj->total_ht;
979 $this->total_tva = $obj->total_tva;
980 $this->total_ttc = $obj->total_ttc;
981 $this->status = $obj->status;
982 $this->statut = $obj->status; // For backward compatibility
983 $this->fk_statut = $obj->status; // For backward compatibility
984 $this->user_creation_id = $obj->fk_user_author;
985 $this->author = $obj->fk_user_author; // deprecated
986 $this->user_validation_id = $obj->fk_user_valid;
987 $this->fk_user_valid = $obj->fk_user_valid;
988 $this->fk_facture_source = $obj->fk_facture_source;
989 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
990 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
991 $this->fk_project = $obj->fk_project;
992 $this->cond_reglement_id = $obj->fk_cond_reglement;
993 $this->cond_reglement_code = $obj->cond_reglement_code;
994 $this->cond_reglement_label = $obj->cond_reglement_label;
995 $this->cond_reglement_doc = $obj->cond_reglement_doc;
996 $this->fk_account = $obj->fk_account;
997 $this->mode_reglement_id = $obj->fk_mode_reglement;
998 $this->mode_reglement_code = $obj->mode_reglement_code;
999 $this->mode_reglement = $obj->mode_reglement_label;
1000 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
1001 $this->note = $obj->note_private; // deprecated
1002 $this->note_private = $obj->note_private;
1003 $this->note_public = $obj->note_public;
1004 $this->model_pdf = $obj->model_pdf;
1005 $this->last_main_doc = $obj->last_main_doc;
1006 $this->import_key = $obj->import_key;
1007
1008 //Incoterms
1009 $this->fk_incoterms = $obj->fk_incoterms;
1010 $this->location_incoterms = $obj->location_incoterms;
1011 $this->label_incoterms = $obj->label_incoterms;
1012 $this->transport_mode_id = $obj->fk_transport_mode;
1013
1014 // Multicurrency
1015 $this->fk_multicurrency = $obj->fk_multicurrency;
1016 $this->multicurrency_code = $obj->multicurrency_code;
1017 $this->multicurrency_tx = $obj->multicurrency_tx;
1018 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1019 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1020 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1021
1022 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1023
1024 $this->socid = $obj->socid;
1025
1026 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1027
1028 // Retrieve all extrafield
1029 // fetch optionals attributes and labels
1030 $this->fetch_optionals();
1031
1032 $result = $this->fetch_lines();
1033 if ($result < 0) {
1034 $this->error = $this->db->lasterror();
1035 return -3;
1036 }
1037 } else {
1038 $this->error = 'Bill with id '.$id.' not found';
1039 dol_syslog(get_class($this).'::fetch '.$this->error);
1040 return 0;
1041 }
1042
1043 $this->db->free($resql);
1044 return 1;
1045 } else {
1046 $this->error = "Error ".$this->db->lasterror();
1047 return -1;
1048 }
1049 }
1050
1051
1052 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1058 public function fetch_lines()
1059 {
1060 // phpcs:enable
1061 $this->lines = array();
1062
1063 $sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description as line_desc, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
1064 $sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn, f.fk_remise_except';
1065 $sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit, f.extraparams';
1066 $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.barcode as product_barcode, p.description as product_desc';
1067 $sql .= ', f.fk_code_ventilation, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc';
1068 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1069 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1070 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1071 $sql .= ' ORDER BY f.rang, f.rowid';
1072
1073 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1074
1075 $resql_rows = $this->db->query($sql);
1076 if ($resql_rows) {
1077 $num_rows = $this->db->num_rows($resql_rows);
1078 if ($num_rows) {
1079 $i = 0;
1080 while ($i < $num_rows) {
1081 $obj = $this->db->fetch_object($resql_rows);
1082
1083 $line = new SupplierInvoiceLine($this->db);
1084
1085 $line->id = $obj->rowid;
1086 $line->rowid = $obj->rowid;
1087
1088 $line->description = $obj->line_desc;
1089 $line->desc = $obj->line_desc;
1090 $line->date_start = $this->db->jdate($obj->date_start);
1091 $line->date_end = $this->db->jdate($obj->date_end);
1092
1093 $line->product_ref = $obj->product_ref;
1094 $line->ref = $obj->product_ref;
1095 $line->ref_supplier = $obj->ref_supplier;
1096 $line->libelle = $obj->label;
1097 $line->label = $obj->label;
1098 $line->product_barcode = $obj->product_barcode;
1099 $line->product_desc = $obj->product_desc;
1100 $line->subprice = $obj->pu_ht;
1101 $line->pu_ht = $obj->pu_ht; // deprecated
1102 $line->subprice_ttc = $obj->pu_ttc;
1103 $line->pu_ttc = $obj->pu_ttc; // deprcated
1104 $line->vat_src_code = $obj->vat_src_code;
1105 $line->tva_tx = $obj->tva_tx;
1106 $line->localtax1_tx = $obj->localtax1_tx;
1107 $line->localtax2_tx = $obj->localtax2_tx;
1108 $line->localtax1_type = $obj->localtax1_type;
1109 $line->localtax2_type = $obj->localtax2_type;
1110 $line->qty = $obj->qty;
1111 $line->remise_percent = $obj->remise_percent;
1112 $line->fk_remise_except = $obj->fk_remise_except;
1113 //$line->tva = $obj->total_tva; // deprecated
1114 $line->total_ht = $obj->total_ht;
1115 $line->total_ttc = $obj->total_ttc;
1116 $line->total_tva = $obj->total_tva;
1117 $line->total_localtax1 = $obj->total_localtax1;
1118 $line->total_localtax2 = $obj->total_localtax2;
1119 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1120 $line->fk_product = $obj->fk_product;
1121 $line->product_type = $obj->product_type;
1122 $line->product_label = $obj->label;
1123 $line->info_bits = $obj->info_bits;
1124 $line->fk_parent_line = $obj->fk_parent_line;
1125 $line->special_code = $obj->special_code;
1126 $line->rang = $obj->rang;
1127 $line->fk_unit = $obj->fk_unit;
1128
1129 $line->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1130
1131 // Accountancy
1132 $line->fk_code_ventilation = $obj->fk_code_ventilation;
1133
1134 // Multicurrency
1135 $line->fk_multicurrency = $obj->fk_multicurrency;
1136 $line->multicurrency_code = $obj->multicurrency_code;
1137 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1138 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1139 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1140 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1141
1142 // Extra fields
1143 $line->fetch_optionals();
1144
1145 $this->lines[$i] = $line;
1146
1147 $i++;
1148 }
1149 }
1150 $this->db->free($resql_rows);
1151 return 1;
1152 } else {
1153 $this->error = $this->db->error();
1154 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1155 return -3;
1156 }
1157 }
1158
1159
1167 public function update($user = null, $notrigger = 0)
1168 {
1169 global $langs;
1170 $error = 0;
1171
1172 // Clean parameters
1173 if (empty($this->type)) {
1174 $this->type = self::TYPE_STANDARD;
1175 }
1176 if (isset($this->ref)) {
1177 $this->ref = trim($this->ref);
1178 }
1179 if (isset($this->ref_supplier)) {
1180 $this->ref_supplier = trim($this->ref_supplier);
1181 }
1182 if (isset($this->ref_ext)) {
1183 $this->ref_ext = trim($this->ref_ext);
1184 }
1185 if (isset($this->entity)) {
1186 $this->entity = (int) $this->entity;
1187 }
1188 if (isset($this->type)) {
1189 $this->type = (int) $this->type;
1190 }
1191 if (isset($this->subtype)) {
1192 $this->subtype = (int) $this->subtype;
1193 }
1194 if (isset($this->socid)) {
1195 $this->socid = (int) $this->socid;
1196 }
1197 if (isset($this->label)) {
1198 $this->label = trim($this->label);
1199 }
1200 if (isset($this->paid)) {
1201 $this->paid = (int) (bool) $this->paid;
1202 $this->paye = $this->paid; // deprecated
1203 } elseif (isset($this->paye)) {
1204 $this->paid = (int) (bool) $this->paye;
1205 $this->paye = $this->paid; // deprecared
1206 }
1207 if (isset($this->close_code)) {
1208 $this->close_code = trim($this->close_code);
1209 }
1210 if (isset($this->close_note)) {
1211 $this->close_note = trim($this->close_note);
1212 }
1213 if (empty($this->total_ht)) {
1214 $this->total_ht = 0;
1215 }
1216 if (empty($this->total_tva)) {
1217 $this->total_tva = 0;
1218 }
1219 if (empty($this->total_localtax1)) {
1220 $this->total_localtax1 = 0;
1221 }
1222 if (empty($this->total_localtax2)) {
1223 $this->total_localtax2 = 0;
1224 }
1225 if (isset($this->total_ttc)) {
1226 $this->total_ttc = (float) $this->total_ttc;
1227 }
1228 if (isset($this->status)) {
1229 $this->status = (int) $this->status;
1230 $this->statut = $this->status;
1231 } elseif (isset($this->statut)) {
1232 $this->status = (int) $this->statut;
1233 $this->statut = $this->status;
1234 }
1235 if (isset($this->author)) { // TODO: user_creation_id?
1236 $this->author = (int) $this->author;
1237 }
1238 if (isset($this->fk_user_valid)) {
1239 $this->fk_user_valid = (int) $this->fk_user_valid;
1240 }
1241 if (isset($this->fk_facture_source)) {
1242 $this->fk_facture_source = (int) $this->fk_facture_source;
1243 }
1244 if (isset($this->fk_project)) {
1245 if (empty($this->fk_project)) {
1246 $this->fk_project = 0;
1247 } else {
1248 $this->fk_project = (int) $this->fk_project;
1249 }
1250 }
1251 if (isset($this->mode_reglement_id)) {
1252 $this->mode_reglement_id = (int) $this->mode_reglement_id;
1253 }
1254 if (isset($this->cond_reglement_id)) {
1255 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1256 }
1257 if (isset($this->note_private)) {
1258 $this->note_private = trim($this->note_private);
1259 $this->note = $this->note_private;
1260 }
1261 if (isset($this->note_public)) {
1262 $this->note_public = trim($this->note_public);
1263 }
1264 if (isset($this->model_pdf)) {
1265 $this->model_pdf = trim($this->model_pdf);
1266 }
1267 if (isset($this->import_key)) {
1268 $this->import_key = trim($this->import_key);
1269 }
1270
1271
1272 // Check parameters
1273 // Put here code to add control on parameters values
1274
1275 // Update request
1276 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1277 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1278 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1279 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1280 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1281 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1282 $sql .= " subtype=".(isset($this->subtype) ? (int) $this->subtype : "null").",";
1283 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1284 $sql .= " datec=".(dol_strlen((string) $this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1285 $sql .= " datef=".(dol_strlen((string) $this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1286 if (dol_strlen((string) $this->date_modification) != 0) {
1287 $sql .= " tms=".(dol_strlen((string) $this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').",";
1288 } elseif (dol_strlen((string) $this->tms) != 0) { // For backward compatibility
1289 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1290 }
1291 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1292 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1293 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1294 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1295 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1296 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1297 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1298 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1299 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1300 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1301 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1302 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1303 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1304 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0).",";
1305 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1306 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
1307 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1308 $sql .= " date_lim_reglement=".(dol_strlen((string) $this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1309 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1310 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1311 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1312 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1313 $sql .= " WHERE rowid=".((int) $this->id);
1314
1315 $this->db->begin();
1316
1317 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1318 $resql = $this->db->query($sql);
1319
1320 if (!$resql) {
1321 $error++;
1322
1323 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1324 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1325 } else {
1326 $this->errors[] = "Error ".$this->db->lasterror();
1327 }
1328 }
1329
1330 if (!$error) {
1331 $result = $this->insertExtraFields();
1332 if ($result < 0) {
1333 $error++;
1334 }
1335 }
1336
1337 if (!$error && !$notrigger) {
1338 // Call trigger
1339 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1340 if ($result < 0) {
1341 $error++;
1342 }
1343 // End call triggers
1344 }
1345
1346 // Commit or rollback
1347 if ($error) {
1348 foreach ($this->errors as $errmsg) {
1349 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1350 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1351 }
1352 $this->db->rollback();
1353 return -1 * $error;
1354 } else {
1355 $this->db->commit();
1356 return 1;
1357 }
1358 }
1359
1360 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1367 public function insert_discount($idremise)
1368 {
1369 // phpcs:enable
1370 global $conf, $langs;
1371
1372 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1373 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1374
1375 $this->db->begin();
1376
1377 $remise = new DiscountAbsolute($this->db);
1378 $result = $remise->fetch($idremise);
1379
1380 if ($result > 0) {
1381 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1382 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1383 $this->db->rollback();
1384 return -5;
1385 }
1386
1387 $facligne = new SupplierInvoiceLine($this->db);
1388 $facligne->fk_facture_fourn = $this->id;
1389 $facligne->fk_remise_except = $remise->id;
1390 $facligne->desc = $remise->description; // Line description
1391 $facligne->vat_src_code = $remise->vat_src_code;
1392 $facligne->tva_tx = $remise->tva_tx;
1393 $facligne->localtax1_tx = $remise->localtax1_tx;
1394 $facligne->localtax1_type = $remise->localtax1_type;
1395 $facligne->localtax2_tx = $remise->localtax1_tx;
1396 $facligne->localtax2_type = $remise->localtax1_type;
1397 $facligne->subprice = -(float) $remise->amount_ht;
1398 $facligne->fk_product = 0; // Predefined Product ID
1399 $facligne->product_type = 0;
1400 $facligne->qty = 1;
1401 $facligne->remise_percent = 0;
1402 $facligne->rang = -1;
1403 $facligne->info_bits = 2;
1404
1405 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1406 $facligne->rang = 1;
1407 $linecount = count($this->lines);
1408 for ($ii = 1; $ii <= $linecount; $ii++) {
1409 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1410 }
1411 }
1412
1413 // Get buy/cost price of invoice that is source of discount
1414 if ($remise->fk_invoice_supplier_source > 0) {
1415 $srcinvoice = new FactureFournisseur($this->db);
1416 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1417 $totalcostpriceofinvoice = 0;
1418 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1419 $formmargin = new FormMargin($this->db);
1420 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1421 $facligne->pa_ht = $arraytmp['pa_total'];
1422 }
1423
1424 $facligne->total_ht = -(float) $remise->amount_ht;
1425 $facligne->total_tva = -(float) $remise->amount_tva;
1426 $facligne->total_ttc = -(float) $remise->amount_ttc;
1427 $facligne->total_localtax1 = -(float) $remise->total_localtax1;
1428 $facligne->total_localtax2 = -(float) $remise->total_localtax2;
1429
1430 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1431 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1432 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1433 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1434
1435 $lineid = $facligne->insert();
1436 if ($lineid > 0) {
1437 $result = $this->update_price(1);
1438 if ($result > 0) {
1439 // Create link between discount and invoice line
1440 $result = $remise->link_to_invoice($lineid, 0);
1441 if ($result < 0) {
1442 $this->error = $remise->error;
1443 $this->db->rollback();
1444 return -4;
1445 }
1446
1447 $this->db->commit();
1448 return 1;
1449 } else {
1450 $this->error = $facligne->error;
1451 $this->db->rollback();
1452 return -1;
1453 }
1454 } else {
1455 $this->error = $facligne->error;
1456 $this->db->rollback();
1457 return -2;
1458 }
1459 } else {
1460 $this->db->rollback();
1461 return -3;
1462 }
1463 }
1464
1465
1473 public function delete(User $user, $notrigger = 0)
1474 {
1475 global $conf;
1476
1477 $rowid = $this->id;
1478
1479 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1480
1481 // TODO Test if there is at least on payment. If yes, refuse to delete.
1482
1483 $error = 0;
1484 $this->db->begin();
1485
1486 if (!$notrigger) {
1487 // Call trigger
1488 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1489 if ($result < 0) {
1490 $this->db->rollback();
1491 return -1;
1492 }
1493 // Fin appel triggers
1494 }
1495
1496 // Remove linked categories.
1497 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_invoice";
1498 $sql .= " WHERE fk_supplier_invoice = ".((int) $this->id);
1499
1500 $result = $this->db->query($sql);
1501 if (!$result) {
1502 $error++;
1503 $this->error = $this->db->lasterror();
1504 $this->errors[] = $this->error;
1505 }
1506
1507 if (!$error) {
1508 // If invoice was converted into a discount not yet consumed, we remove discount
1509 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1510 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1511 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1512 $resql = $this->db->query($sql);
1513
1514 // If invoice has consumned discounts
1515 $this->fetch_lines();
1516 $list_rowid_det = array();
1517 foreach ($this->lines as $key => $invoiceline) {
1518 $list_rowid_det[] = $invoiceline->id;
1519 }
1520
1521 // Consumned discounts are freed
1522 if (count($list_rowid_det)) {
1523 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1524 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1525 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1526
1527 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1528 if (!$this->db->query($sql)) {
1529 $error++;
1530 }
1531 }
1532 }
1533
1534 if (!$error) {
1535 $sql_main_table = MAIN_DB_PREFIX.'facture_fourn_det';
1536 $sql_ef_table = $sql_main_table."_extrafields";
1537 $sqlef = "DELETE FROM $sql_ef_table WHERE fk_object IN (SELECT rowid FROM ".$sql_main_table." WHERE fk_facture_fourn = ".((int) $rowid).")";
1538 $resqlef = $this->db->query($sqlef);
1539 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1540 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1541 $resql = $this->db->query($sql);
1542 if ($resqlef && $resql) {
1543 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1544 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1545 $resql2 = $this->db->query($sql);
1546 if (!$resql2) {
1547 $error++;
1548 }
1549 } else {
1550 $error++;
1551 }
1552 }
1553
1554 if (!$error) {
1555 // Delete linked object
1556 $res = $this->deleteObjectLinked();
1557 if ($res < 0) {
1558 $error++;
1559 }
1560 }
1561
1562 if (!$error) {
1563 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1564 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1565 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1566
1567 // We remove directory
1568 $ref = dol_sanitizeFileName($this->ref);
1569 if ($conf->fournisseur->facture->dir_output && !empty($ref)) {
1570 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1571
1572 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1573 $file = $dir."/".$ref.".pdf";
1574 if (file_exists($file)) {
1575 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1576 $this->error = 'ErrorFailToDeleteFile';
1577 $error++;
1578 }
1579 }
1580 if (file_exists($dir)) {
1581 $res = @dol_delete_dir_recursive($dir);
1582
1583 if (!$res) {
1584 $this->error = 'ErrorFailToDeleteDir';
1585 $error++;
1586 }
1587 }
1588 }
1589 }
1590
1591 // Remove extrafields
1592 if (!$error) {
1593 $result = $this->deleteExtraFields();
1594 if ($result < 0) {
1595 $error++;
1596 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1597 }
1598 }
1599
1600 if (!$error) {
1601 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1602 $this->db->commit();
1603 return 1;
1604 } else {
1605 $this->error = $this->db->lasterror();
1606 $this->db->rollback();
1607 return -$error;
1608 }
1609 }
1610
1611
1612 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1623 public function set_paid($user, $close_code = '', $close_note = '')
1624 {
1625 // phpcs:enable
1626 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1627 return $this->setPaid($user, $close_code, $close_note);
1628 }
1629
1638 public function setPaid($user, $close_code = '', $close_note = '')
1639 {
1640 $error = 0;
1641
1642 if ($this->paid != 1) {
1643 $this->db->begin();
1644
1645 $now = dol_now();
1646
1647 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1648
1649 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1650 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1651 if (!$close_code) {
1652 $sql .= ', paye=1';
1653 }
1654 if ($close_code) {
1655 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1656 }
1657 if ($close_note) {
1658 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1659 }
1660 $sql .= ', fk_user_closing = '.((int) $user->id);
1661 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1662 $sql .= ' WHERE rowid = '.((int) $this->id);
1663
1664 $resql = $this->db->query($sql);
1665 if ($resql) {
1666 // Call trigger
1667 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1668 if ($result < 0) {
1669 $error++;
1670 }
1671 // End call triggers
1672 } else {
1673 $error++;
1674 $this->error = $this->db->error();
1675 dol_print_error($this->db);
1676 }
1677
1678 if (!$error) {
1679 $this->db->commit();
1680 return 1;
1681 } else {
1682 $this->db->rollback();
1683 return -1;
1684 }
1685 } else {
1686 return 0;
1687 }
1688 }
1689
1690 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1701 public function set_unpaid($user)
1702 {
1703 // phpcs:enable
1704 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1705 return $this->setUnpaid($user);
1706 }
1707
1716 public function setUnpaid($user)
1717 {
1718 $error = 0;
1719
1720 $this->db->begin();
1721
1722 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1723 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1724 $sql .= ' date_closing=null,';
1725 $sql .= ' fk_user_closing=null';
1726 $sql .= ' WHERE rowid = '.((int) $this->id);
1727
1728 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1729 $resql = $this->db->query($sql);
1730 if ($resql) {
1731 // Call trigger
1732 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1733 if ($result < 0) {
1734 $error++;
1735 }
1736 // End call triggers
1737 } else {
1738 $error++;
1739 $this->error = $this->db->error();
1740 dol_print_error($this->db);
1741 }
1742
1743 if (!$error) {
1744 $this->db->commit();
1745 return 1;
1746 } else {
1747 $this->db->rollback();
1748 return -1;
1749 }
1750 }
1751
1762 public function setCanceled($user, $close_code = '', $close_note = '')
1763 {
1764 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1765
1766 $this->db->begin();
1767
1768 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1769 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1770 if ($close_code) {
1771 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1772 }
1773 if ($close_note) {
1774 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1775 }
1776 $sql .= " WHERE rowid = ".((int) $this->id);
1777
1778 $resql = $this->db->query($sql);
1779 if ($resql) {
1780 // Bound discounts are deducted from the invoice
1781 // as they have not been used since the invoice is abandoned.
1782 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1783 $sql .= ' SET fk_invoice_supplier = NULL';
1784 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1785
1786 $resql = $this->db->query($sql);
1787 if ($resql) {
1788 // Call trigger
1789 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1790 if ($result < 0) {
1791 $this->db->rollback();
1792 return -1;
1793 }
1794 // End call triggers
1795
1796 $this->db->commit();
1797 return 1;
1798 } else {
1799 $this->error = $this->db->error()." sql=".$sql;
1800 $this->db->rollback();
1801 return -1;
1802 }
1803 } else {
1804 $this->error = $this->db->error()." sql=".$sql;
1805 $this->db->rollback();
1806 return -2;
1807 }
1808 }
1809
1819 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1820 {
1821 global $mysoc, $conf, $langs;
1822
1823 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1824
1825 $now = dol_now();
1826
1827 $error = 0;
1828 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1829
1830 // Force to have object complete for checks
1831 $this->fetch_thirdparty();
1832 $this->fetch_lines();
1833
1834 // Check parameters
1835 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1836 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1837 return 0;
1838 }
1839 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ', '/').'/', $this->ref_supplier)) {
1840 $langs->load("errors");
1841 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1842 return -1;
1843 }
1844 if (count($this->lines) <= 0) {
1845 $langs->load("errors");
1846 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1847 return -1;
1848 }
1849
1850 // Check for mandatory fields in thirdparty (defined into setup)
1851 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1852 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1853 foreach ($array_to_check as $key) {
1854 $keymin = strtolower($key);
1855 if ($keymin == 'accountancy_code_supplier') {
1856 $keymin = 'code_compta_fournisseur';
1857 }
1858 if (!property_exists($this->thirdparty, $keymin)) {
1859 continue;
1860 }
1861 $vallabel = $this->thirdparty->$keymin;
1862
1863 $i = (int) preg_replace('/[^0-9]/', '', $key);
1864 if ($i > 0) {
1865 if ($this->thirdparty->isACompany()) {
1866 // Check for mandatory prof id (but only if country is other than ours)
1867 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1868 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1869 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1870 $langs->load("errors");
1871 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1872 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1873 return -1;
1874 }
1875 }
1876 }
1877 } else {
1878 if ($key == 'EMAIL') {
1879 // Check for mandatory
1880 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1881 $langs->load("errors");
1882 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1883 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1884 return -1;
1885 }
1886 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1887 // Check for mandatory
1888 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1889 $langs->load("errors");
1890 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1891 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1892 return -1;
1893 }
1894 }
1895 }
1896 }
1897 }
1898
1899 $this->db->begin();
1900
1901 // Define new ref
1902 if ($force_number) {
1903 $num = $force_number;
1904 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1905 $num = $this->getNextNumRef($this->thirdparty);
1906 } else {
1907 $num = (string) $this->ref;
1908 }
1909 $this->newref = dol_sanitizeFileName($num);
1910
1911 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1912 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1913 $sql .= " WHERE rowid = ".((int) $this->id);
1914
1915 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1916 $resql = $this->db->query($sql);
1917 if ($resql) {
1918 // If we increment the main product and its components upon validation of the supplier invoice
1919 if (isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1920 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1921 $langs->load("agenda");
1922
1923 $cpt = count($this->lines);
1924 for ($i = 0; $i < $cpt; $i++) {
1925 if ($this->lines[$i]->fk_product > 0) {
1926 $mouvP = new MouvementStock($this->db);
1927 $mouvP->origin = &$this;
1928 $mouvP->setOrigin($this->element, $this->id);
1929 // We increase stock for product
1930 $up_ht_disc = $this->lines[$i]->subprice;
1931 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1932 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1933 }
1935 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1936 } else {
1937 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1938 }
1939 if ($result < 0) {
1940 $this->setErrorsFromObject($mouvP);
1941 return -2;
1942 }
1943 }
1944 }
1945 }
1946
1947 // Triggers call
1948 if (empty($notrigger)) {
1949 // Call trigger
1950 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1951 if ($result < 0) {
1952 $error++;
1953 }
1954 // End call triggers
1955 }
1956
1957 if (!$error) {
1958 $this->oldref = $this->ref;
1959
1960 // Rename directory if dir was a temporary ref
1961 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1962 // Now we rename also files into index
1963 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->newref)."'";
1964 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1965 $resql = $this->db->query($sql);
1966 if (!$resql) {
1967 $error++;
1968 $this->error = $this->db->lasterror();
1969 }
1970 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->newref)."'";
1971 $sql .= " WHERE filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1972 $resql = $this->db->query($sql);
1973 if (!$resql) {
1974 $error++;
1975 $this->error = $this->db->lasterror();
1976 }
1977
1978 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1979 $oldref = dol_sanitizeFileName($this->ref);
1980 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1981 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1982 if (!$error && file_exists($dirsource)) {
1983 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1984
1985 if (@rename($dirsource, $dirdest)) {
1986 dol_syslog("Rename ok");
1987 // Rename docs starting with $oldref with $this->newref
1988 $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1989 foreach ($listoffiles as $fileentry) {
1990 $dirsource = $fileentry['name'];
1991 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1992 $dirsource = $fileentry['path'].'/'.$dirsource;
1993 $dirdest = $fileentry['path'].'/'.$dirdest;
1994 @rename($dirsource, $dirdest);
1995 }
1996 }
1997 }
1998 }
1999 }
2000
2001 // Set new ref and define current status
2002 if (!$error) {
2003 $this->ref = $this->newref;
2004 $this->statut = self::STATUS_VALIDATED;
2006 //$this->date_validation=$now; this is stored into log table
2007 }
2008
2009 if (!$error) {
2010 $this->db->commit();
2011 return 1;
2012 } else {
2013 $this->db->rollback();
2014 return -1;
2015 }
2016 } else {
2017 $this->error = $this->db->error();
2018 $this->db->rollback();
2019 return -1;
2020 }
2021 }
2022
2031 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
2032 {
2033 // phpcs:enable
2034 global $conf, $langs;
2035
2036 $error = 0;
2037
2038 if ($this->status == self::STATUS_DRAFT) {
2039 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
2040 return 0;
2041 }
2042
2043 dol_syslog(__METHOD__, LOG_DEBUG);
2044
2045 $this->db->begin();
2046
2047 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2048 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2049 $sql .= " WHERE rowid = ".((int) $this->id);
2050
2051 $result = $this->db->query($sql);
2052 if ($result) {
2053 $this->oldcopy = clone $this;
2054
2055 // If we increment the main product and its components upon supplier invoice validation, we decrement upon customer invoice validation
2056 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2057 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2058 $langs->load("agenda");
2059
2060 $cpt = count($this->lines);
2061 for ($i = 0; $i < $cpt; $i++) {
2062 if ($this->lines[$i]->fk_product > 0) {
2063 $mouvP = new MouvementStock($this->db);
2064 $mouvP->origin = &$this;
2065 $mouvP->setOrigin($this->element, $this->id);
2066 // We increase stock for product
2068 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2069 } else {
2070 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2071 }
2072 }
2073 }
2074 }
2075 // Triggers call
2076 if (empty($notrigger)) {
2077 // Call trigger
2078 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2079 if ($result < 0) {
2080 $error++;
2081 }
2082 // End call triggers
2083 }
2084 if ($error == 0) {
2085 $this->db->commit();
2086 return 1;
2087 } else {
2088 $this->db->rollback();
2089 return -1;
2090 }
2091 } else {
2092 $this->error = $this->db->error();
2093 $this->db->rollback();
2094 return -1;
2095 }
2096 }
2097
2098
2133 public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = 0, $date_end = 0, $fk_code_ventilation = 0, $info_bits = 0, $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = 0, $array_options = [], $fk_unit = null, $origin_id = 0, $pu_devise = 0, $ref_supplier = '', $special_code = 0, $fk_parent_line = 0, $fk_remise_except = 0, $origin_type = '')
2134 {
2135 global $langs, $mysoc;
2136
2137 dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$fk_code_ventilation,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG);
2138 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2139
2140 if ($this->status == self::STATUS_DRAFT) {
2141 // Clean parameters
2142 if (empty($remise_percent)) {
2143 $remise_percent = 0;
2144 }
2145 if (empty($qty)) {
2146 $qty = 0;
2147 }
2148 if (empty($info_bits)) {
2149 $info_bits = 0;
2150 }
2151 if (empty($rang)) {
2152 $rang = 0;
2153 }
2154 if (empty($fk_code_ventilation)) {
2155 $fk_code_ventilation = 0;
2156 }
2157 if (empty($txtva)) {
2158 $txtva = 0;
2159 }
2160 if (empty($txlocaltax1)) {
2161 $txlocaltax1 = 0;
2162 }
2163 if (empty($txlocaltax2)) {
2164 $txlocaltax2 = 0;
2165 }
2166
2167 $remise_percent = price2num($remise_percent);
2168 $qty = price2num($qty);
2169 $pu = price2num($pu);
2170 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2171 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2172 }
2173 $txlocaltax1 = price2num($txlocaltax1);
2174 $txlocaltax2 = price2num($txlocaltax2);
2175
2176 if ($date_start && $date_end && $date_start > $date_end) {
2177 $langs->load("errors");
2178 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2179 return -1;
2180 }
2181
2182 $this->db->begin();
2183
2184 if ($fk_product > 0) {
2185 if (getDolGlobalInt('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2186 // Check quantity is enough
2187 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2188 $prod = new ProductFournisseur($this->db);
2189 if ($prod->fetch($fk_product) > 0) {
2190 $product_type = $prod->type;
2191 $label = $prod->label;
2192 $fk_prod_fourn_price = 0;
2193
2194 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2195 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2196 $result = $prod->get_buyprice($fk_prod_fourn_price, (float) $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
2197 if ($result > 0) {
2198 if (empty($pu)) {
2199 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2200 }
2201 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2202 // is remise percent not keyed but present for the product we add it
2203 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2204 $remise_percent = $prod->remise_percent;
2205 }
2206 }
2207 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2208 $langs->load("errors");
2209 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2210 $this->db->rollback();
2211 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2212 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2213 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2214 return -1;
2215 }
2216 if ($result == -1) {
2217 $langs->load("errors");
2218 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2219 $this->db->rollback();
2220 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2221 return -1;
2222 }
2223 if ($result < -1) {
2224 $this->error = $prod->error;
2225 $this->db->rollback();
2226 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2227 return -1;
2228 }
2229 } else {
2230 $this->error = $prod->error;
2231 $this->db->rollback();
2232 return -1;
2233 }
2234 }
2235 } else {
2236 $product_type = $type;
2237 }
2238
2239 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2240 $pu = 0;
2241 }
2242
2243 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2244
2245 // Clean vat code
2246 $reg = array();
2247 $vat_src_code = '';
2248 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2249 $vat_src_code = $reg[1];
2250 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2251 }
2252
2253 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2254 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2255 // and this is done at the line level, which has its own VAT rate
2256
2257 $tabprice = calcul_price_total((float) $qty, $pu, (float) $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2258 $total_ht = $tabprice[0];
2259 $total_tva = $tabprice[1];
2260 $total_ttc = $tabprice[2];
2261 $total_localtax1 = $tabprice[9];
2262 $total_localtax2 = $tabprice[10];
2263 $pu_ht = $tabprice[3];
2264
2265 // MultiCurrency
2266 $multicurrency_total_ht = $tabprice[16];
2267 $multicurrency_total_tva = $tabprice[17];
2268 $multicurrency_total_ttc = $tabprice[18];
2269 $pu_ht_devise = $tabprice[19];
2270
2271 // Check parameters
2272 if ($type < 0) {
2273 return -1;
2274 }
2275
2276 if ($rang < 0) {
2277 $rangmax = $this->line_max();
2278 $rang = $rangmax + 1;
2279 }
2280
2281 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2282 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2283 $apply_abs_price_on_credit_note = false;
2284 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2285 $apply_abs_price_on_credit_note = true;
2286 }
2287
2288 // Insert line
2289 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2290
2291 $supplierinvoiceline->context = $this->context;
2292
2293 $supplierinvoiceline->fk_facture_fourn = $this->id;
2294 //$supplierinvoiceline->label=$label; // deprecated
2295 $supplierinvoiceline->desc = $desc;
2296 $supplierinvoiceline->ref_supplier = $ref_supplier;
2297
2298 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2299
2300 $supplierinvoiceline->subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2301 // Only keep the TTC unit price when the line was entered including tax, so it acts as a reliable "TTC entry mode" marker.
2302 $supplierinvoiceline->subprice_ttc = ($price_base_type === 'TTC') ? ($apply_abs_price_on_credit_note ? -abs((float) $tabprice[5]) : (float) $tabprice[5]) : 0;
2303
2304 $supplierinvoiceline->vat_src_code = $vat_src_code;
2305 $supplierinvoiceline->tva_tx = $txtva;
2306 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2307 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2308 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2309 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2310
2311 $supplierinvoiceline->total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht); // For credit note and if qty is negative, total is negative
2312 $supplierinvoiceline->total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva); // For credit note and if qty is negative, total is negative
2313 $supplierinvoiceline->total_localtax1 = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_localtax1) : (float) $total_localtax1); // For credit note and if qty is negative, total is negative
2314 $supplierinvoiceline->total_localtax2 = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_localtax2) : (float) $total_localtax2); // For credit note and if qty is negative, total is negative
2315 $supplierinvoiceline->total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc); // For credit note and if qty is negative, total is negative
2316
2317 $supplierinvoiceline->fk_product = $fk_product;
2318 $supplierinvoiceline->product_type = $type;
2319 $supplierinvoiceline->remise_percent = $remise_percent;
2320 $supplierinvoiceline->date_start = $date_start;
2321 $supplierinvoiceline->date_end = $date_end;
2322 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2323 $supplierinvoiceline->rang = $rang;
2324 $supplierinvoiceline->info_bits = $info_bits;
2325 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2326
2327
2328 $supplierinvoiceline->special_code = (int) $special_code;
2329 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2330 $supplierinvoiceline->origin = $this->origin;
2331 $supplierinvoiceline->origin_type = $origin_type;
2332 $supplierinvoiceline->origin_id = $origin_id;
2333 $supplierinvoiceline->fk_unit = $fk_unit;
2334
2335 // Multicurrency
2336 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2337 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2338 $supplierinvoiceline->multicurrency_subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht_devise) : (float) $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2339
2340 $supplierinvoiceline->multicurrency_total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_ht) : (float) $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2341 $supplierinvoiceline->multicurrency_total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_tva) : (float) $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2342 $supplierinvoiceline->multicurrency_total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_ttc) : (float) $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2343
2344 if (is_array($array_options) && count($array_options) > 0) {
2345 $supplierinvoiceline->array_options = $array_options;
2346 }
2347
2348 $result = $supplierinvoiceline->insert($notrigger);
2349 if ($result > 0) {
2350 // Update denormalized fields at the order level
2351 $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // The addline method is designed to add line from user input so total calculation with update_price must be done using 'auto' mode.
2352
2353 if ($result > 0) {
2354 if (!isset($this->context['createfromclone'])) {
2355 if (!empty($fk_parent_line)) {
2356 // Always reorder if child line
2357 $this->line_order(true, 'DESC');
2358 } elseif ($rang > 0 && $rang <= count($this->lines)) {
2359 // Update all rank of all other lines starting from the same $ranktouse
2360 $linecount = count($this->lines);
2361 for ($ii = $rang; $ii <= $linecount; $ii++) {
2362 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2363 }
2364 }
2365
2366 $this->lines[] = $supplierinvoiceline;
2367 }
2368
2369 $this->db->commit();
2370 return $supplierinvoiceline->id;
2371 } else {
2372 $this->error = $this->db->error();
2373 $this->db->rollback();
2374 return -1;
2375 }
2376 } else {
2377 $this->error = $supplierinvoiceline->error;
2378 $this->errors = $supplierinvoiceline->errors;
2379 $this->db->rollback();
2380 return -2;
2381 }
2382 } else {
2383 return 0;
2384 }
2385 }
2386
2412 public function updateline($id, $desc, $pu, $vatrate, $txlocaltax1 = 0, $txlocaltax2 = 0, $qty = 1, $idproduct = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $remise_percent = 0, $notrigger = 0, $date_start = '', $date_end = '', $array_options = [], $fk_unit = null, $pu_devise = 0, $ref_supplier = '', $rang = 0)
2413 {
2414 global $mysoc, $langs;
2415
2416 dol_syslog(get_class($this)."::updateline $id,$desc,$pu,$vatrate,$qty,$idproduct,$price_base_type,$info_bits,$type,$remise_percent,$notrigger,$date_start,$date_end,$fk_unit,$pu_devise,$ref_supplier", LOG_DEBUG);
2417 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2418
2419 $pu = price2num($pu);
2420 $qty = price2num($qty);
2421 $remise_percent = (float) price2num($remise_percent);
2422 $pu_devise = price2num($pu_devise);
2423
2424 // Check parameters
2425 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2426 if ($type < 0) {
2427 return -1;
2428 }
2429
2430 if ($date_start && $date_end && $date_start > $date_end) {
2431 $langs->load("errors");
2432 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2433 return -1;
2434 }
2435
2436 // Clean parameters
2437 if (empty($vatrate)) {
2438 $vatrate = 0;
2439 }
2440 if (empty($txlocaltax1)) {
2441 $txlocaltax1 = 0;
2442 }
2443 if (empty($txlocaltax2)) {
2444 $txlocaltax2 = 0;
2445 }
2446
2447 $txlocaltax1 = (float) price2num($txlocaltax1);
2448 $txlocaltax2 = (float) price2num($txlocaltax2);
2449
2450 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2451 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2452 // and this is done at the line level, which has its own VAT rate
2453
2454 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2455
2456 $reg = array();
2457
2458 // Clean vat code
2459 $vat_src_code = '';
2460 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2461 $vat_src_code = $reg[1];
2462 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2463 }
2464
2465 $tabprice = calcul_price_total((float) $qty, (float) $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_devise);
2466 $total_ht = $tabprice[0];
2467 $total_tva = $tabprice[1];
2468 $total_ttc = $tabprice[2];
2469 $pu_ht = $tabprice[3];
2470 $pu_tva = $tabprice[4];
2471 $pu_ttc = $tabprice[5];
2472 $total_localtax1 = $tabprice[9];
2473 $total_localtax2 = $tabprice[10];
2474
2475 // MultiCurrency
2476 $multicurrency_total_ht = $tabprice[16];
2477 $multicurrency_total_tva = $tabprice[17];
2478 $multicurrency_total_ttc = $tabprice[18];
2479 $pu_ht_devise = $tabprice[19];
2480
2481 if (empty($info_bits)) {
2482 $info_bits = 0;
2483 }
2484
2485 // Fetch current line from the database and then clone the object and set it in $oldline property
2486 $line = new SupplierInvoiceLine($this->db);
2487 $line->fetch($id);
2488 $line->fetch_optionals();
2489
2490 $staticline = clone $line;
2491
2492 if ($idproduct) {
2493 $product = new Product($this->db);
2494 $result = $product->fetch($idproduct);
2495 $product_type = $product->type;
2496 } else {
2497 $idproduct = $staticline->fk_product;
2498 $product_type = $type;
2499 }
2500
2501 $line->oldline = $staticline;
2502 $line->context = $this->context;
2503
2504 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2505 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2506 $apply_abs_price_on_credit_note = false;
2507 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2508 $apply_abs_price_on_credit_note = true;
2509 }
2510
2511 $line->desc = $desc;
2512
2513 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2514
2515 $line->subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2516 // Only keep the TTC unit price when the line was entered including tax, so it acts as a reliable "TTC entry mode" marker.
2517 $line->subprice_ttc = ($price_base_type === 'TTC') ? ($apply_abs_price_on_credit_note ? -abs((float) $pu_ttc) : (float) $pu_ttc) : 0; // For credit note, unit price always negative, always positive otherwise
2518
2519 $line->remise_percent = $remise_percent;
2520 $line->ref_supplier = $ref_supplier;
2521
2522 $line->date_start = $date_start;
2523 $line->date_end = $date_end;
2524
2525 $line->vat_src_code = $vat_src_code;
2526 $line->tva_tx = $vatrate;
2527 $line->localtax1_tx = $txlocaltax1;
2528 $line->localtax2_tx = $txlocaltax2;
2529 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2530 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2531
2532 $line->total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht);
2533 $line->total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva);
2534 $line->total_localtax1 = (float) $total_localtax1;
2535 $line->total_localtax2 = (float) $total_localtax2;
2536 $line->total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc);
2537
2538 $line->fk_product = $idproduct;
2539 $line->product_type = $product_type;
2540 $line->info_bits = $info_bits;
2541 $line->fk_unit = $fk_unit;
2542 $line->rang = $rang;
2543
2544 if (is_array($array_options) && count($array_options) > 0) {
2545 // We replace values in this->line->array_options only for entries defined into $array_options
2546 foreach ($array_options as $key => $value) {
2547 $line->array_options[$key] = $array_options[$key];
2548 }
2549 }
2550
2551 // Multicurrency
2552 $line->multicurrency_subprice = (float) $pu_ht_devise;
2553 $line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2554 $line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2555 $line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2556
2557 $res = $line->update($notrigger);
2558
2559 if ($res < 1) {
2560 $this->setErrorsFromObject($line);
2561 } else {
2562 // Update total price into invoice record
2563 $res = $this->update_price(1, 'auto', 0, $this->thirdparty);
2564 }
2565
2566 return $res;
2567 }
2568
2576 public function deleteLine($rowid, $notrigger = 0)
2577 {
2578 if (!$rowid) {
2579 $rowid = $this->id;
2580 }
2581
2582 $this->db->begin();
2583
2584 // Free the discount linked to a line of invoice
2585 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2586 $sql .= ' SET fk_invoice_supplier_line = NULL';
2587 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2588
2589 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2590 $result = $this->db->query($sql);
2591 if (!$result) {
2592 $this->error = $this->db->error();
2593 $this->db->rollback();
2594 return -2;
2595 }
2596
2597 $line = new SupplierInvoiceLine($this->db);
2598
2599 if ($line->fetch($rowid) < 1) {
2600 return -1;
2601 }
2602
2603 $res = $line->delete($notrigger);
2604
2605 if ($res < 1) {
2606 $this->errors[] = $line->error;
2607 $this->db->rollback();
2608 return -3;
2609 } else {
2610 $res = $this->update_price(1);
2611
2612 if ($res > 0) {
2613 $this->db->commit();
2614 return 1;
2615 } else {
2616 $this->db->rollback();
2617 $this->error = $this->db->lasterror();
2618 return -4;
2619 }
2620 }
2621 }
2622
2623
2630 public function info($id)
2631 {
2632 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2633 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2634 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2635 $sql .= ' WHERE c.rowid = '.((int) $id);
2636
2637 $result = $this->db->query($sql);
2638 if ($result) {
2639 if ($this->db->num_rows($result)) {
2640 $obj = $this->db->fetch_object($result);
2641
2642 $this->id = $obj->rowid;
2643
2644 $this->user_creation_id = $obj->fk_user_author;
2645 $this->user_validation_id = $obj->fk_user_valid;
2646 $this->user_modification_id = $obj->fk_user_modif;
2647 $this->date_creation = $this->db->jdate($obj->datec);
2648 $this->date_modification = $this->db->jdate($obj->datem);
2649 //$this->date_validation = $obj->datev; // This field is not available. Should be store into log table and using this function should be replaced with showing content of log (like for supplier orders)
2650 }
2651 $this->db->free($result);
2652 } else {
2653 dol_print_error($this->db);
2654 }
2655 }
2656
2657 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2666 public function list_replacable_supplier_invoices($socid = 0)
2667 {
2668 // phpcs:enable
2669 global $conf;
2670
2671 $return = array();
2672
2673 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2674 $sql .= " ff.rowid as rowidnext";
2675 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2676 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2677 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2678 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2679 $sql .= " AND f.entity = ".((int) $conf->entity);
2680 $sql .= " AND f.paye = 0"; // Not closed completely
2681 $sql .= " AND pf.fk_paiementfourn IS NULL"; // No payment already done
2682 $sql .= " AND ff.fk_statut IS NULL"; // Return true (is null) if it is not a replacing invoice (we can't replace a replacing invoice)
2683 if ($socid > 0) {
2684 $sql .= " AND f.fk_soc = ".((int) $socid);
2685 }
2686 $sql .= " ORDER BY f.ref";
2687
2688 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2689 $resql = $this->db->query($sql);
2690 if ($resql) {
2691 while ($obj = $this->db->fetch_object($resql)) {
2692 $return[$obj->rowid] = array(
2693 'id' => $obj->rowid,
2694 'ref' => $obj->ref,
2695 'status' => $obj->fk_statut
2696 );
2697 }
2698 //print_r($return);
2699 return $return;
2700 } else {
2701 $this->error = $this->db->error();
2702 return -1;
2703 }
2704 }
2705
2706 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2715 public function list_qualified_avoir_supplier_invoices($socid = 0)
2716 {
2717 // phpcs:enable
2718 global $conf;
2719
2720 $return = array();
2721
2722 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye as paid, pf.fk_paiementfourn";
2723 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2724 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2725 $sql .= " WHERE f.entity = ".((int) $conf->entity);
2726 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2727 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2728 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2729 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2730 if ($socid > 0) {
2731 $sql .= " AND f.fk_soc = ".((int) $socid);
2732 }
2733 $sql .= " ORDER BY f.ref";
2734
2735 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2736 $resql = $this->db->query($sql);
2737 if ($resql) {
2738 while ($obj = $this->db->fetch_object($resql)) {
2739 $qualified = 0;
2740 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2741 $qualified = 1;
2742 }
2743 if ($obj->fk_statut == self::STATUS_CLOSED) {
2744 $qualified = 1;
2745 }
2746 if ($qualified) {
2747 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2748 $return[$obj->rowid] = array('ref' => (string) $obj->ref, 'status' => (int) $obj->fk_statut, 'type' => (int) $obj->type, 'paid' => (int) $obj->paid, 'paye' => (int) $obj->paid, 'paymentornot' => (int) $paymentornot);
2749 }
2750 }
2751
2752 return $return;
2753 } else {
2754 $this->error = $this->db->error();
2755 return -1;
2756 }
2757 }
2758
2759 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2766 public function load_board($user)
2767 {
2768 // phpcs:enable
2769 global $conf, $langs;
2770
2771 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2772 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2773 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2774 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2775 }
2776 $sql .= ' WHERE ff.paye = 0';
2777 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2778 $sql .= " AND ff.entity = ".((int) $conf->entity);
2779 if ($user->socid) {
2780 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2781 }
2782
2783 $resql = $this->db->query($sql);
2784 if ($resql) {
2785 $langs->load("bills");
2786 $now = dol_now();
2787
2788 $response = new WorkboardResponse();
2789 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2790 $response->label = $langs->trans("SupplierBillsToPay");
2791 $response->labelShort = $langs->trans("StatusToPay");
2792
2793 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2794 $response->img = img_object($langs->trans("Bills"), "bill");
2795
2796 $facturestatic = new FactureFournisseur($this->db);
2797
2798 while ($obj = $this->db->fetch_object($resql)) {
2799 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2800 $facturestatic->statut = $obj->status; // For backward compatibility
2801 $facturestatic->status = $obj->status;
2802
2803 $response->nbtodo++;
2804 $response->total += (float) $obj->total_ht;
2805
2806 if ($facturestatic->hasDelay()) {
2807 $response->nbtodolate++;
2808 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2809 }
2810 }
2811
2812 $this->db->free($resql);
2813 return $response;
2814 } else {
2815 dol_print_error($this->db);
2816 $this->error = $this->db->error();
2817 return -1;
2818 }
2819 }
2820
2828 public function getTooltipContentArray($params)
2829 {
2830 global $conf, $langs, $mysoc;
2831
2832 $langs->load('bills');
2833
2834 $datas = [];
2835 $moretitle = $params['moretitle'] ?? '';
2836
2837 $picto = $this->picto;
2838 if ($this->type == self::TYPE_REPLACEMENT) {
2839 $picto .= 'r'; // Replacement invoice
2840 }
2841 if ($this->type == self::TYPE_CREDIT_NOTE) {
2842 $picto .= 'a'; // Credit note
2843 }
2844 if ($this->type == self::TYPE_DEPOSIT) {
2845 $picto .= 'd'; // Deposit invoice
2846 }
2847
2848 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2849 if ($this->type == self::TYPE_REPLACEMENT) {
2850 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplacement").'</u>';
2851 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2852 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2853 } elseif ($this->type == self::TYPE_DEPOSIT) {
2854 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2855 }
2856 if (isset($this->status)) {
2857 $alreadypaid = -1;
2858 if (isset($this->totalpaid)) {
2859 $alreadypaid = $this->totalpaid;
2860 }
2861
2862 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2863 }
2864 if ($moretitle) {
2865 $datas['picto'] .= ' - '.$moretitle;
2866 }
2867 if (!empty($this->ref)) {
2868 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2869 }
2870 if (!empty($this->ref_supplier)) {
2871 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2872 }
2873 if (empty($params['nofetch'])) {
2874 $langs->load('companies');
2875 if (empty($this->thirdparty)) {
2876 $this->fetch_thirdparty();
2877 }
2878 if (is_object($this->thirdparty)) {
2879 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
2880 }
2881 }
2882 if (!empty($this->label)) {
2883 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2884 }
2885 if (!empty($this->date)) {
2886 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2887 }
2888 if (!empty($this->date_echeance)) {
2889 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2890 }
2891 if (!empty($this->total_ht)) {
2892 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2893 }
2894 if (!empty($this->total_tva)) {
2895 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2896 }
2897 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2898 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2899 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2900 }
2901 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2902 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2903 }
2904 if (!empty($this->revenuestamp)) {
2905 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2906 }
2907 if (!empty($this->total_ttc)) {
2908 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2909 }
2910 return $datas;
2911 }
2912
2926 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2927 {
2928 global $langs, $user, $hookmanager;
2929
2930 $result = '';
2931
2932 if ($option == 'withdraw') {
2933 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2934 } elseif ($option == 'document') {
2935 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2936 } else {
2937 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2938 }
2939
2940 if ($short) {
2941 return $url;
2942 }
2943
2944 if ($option !== 'nolink') {
2945 // Add param to save lastsearch_values or not
2946 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2947 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2948 $add_save_lastsearch_values = 1;
2949 }
2950 if ($add_save_lastsearch_values) {
2951 $url .= '&save_lastsearch_values=1';
2952 }
2953 }
2954
2955 $picto = $this->picto;
2956 if ($this->type == self::TYPE_REPLACEMENT) {
2957 $picto .= 'r'; // Replacement invoice
2958 }
2959 if ($this->type == self::TYPE_CREDIT_NOTE) {
2960 $picto .= 'a'; // Credit note
2961 }
2962 if ($this->type == self::TYPE_DEPOSIT) {
2963 $picto .= 'd'; // Deposit invoice
2964 }
2965
2966 $params = [
2967 'id' => $this->id,
2968 'objecttype' => $this->element,
2969 'option' => $option,
2970 'moretitle' => $moretitle,
2971 ];
2972 $classfortooltip = 'classfortooltip';
2973 $dataparams = '';
2974 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2975 $classfortooltip = 'classforajaxtooltip';
2976 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2977 $label = '';
2978 } else {
2979 $label = implode($this->getTooltipContentArray($params));
2980 }
2981
2982 $ref = $this->ref;
2983 if (empty($ref)) {
2984 $ref = $this->id;
2985 }
2986
2987 $linkclose = '';
2988 if (empty($notooltip)) {
2989 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2990 $label = $langs->trans("ShowSupplierInvoice");
2991 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2992 }
2993 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2994 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2995 }
2996
2997 $linkstart = '<a href="'.$url.'"';
2998 $linkstart .= $linkclose.'>';
2999 $linkend = '</a>';
3000
3001 $result .= $linkstart;
3002 if ($withpicto) {
3003 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
3004 }
3005 if ($withpicto != 2) {
3006 $result .= ($max ? dol_trunc($ref, $max) : $ref);
3007 }
3008 $result .= $linkend;
3009
3010 if ($addlinktonotes) {
3011 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3012 if ($txttoshow) {
3013 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3014 $result .= ' <span class="note inline-block">';
3015 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3016 $result .= img_picto('', 'note');
3017 $result .= '</a>';
3018 $result .= '</span>';
3019 }
3020 }
3021 global $action;
3022 $hookmanager->initHooks(array($this->element . 'dao'));
3023 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3024 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3025 if ($reshook > 0) {
3026 $result = $hookmanager->resPrint;
3027 } else {
3028 $result .= $hookmanager->resPrint;
3029 }
3030 return $result;
3031 }
3032
3043 public function setCategories($categories)
3044 {
3045 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3046 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_INVOICE);
3047 }
3048
3057 public function getNextNumRef($soc, $mode = 'next')
3058 {
3059 global $db, $langs, $conf;
3060 $langs->load("orders");
3061
3062 // Clean parameters (if not defined or using deprecated value)
3063 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
3064 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
3065 }
3066
3067 $mybool = false;
3068
3069 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
3070 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
3071
3072 // Include file with class
3073 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3074
3075 foreach ($dirmodels as $reldir) {
3076 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
3077
3078 // Load file with numbering class (if found)
3079 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3080 }
3081
3082 if (!$mybool) {
3083 dol_print_error(null, "Failed to include file ".$file);
3084 return '';
3085 }
3086
3087 $obj = new $classname();
3088 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3089 $numref = "";
3090 $numref = $obj->getNextValue($soc, $this, $mode);
3091
3092 if ($numref != "") {
3093 return $numref;
3094 } else {
3095 $this->error = $obj->error;
3096 return -1;
3097 }
3098 }
3099
3100
3109 public function initAsSpecimen($option = '')
3110 {
3111 global $langs, $conf;
3112 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3113
3114 $now = dol_now();
3115
3116 // Load array of products prodids
3117 $num_prods = 0;
3118 $prodids = array();
3119
3120 $sql = "SELECT rowid";
3121 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3122 $sql .= " WHERE entity IN (".getEntity('product').")";
3123 $sql .= $this->db->plimit(100);
3124
3125 $resql = $this->db->query($sql);
3126 if ($resql) {
3127 $num_prods = $this->db->num_rows($resql);
3128 $i = 0;
3129 while ($i < $num_prods) {
3130 $i++;
3131 $row = $this->db->fetch_row($resql);
3132 $prodids[$i] = $row[0];
3133 }
3134 }
3135
3136 // Initialise parameters
3137 $this->id = 0;
3138 $this->ref = 'SPECIMEN';
3139 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3140 $this->specimen = 1;
3141 $this->socid = 1;
3142 $this->date = $now;
3143 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3144 $this->cond_reglement_code = 'RECEP';
3145 $this->mode_reglement_code = 'CHQ';
3146
3147 $this->note_public = 'This is a comment (public)';
3148 $this->note_private = 'This is a comment (private)';
3149
3150 $this->multicurrency_tx = 1;
3151 $this->multicurrency_code = $conf->currency;
3152
3153 $xnbp = 0;
3154 if (empty($option) || $option != 'nolines') {
3155 // Lines
3156 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
3157 while ($xnbp < $nbp) {
3158 $line = new SupplierInvoiceLine($this->db);
3159 $line->desc = $langs->trans("Description")." ".$xnbp;
3160 $line->qty = 1;
3161 $line->subprice = 100;
3162 $line->price = 100;
3163 $line->tva_tx = 19.6;
3164 $line->localtax1_tx = 0;
3165 $line->localtax2_tx = 0;
3166 if ($xnbp == 2) {
3167 $line->total_ht = 50;
3168 $line->total_ttc = 59.8;
3169 $line->total_tva = 9.8;
3170 $line->remise_percent = 50;
3171 } else {
3172 $line->total_ht = 100;
3173 $line->total_ttc = 119.6;
3174 $line->total_tva = 19.6;
3175 $line->remise_percent = 0;
3176 }
3177
3178 if ($num_prods > 0) {
3179 $prodid = mt_rand(1, $num_prods);
3180 $line->fk_product = $prodids[$prodid];
3181 }
3182 $line->product_type = 0;
3183
3184 $this->lines[$xnbp] = $line;
3185
3186 $this->total_ht += $line->total_ht;
3187 $this->total_tva += $line->total_tva;
3188 $this->total_ttc += $line->total_ttc;
3189
3190 $xnbp++;
3191 }
3192 }
3193
3194 $this->total_ht = $xnbp * 100;
3195 $this->total_tva = $xnbp * 19.6;
3196 $this->total_ttc = $xnbp * 119.6;
3197
3198 return 1;
3199 }
3200
3206 public function loadStateBoard()
3207 {
3208 global $conf, $user;
3209
3210 $this->nb = array();
3211
3212 $clause = "WHERE";
3213
3214 $sql = "SELECT count(f.rowid) as nb";
3215 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3216 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3217 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3218 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3219 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3220 $clause = "AND";
3221 }
3222 $sql .= " ".$clause." f.entity = ".((int) $conf->entity);
3223
3224 $resql = $this->db->query($sql);
3225 if ($resql) {
3226 while ($obj = $this->db->fetch_object($resql)) {
3227 $this->nb["supplier_invoices"] = $obj->nb;
3228 }
3229 $this->db->free($resql);
3230 return 1;
3231 } else {
3232 dol_print_error($this->db);
3233 $this->error = $this->db->error();
3234 return -1;
3235 }
3236 }
3237
3246 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3247 {
3248 global $conf, $langs, $hookmanager;
3249
3250 $error = 0;
3251
3252 $object = new FactureFournisseur($this->db);
3253
3254 $this->db->begin();
3255
3256 // Load source object
3257 $object->fetch($fromid);
3258 $objFrom = clone $object;
3259
3260 $object->id = 0;
3261 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3262 $object->status = self::STATUS_DRAFT;
3263
3264 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3265
3266 // Clear fields
3267 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3268 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3269 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3270 $object->fk_facture_source = 0;
3271 $object->date_creation = '';
3272 $object->date_validation = '';
3273 $object->date = (empty($this->date) ? dol_now() : $this->date);
3274 $object->ref_client = '';
3275 $object->close_code = '';
3276 $object->close_note = '';
3277 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3278 $object->note_private = '';
3279 $object->note_public = '';
3280 }
3281
3282 $object->date_echeance = $object->calculate_date_lim_reglement();
3283
3284 // Loop on each line of new invoice
3285 foreach ($object->lines as $i => $line) {
3286 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3287 unset($object->lines[$i]);
3288 }
3289 }
3290
3291 // Create clone
3292 $object->context['createfromclone'] = 'createfromclone';
3293 $result = $object->create($user);
3294
3295 // Other options
3296 if ($result < 0) {
3297 $this->error = $object->error;
3298 $this->errors = $object->errors;
3299 $error++;
3300 }
3301
3302 if (!$error) {
3303 // Hook of thirdparty module
3304 if (is_object($hookmanager)) {
3305 $parameters = array('objFrom' => $objFrom);
3306 $action = '';
3307 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
3308 if ($reshook < 0) {
3309 $this->setErrorsFromObject($hookmanager);
3310 $error++;
3311 }
3312 }
3313 }
3314
3315 unset($object->context['createfromclone']);
3316
3317 // End
3318 if (!$error) {
3319 $this->db->commit();
3320 return $object->id;
3321 } else {
3322 $this->db->rollback();
3323 return -1;
3324 }
3325 }
3326
3338 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3339 {
3340 global $langs;
3341
3342 $langs->load("suppliers");
3343 $outputlangs->load("products");
3344
3345 // Set the model on the model name to use
3346 if (empty($modele)) {
3347 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3348 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3349 } else {
3350 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3351 }
3352 } elseif ($modele == 'auto') {
3353 $modele = 'canelle';
3354 }
3355
3356 if (empty($modele)) {
3357 return 0;
3358 } else {
3359 $modelpath = "core/modules/supplier_invoice/doc/";
3360
3361 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3362 }
3363 }
3364
3369 public function getRights()
3370 {
3371 global $user;
3372
3373 return $user->hasRight("fournisseur", "facture");
3374 }
3375
3384 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3385 {
3386 $tables = array(
3387 'facture_fourn'
3388 );
3389
3390 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3391 }
3392
3401 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3402 {
3403 $tables = array(
3404 'facture_fourn_det'
3405 );
3406
3407 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3408 }
3409
3415 public function hasDelay()
3416 {
3417 global $conf;
3418
3419 $now = dol_now();
3420
3421 if (!$this->date_echeance) {
3422 return false;
3423 }
3424
3425 $status = isset($this->status) ? $this->status : $this->statut;
3426
3427 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3428 }
3429
3435 public function isCreditNoteUsed()
3436 {
3437 $isUsed = false;
3438
3439 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3440 $resql = $this->db->query($sql);
3441 if (!empty($resql)) {
3442 $obj = $this->db->fetch_object($resql);
3443 if (!empty($obj->fk_invoice_supplier)) {
3444 $isUsed = true;
3445 }
3446 }
3447
3448 return $isUsed;
3449 }
3457 public function getKanbanView($option = '', $arraydata = null)
3458 {
3459 global $langs;
3460
3461 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3462
3463 $picto = $this->picto;
3464 if ($this->type == self::TYPE_REPLACEMENT) {
3465 $picto .= 'r'; // Replacement invoice
3466 }
3467 if ($this->type == self::TYPE_CREDIT_NOTE) {
3468 $picto .= 'a'; // Credit note
3469 }
3470 if ($this->type == self::TYPE_DEPOSIT) {
3471 $picto .= 'd'; // Deposit invoice
3472 }
3473
3474 $return = '<div class="box-flex-item box-flex-grow-zero">';
3475 $return .= '<div class="info-box info-box-sm">';
3476 $return .= '<span class="info-box-icon bg-infobox-action">';
3477 $return .= img_picto('', $picto);
3478 $return .= '</span>';
3479 $return .= '<div class="info-box-content">';
3480 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3481 if ($selected >= 0) {
3482 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3483 }
3484 if (!empty($arraydata['thirdparty'])) {
3485 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3486 }
3487 if (!empty($this->date)) {
3488 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3489 }
3490 if (!empty($this->total_ht)) {
3491 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3492 $return .= ' '.$langs->trans("HT");
3493 $return .= '</span>';
3494 }
3495 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3496 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3497
3498 $return .= '</div>';
3499 $return .= '</div>';
3500 $return .= '</div>';
3501
3502 return $return;
3503 }
3504
3511 public function setVATReverseCharge($vatreversecharge)
3512 {
3513 if (!$this->table_element) {
3514 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3515 return -1;
3516 }
3517
3518 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3519
3520 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3521 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3522 $sql .= " WHERE rowid=".((int) $this->id);
3523
3524 if ($this->db->query($sql)) {
3525 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3526 return 1;
3527 } else {
3528 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3529 $this->error = $this->db->error();
3530 return 0;
3531 }
3532 }
3533
3545 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3546 {
3547 global $conf, $langs, $user;
3548
3549 $this->output = '';
3550 $this->error = '';
3551 $nbMailSend = 0;
3552
3553 $error = 0;
3554 $errorsMsg = array();
3555
3556 $langs->load('bills');
3557
3558 if (!isModEnabled(!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3559 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3560 return 0;
3561 }
3562 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3563 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3564 return 0;
3565 }
3566
3567 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3568 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3569 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3570 $formmail = new FormMail($this->db);
3571
3572 $now = dol_now();
3573 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3574
3575 $tmpinvoice = new FactureFournisseur($this->db);
3576
3577 dol_syslog(__METHOD__." start", LOG_INFO);
3578
3579 // Select all action comm reminder
3580 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3581 if (!empty($paymentmode) && $paymentmode != 'all') {
3582 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3583 }
3584 $sql .= " WHERE f.paye = 0"; // Only unpaid
3585 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3586 if ($datetouse == 'invoicedate') {
3587 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3588 } else {
3589 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3590 }
3591 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3592 if (!empty($paymentmode) && $paymentmode != 'all') {
3593 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3594 }
3595 // TODO Add a filter to check there is no payment started yet
3596 if ($datetouse == 'invoicedate') {
3597 $sql .= $this->db->order("datef", "ASC");
3598 } else {
3599 $sql .= $this->db->order("date_lim_reglement", "ASC");
3600 }
3601
3602 $resql = $this->db->query($sql);
3603
3604 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3605 if ($datetouse == 'invoicedate') {
3606 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3607 } else {
3608 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3609 }
3610 if (!empty($paymentmode) && $paymentmode != 'all') {
3611 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3612 }
3613 $this->output .= '<br>';
3614
3615 if ($resql) {
3616 while ($obj = $this->db->fetch_object($resql)) {
3617 if (!$error) {
3618 // Load event
3619 $res = $tmpinvoice->fetch($obj->id);
3620 if ($res > 0) {
3621 $tmpinvoice->fetch_thirdparty();
3622
3623 $outputlangs = new Translate('', $conf);
3624 if ($tmpinvoice->thirdparty->default_lang) {
3625 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3626 $outputlangs->loadLangs(array("main", "suppliers"));
3627 } else {
3628 $outputlangs = $langs;
3629 }
3630
3631 // Select email template according to language of recipient
3632 $templateId = 0;
3633 $templateLabel = '';
3634 if (empty($template) || $template == 'EmailTemplateCode') {
3635 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3636 } else {
3637 if (is_numeric($template)) {
3638 $templateId = $template;
3639 } else {
3640 $templateLabel = $template;
3641 }
3642 }
3643
3644 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3645 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3646 $langs->load("errors");
3647 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3648 return 0;
3649 }
3650
3651 // PREPARE EMAIL
3652 $errormesg = '';
3653
3654 // Make substitution in email content
3655 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $tmpinvoice);
3656
3657 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3658
3659 // Topic
3660 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3661
3662 // Content
3663 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3664
3665 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3666
3667 // Recipient
3668 $to = array();
3669 if ($forcerecipient) { // If a recipient was forced
3670 $to = array($forcerecipient);
3671 } else {
3672 $res = $tmpinvoice->fetch_thirdparty();
3673 $recipient = $tmpinvoice->thirdparty;
3674 if ($res > 0) {
3675 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3676 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3677 foreach ($tmparraycontact as $data_email) {
3678 if (!empty($data_email['email'])) {
3679 $to[] = $data_email['email'];
3680 }
3681 }
3682 }
3683 if (empty($to) && !empty($recipient->email)) {
3684 $to[] = $recipient->email;
3685 }
3686 if (empty($to)) {
3687 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3688 $error++;
3689 }
3690 } else {
3691 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3692 $error++;
3693 }
3694 }
3695
3696 // Sender
3697 $email_from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3698 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3699 $email_from = (string) $arraymessage->email_from;
3700 }
3701 if (empty($email_from)) {
3702 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3703 $error++;
3704 }
3705
3706 if (!$error && !empty($to)) {
3707 $this->db->begin();
3708
3709 $to = implode(',', $to);
3710 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3711 $to = $to.','.$arraymessage->email_to;
3712 }
3713
3714 // Errors Recipient
3715 $errors_to = getDolGlobalString('MAIN_MAIL_ERRORS_TO');
3716
3717 $trackid = 'inv'.$tmpinvoice->id;
3718 $sendcontext = 'standard';
3719
3720 $email_tocc = '';
3721 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3722 $email_tocc = (string) $arraymessage->email_tocc;
3723 }
3724
3725 $email_tobcc = '';
3726 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3727 $email_tobcc = (string) $arraymessage->email_tobcc;
3728 }
3729
3730 // Mail Creation
3731 $cMailFile = new CMailFile($sendTopic, $to, $email_from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3732
3733 // Sending Mail
3734 if ($cMailFile->sendfile()) {
3735 $nbMailSend++;
3736
3737 // Add a line into event table
3738 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3739
3740 // Insert record of emails sent
3741 $actioncomm = new ActionComm($this->db);
3742
3743 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3744 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3745 $actioncomm->contact_id = 0;
3746
3747 $actioncomm->code = 'AC_EMAIL';
3748 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3749 $actioncomm->note_private = $sendContent;
3750 $actioncomm->fk_project = $tmpinvoice->fk_project;
3751 $actioncomm->datep = dol_now();
3752 $actioncomm->datef = $actioncomm->datep;
3753 $actioncomm->percentage = -1; // Not applicable
3754 $actioncomm->authorid = $user->id; // User saving action
3755 $actioncomm->userownerid = $user->id; // Owner of action
3756 // Fields when action is an email (content should be added into note)
3757 $actioncomm->email_msgid = $cMailFile->msgid;
3758 $actioncomm->email_subject = $sendTopic;
3759 $actioncomm->email_from = $email_from;
3760 $actioncomm->email_sender = '';
3761 $actioncomm->email_to = $to;
3762 //$actioncomm->email_tocc = $sendtocc;
3763 //$actioncomm->email_tobcc = $sendtobcc;
3764 //$actioncomm->email_subject = $subject;
3765 $actioncomm->errors_to = $errors_to;
3766
3767 $actioncomm->elementtype = 'invoice_supplier';
3768 $actioncomm->elementid = $tmpinvoice->id;
3769 $actioncomm->fk_element = $tmpinvoice->id;
3770
3771 //$actioncomm->extraparams = $extraparams;
3772
3773 $actioncomm->create($user);
3774 } else {
3775 $errormesg = $cMailFile->error.' : '.$to;
3776 $error++;
3777
3778 // Add a line into event table
3779 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3780
3781 // Insert record of emails sent
3782 $actioncomm = new ActionComm($this->db);
3783
3784 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3785 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3786 $actioncomm->contact_id = 0;
3787
3788 $actioncomm->code = 'AC_EMAIL';
3789 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3790 $actioncomm->note_private = $errormesg;
3791 $actioncomm->fk_project = $tmpinvoice->fk_project;
3792 $actioncomm->datep = dol_now();
3793 $actioncomm->datef = $actioncomm->datep;
3794 $actioncomm->percentage = -1; // Not applicable
3795 $actioncomm->authorid = $user->id; // User saving action
3796 $actioncomm->userownerid = $user->id; // Owner of action
3797 // Fields when action is an email (content should be added into note)
3798 $actioncomm->email_msgid = $cMailFile->msgid;
3799 $actioncomm->email_from = $email_from;
3800 $actioncomm->email_sender = '';
3801 $actioncomm->email_to = $to;
3802 //$actioncomm->email_tocc = $sendtocc;
3803 //$actioncomm->email_tobcc = $sendtobcc;
3804 //$actioncomm->email_subject = $subject;
3805 $actioncomm->errors_to = $errors_to;
3806
3807 //$actioncomm->extraparams = $extraparams;
3808
3809 $actioncomm->create($user);
3810 }
3811
3812 $this->db->commit(); // We always commit
3813 }
3814
3815 if ($errormesg) {
3816 $errorsMsg[] = $errormesg;
3817 }
3818 } else {
3819 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3820 $error++;
3821 }
3822 }
3823 }
3824 } else {
3825 $error++;
3826 }
3827
3828 if (!$error) {
3829 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3830
3831 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3832
3833 return 0;
3834 } else {
3835 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3836
3837 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3838
3839 return $error;
3840 }
3841 }
3842}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Superclass for invoice classes.
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class to manage suppliers invoices.
const TYPE_DEPOSIT
Deposit invoice.
create($user)
Create supplier invoice into database.
list_qualified_avoir_supplier_invoices($socid=0)
Return list of qualifying invoices for correction by credit note Invoices that respect the following ...
list_replacable_supplier_invoices($socid=0)
Return list of replaceable invoices Status valid or abandoned for other reason + not paid + no paymen...
deleteLine($rowid, $notrigger=0)
Delete a detail line from database.
set_unpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
setCanceled($user, $close_code='', $close_note='')
Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never rece...
fetch($id=0, $ref='', $ref_ext='')
Load object in memory from database.
setCategories($categories)
Sets object to given categories.
info($id)
Loads the info order information into the invoice object.
const TYPE_CREDIT_NOTE
Credit note invoice.
isCreditNoteUsed()
Is credit note used.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable name (with optional picto)
getTooltipContentArray($params)
getTooltipContentArray
setPaid($user, $close_code='', $close_note='')
Tag invoice as a paid invoice.
update($user=null, $notrigger=0)
Update database.
addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product=0, $remise_percent=0, $date_start=0, $date_end=0, $fk_code_ventilation=0, $info_bits=0, $price_base_type='HT', $type=0, $rang=-1, $notrigger=0, $array_options=[], $fk_unit=null, $origin_id=0, $pu_devise=0, $ref_supplier='', $special_code=0, $fk_parent_line=0, $fk_remise_except=0, $origin_type='')
Adds an invoice line (associated with no predefined product/service) The parameters are already suppo...
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
const TYPE_REPLACEMENT
Replacement invoice.
setUnpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
const STATUS_VALIDATED
Validated (need to be paid)
setDraft($user, $idwarehouse=-1, $notrigger=0)
Set draft status.
getNextNumRef($soc, $mode='next')
Return next reference of supplier invoice not already used (or last reference) according to numbering...
updateline($id, $desc, $pu, $vatrate, $txlocaltax1=0, $txlocaltax2=0, $qty=1, $idproduct=0, $price_base_type='HT', $info_bits=0, $type=0, $remise_percent=0, $notrigger=0, $date_start='', $date_end='', $array_options=[], $fk_unit=null, $pu_devise=0, $ref_supplier='', $rang=0)
Update a line detail in the database.
sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays=0, $paymentmode='all', $template='', $datetouse='duedate', $forcerecipient='')
Send reminders by emails for supplier invoices validated that are due.
insert_discount($idremise)
Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume...
initAsSpecimen($option='')
Initialise an instance with random values.
const TYPE_STANDARD
Standard invoice.
validate($user, $force_number='', $idwarehouse=0, $notrigger=0)
Tag invoice as validated + call trigger BILL_VALIDATE.
set_paid($user, $close_code='', $close_note='')
Tag invoice as a paid invoice.
createFromClone(User $user, $fromid, $invertdetail=0)
Load an object from its id and create a new one in database.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
getRights()
Returns the rights used for this class.
const STATUS_ABANDONED
Classified abandoned and no payment done.
hasDelay()
Is the payment of the supplier invoice having a delay?
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
loadStateBoard()
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
const STATUS_CLOSED
Classified paid.
setVATReverseCharge($vatreversecharge)
Change the option VAT reverse charge.
Class to manage invoice templates.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class to manage "other" html components Only common components are here.
Class to manage stock movements.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
static getIdFromCode($dbs, $code)
Get id of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage line invoices.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
dol_get_first_hour($date, $gm='tzserver')
Return GMT time for first hour of a given GMT date (it removes hours, min and second part)
Definition date.lib.php:665
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:126
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:65
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
setEntity($currentobject)
Set entity id to use when to create an object.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
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.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
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)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
Definition html.lib.php:172
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $notused, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
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 date
Definition receipt.php:487
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