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
192 public $date_pointoftax;
193
198 public $amount = 0;
203 public $remise = 0;
204
209 public $tva;
210
211 // Warning: Do not set default value into property definition. it must stay null.
212 // For example to avoid to have substitution done when object is generic and not yet defined.
214 public $localtax1;
216 public $localtax2;
218 public $total_ht;
220 public $total_tva;
222 public $total_localtax1;
224 public $total_localtax2;
226 public $total_ttc;
227
233 public $note;
235 public $note_private;
237 public $note_public;
239 public $propalid;
240
244 public $fk_account; // default bank account
245
249 public $transport_mode_id;
250
254 public $vat_reverse_charge;
255
259 public $extraparams = array();
260
264 public $lines = array();
265
270 public $fournisseur;
271
273
276 public $fk_facture_source;
277
279 public $fac_rec;
281 public $fk_fac_rec_source;
282
283 public $fields = array(
284 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
285 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
286 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
287 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
288 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
289 'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
290 'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
291 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
292 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
293 'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
294 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
295 'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
296 'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
297 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
298 'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
299 'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
300 'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
301 'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
302 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
303 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
304 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
305 'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
306 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
307 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
308 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
309 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
310 'fk_facture_source' => array('type' => 'integer', 'label' => 'SourceInvoice', 'enabled' => 1, 'visible' => -1, 'position' => 140),
311 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
312 'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
313 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
314 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
315 'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
316 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
317 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
318 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
319 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
320 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
321 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
322 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
323 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
324 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
325 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
326 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
327 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
328 'date_pointoftax' => array('type' => 'date', 'label' => 'DatePointOfTax', 'enabled' => 'getDolGlobalString("INVOICE_POINTOFTAX_DATE")', 'visible' => -1, 'position' => 235),
329 'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
330 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
331 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
332 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
333 );
334
335
339 const TYPE_STANDARD = 0;
340
345
350
354 const TYPE_DEPOSIT = 3;
355
359 const STATUS_DRAFT = 0;
360
365
373 const STATUS_CLOSED = 2;
374
383
384 const CLOSECODE_DISCOUNTVAT = 'discount_vat';
385 const CLOSECODE_BADCREDIT = 'badsupplier';
386 const CLOSECODE_ABANDONED = 'abandon';
387 const CLOSECODE_REPLACED = 'replaced';
388
389
395 public function __construct($db)
396 {
397 $this->db = $db;
398
399 $this->ismultientitymanaged = 1;
400 }
401
408 public function create($user)
409 {
410 global $langs, $conf, $hookmanager;
411
412 $error = 0;
413 $now = dol_now();
414
415 // Clean parameters
416 if (isset($this->ref_supplier)) {
417 $this->ref_supplier = trim($this->ref_supplier);
418 }
419 if (empty($this->type)) {
420 $this->type = self::TYPE_STANDARD;
421 }
422 if (empty($this->date)) {
423 $this->date = $now;
424 }
425
426 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
427 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
428 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
429 } else {
430 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
431 }
432 if (empty($this->fk_multicurrency)) {
433 $this->multicurrency_code = $conf->currency;
434 $this->fk_multicurrency = 0;
435 $this->multicurrency_tx = 1;
436 }
437 $this->entity = setEntity($this);
438
439 $this->db->begin();
440
441 // Defaults
442 $originaldatewhen = 0;
443 $nextdatewhen = 0;
444 $previousdaynextdatewhen = 0;
445 $_facrec = null;
446
447 // Create invoice from a template recurring invoice
448 if ($this->fac_rec > 0) {
449 $this->fk_fac_rec_source = $this->fac_rec;
450
451 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
452 $_facrec = new FactureFournisseurRec($this->db);
453 $result = $_facrec->fetch($this->fac_rec);
454 $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
455
456 // Define some dates
457 if (!empty($_facrec->frequency)) {
458 $originaldatewhen = $_facrec->date_when;
459 $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
460 $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
461 $this->socid = $_facrec->socid;
462 }
463
464 $this->entity = $_facrec->entity; // Invoice created in same entity than template
465
466 // Fields coming from GUI
467 // @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
468 // set by posted page with $object->xxx = ... and this section should be removed.
469 $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
470 $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
471 $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
472 $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
473 $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
474 $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
475 $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
476
477 // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
478 $this->total_ht = $_facrec->total_ht;
479 $this->total_ttc = $_facrec->total_ttc;
480
481 // Fields always coming from template
482 $this->fk_incoterms = $_facrec->fk_incoterms;
483 $this->location_incoterms = $_facrec->location_incoterms;
484
485 // Clean parameters
486 if (! $this->type) {
487 $this->type = self::TYPE_STANDARD;
488 }
489 $this->note_public = trim((string) $this->note_public);
490 $this->note_private = trim((string) $this->note_private);
491 $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
492
493 $this->array_options = $_facrec->array_options;
494
495 if (! $this->mode_reglement_id) {
496 $this->mode_reglement_id = 0;
497 }
498 $this->status = self::STATUS_DRAFT;
499 $this->statut = self::STATUS_DRAFT; // deprecated
500
501 $this->linked_objects = $_facrec->linkedObjectsIds;
502 // We do not add link to template invoice or next invoice will be linked to all generated invoices
503 //$this->linked_objects['facturerec'][0] = $this->fac_rec;
504
505 $forceduedate = $this->calculate_date_lim_reglement();
506
507 // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
508 if ($_facrec->frequency > 0) {
509 $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
510 dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
511 if (empty($_facrec->date_when)) {
512 $_facrec->date_when = $now;
513 }
514 $next_date = $_facrec->getNextDate(); // Calculate next date
515 $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
516 //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
517 $result = $_facrec->setNextDate($next_date, 1);
518 }
519
520 // Define lang of customer
521 $outputlangs = $langs;
522 $newlang = '';
523
524 if (getDolGlobalInt('MAIN_MULTILANGS') && isset($this->thirdparty->default_lang)) {
525 $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
526 }
527 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && property_exists($this, 'default_lang') && isset($this->default_lang)) { // @phan-suppress-current-line PhanUndeclaredProperty
528 $newlang = $this->default_lang; // for thirdparty @phan-suppress-current-line PhanUndeclaredProperty
529 }
530 if (!empty($newlang)) {
531 $outputlangs = new Translate("", $conf);
532 $outputlangs->setDefaultLang($newlang);
533 } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
534 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
535 $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
536 $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
537 $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
538 $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
539 $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
540 $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
541 $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
542 $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
543 $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
544 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
545 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
546 $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
547 $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
548 $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
549
550 complete_substitutions_array($substitutionarray, $outputlangs);
551
552 $this->note_public = make_substitutions($this->note_public, $substitutionarray);
553 $this->note_private = make_substitutions($this->note_private, $substitutionarray);
554 }
555
556 // Define due date if not already defined
557 if (!empty($forceduedate)) {
558 $this->date_echeance = $forceduedate;
559 }
560
561 $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
562 $sql .= "ref";
563 $sql .= ", ref_supplier";
564 $sql .= ", ref_ext";
565 $sql .= ", entity";
566 $sql .= ", type";
567 $sql .= ", subtype";
568 $sql .= ", libelle";
569 $sql .= ", fk_soc";
570 $sql .= ", datec";
571 $sql .= ", datef";
572 $sql .= ", date_pointoftax";
573 $sql .= ", vat_reverse_charge";
574 $sql .= ", fk_projet";
575 $sql .= ", fk_cond_reglement";
576 $sql .= ", fk_mode_reglement";
577 $sql .= ", fk_account";
578 $sql .= ", note_private";
579 $sql .= ", note_public";
580 $sql .= ", fk_user_author";
581 $sql .= ", date_lim_reglement";
582 $sql .= ", fk_incoterms, location_incoterms";
583 $sql .= ", fk_multicurrency";
584 $sql .= ", multicurrency_code";
585 $sql .= ", multicurrency_tx";
586 $sql .= ", fk_facture_source";
587 $sql .= ", fk_fac_rec_source";
588 $sql .= ")";
589 $sql .= " VALUES (";
590 $sql .= "'(PROV)'";
591 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
592 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
593 $sql .= ", ".((int) $this->entity);
594 $sql .= ", '".$this->db->escape((string) $this->type)."'";
595 $sql .= ", ".(isset($this->subtype) ? (int) $this->subtype : "null");
596 $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
597 $sql .= ", ".((int) $this->socid);
598 $sql .= ", '".$this->db->idate($now)."'";
599 $sql .= ", '".$this->db->idate($this->date)."'";
600 $sql .= ", ".(empty($this->date_pointoftax) ? "null" : "'".$this->db->idate($this->date_pointoftax)."'");
601 $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0);
602 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
603 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
604 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
605 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
606 $sql .= ", '".$this->db->escape($this->note_private)."'";
607 $sql .= ", '".$this->db->escape($this->note_public)."'";
608 $sql .= ", ".((int) $user->id).",";
609 $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
610 $sql .= ", ".(int) $this->fk_incoterms;
611 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
612 $sql .= ", ".(int) $this->fk_multicurrency;
613 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
614 $sql .= ", ".(float) $this->multicurrency_tx;
615 $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
616 $sql .= ", ".(isset($this->fk_fac_rec_source) ? ((int) $this->fk_fac_rec_source) : "NULL");
617 $sql .= ")";
618
619 dol_syslog(get_class($this)."::create", LOG_DEBUG);
620 $resql = $this->db->query($sql);
621 if ($resql) {
622 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
623
624 // Update ref with new one
625 $this->ref = '(PROV'.$this->id.')';
626 $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
627
628 dol_syslog(get_class($this)."::create", LOG_DEBUG);
629 $resql = $this->db->query($sql);
630 if (!$resql) {
631 $error++;
632 }
633
634 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
635 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
636 }
637
638 // Add object linked
639 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
640 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
641 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, ...))
642 foreach ($tmp_origin_id as $origin_id) {
643 $ret = $this->add_object_linked($origin, $origin_id);
644 if (!$ret) {
645 dol_print_error($this->db);
646 $error++;
647 }
648 }
649 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
650 $origin_id = $tmp_origin_id;
651 $ret = $this->add_object_linked($origin, $origin_id);
652 if (!$ret) {
653 dol_print_error($this->db);
654 $error++;
655 }
656 }
657 }
658 }
659
660 if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
661 dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
662 foreach ($this->lines as $i => $val) {
663 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
664 $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').')';
665
666 $resql_insert = $this->db->query($sql);
667 if ($resql_insert) {
668 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
669
670 // Preserve the original entry mode of the line so the total is computed from the typed value (no rounding drift).
671 $line_price_base_type = $this->lines[$i]->getPriceBaseType();
672 $line_pu = ($line_price_base_type === 'TTC') ? (float) $this->lines[$i]->subprice_ttc : $this->lines[$i]->subprice;
673 $res = $this->updateline(
674 $idligne,
675 $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
676 $line_pu,
677 $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
678 $this->lines[$i]->localtax1_tx,
679 $this->lines[$i]->localtax2_tx,
680 $this->lines[$i]->qty,
681 $this->lines[$i]->fk_product,
682 $line_price_base_type,
683 (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
684 $this->lines[$i]->product_type,
685 $this->lines[$i]->remise_percent,
686 0,
687 $this->lines[$i]->date_start,
688 $this->lines[$i]->date_end,
689 $this->lines[$i]->array_options,
690 $this->lines[$i]->fk_unit,
691 $this->lines[$i]->multicurrency_subprice,
692 $this->lines[$i]->ref_supplier
693 );
694 } else {
695 $this->error = $this->db->lasterror();
696 $this->db->rollback();
697 return -5;
698 }
699 }
700 } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
701 dol_syslog("There is ".count($this->lines)." lines that are array lines");
702 foreach ($this->lines as $i => $val) {
703 $line = $this->lines[$i];
704
705 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
706 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
707 if (!is_object($line)) {
708 $line = (object) $line;
709 }
710
711 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
712 $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').')';
713
714 $resql_insert = $this->db->query($sql);
715 if ($resql_insert) {
716 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
717
718 $this->updateline(
719 $idligne,
720 $line->desc ? $line->desc : $line->description,
721 $line->subprice,
722 $line->tva_tx,
723 $line->localtax1_tx,
724 $line->localtax2_tx,
725 $line->qty,
726 $line->fk_product,
727 'HT',
728 (!empty($line->info_bits) ? $line->info_bits : ''),
729 $line->product_type,
730 $line->remise_percent,
731 0,
732 $line->date_start,
733 $line->date_end,
734 $line->array_options,
735 $line->fk_unit,
736 $line->multicurrency_subprice,
737 $line->ref_supplier
738 );
739 } else {
740 $this->error = $this->db->lasterror();
741 $this->db->rollback();
742 return -5;
743 }
744 }
745 }
746
747 /*
748 * Insert lines of template invoices
749 */
750 if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
751 foreach ($_facrec->lines as $i => $val) {
752 $product_type = $_facrec->lines[$i]->product_type;
753 if ($_facrec->lines[$i]->fk_product) {
754 $prod = new Product($this->db);
755 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
756 }
757
758 // For line from template invoice, we use data from template invoice
759 /*
760 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
761 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
762 if (empty($tva_tx)) $tva_npr=0;
763 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
764 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
765 */
766 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
767 $tva_npr = $_facrec->lines[$i]->info_bits;
768 if (empty($tva_tx)) {
769 $tva_npr = 0;
770 }
771 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
772 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
773
774 // $fk_product_fournisseur_price not used and does not exist on line
775 // $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
776 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
777
778 // If buyprice not defined from template invoice, we try to guess the best value
779 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
780 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
781 $producttmp = new ProductFournisseur($this->db);
782 $producttmp->fetch($_facrec->lines[$i]->fk_product);
783
784 // If margin module defined on costprice, we try the costprice
785 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
786 // else we get the best supplier price
787 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
788 $buyprice = $producttmp->cost_price;
789 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
790 $buyprice = $producttmp->pmp;
791 } else {
792 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
793 if ($producttmp->product_fourn_price_id > 0) {
794 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
795 }
796 }
797 }
798 }
799
800 $result_insert = $this->addline(
801 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
802 $_facrec->lines[$i]->pu_ht,
803 $tva_tx,
804 $localtax1_tx,
805 $localtax2_tx,
806 $_facrec->lines[$i]->qty,
807 $_facrec->lines[$i]->fk_product,
808 $_facrec->lines[$i]->remise_percent,
809 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
810 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
811 0,
812 $_facrec->lines[$i]->info_bits,
813 'HT',
814 $product_type,
815 $_facrec->lines[$i]->rang,
816 0,
817 $_facrec->lines[$i]->array_options,
818 $_facrec->lines[$i]->fk_unit,
819 0,
820 0,
821 $_facrec->lines[$i]->ref_supplier,
822 $_facrec->lines[$i]->special_code,
823 0,
824 0
825 );
826 if ($result_insert < 0) {
827 $error++;
828 $this->error = $this->db->error();
829 break;
830 }
831 }
832 }
833
834
835 // Update total price
836 $result = $this->update_price(1);
837 if ($result > 0) {
838 // Actions on extra fields
839 if (!$error) {
840 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
841 if ($result < 0) {
842 $error++;
843 }
844 }
845
846 if (!$error) {
847 // Call trigger
848 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
849 if ($result < 0) {
850 $error++;
851 }
852 // End call triggers
853 }
854
855 if (!$error) {
856 $this->db->commit();
857 return $this->id;
858 } else {
859 $this->db->rollback();
860 return -4;
861 }
862 } else {
863 $this->error = $langs->trans('FailedToUpdatePrice');
864 $this->db->rollback();
865 return -3;
866 }
867 } else {
868 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
869 $this->error = $langs->trans('ErrorRefAlreadyExists');
870 $this->db->rollback();
871 return -1;
872 } else {
873 $this->error = $this->db->lasterror();
874 $this->db->rollback();
875 return -2;
876 }
877 }
878 }
879
888 public function fetch($id = 0, $ref = '', $ref_ext = '')
889 {
890 if (empty($id) && empty($ref) && empty($ref_ext)) {
891 return -1;
892 }
893
894 $sql = "SELECT";
895 $sql .= " t.rowid,";
896 $sql .= " t.ref,";
897 $sql .= " t.ref_supplier,";
898 $sql .= " t.ref_ext,";
899 $sql .= " t.entity,";
900 $sql .= " t.type,";
901 $sql .= " t.subtype,";
902 $sql .= " t.fk_soc,";
903 $sql .= " t.datec,";
904 $sql .= " t.datef,";
905 $sql .= " t.date_pointoftax,";
906 $sql .= " t.tms as datem,";
907 $sql .= " t.libelle as label,";
908 $sql .= " t.paye as paid,";
909 $sql .= " t.close_code,";
910 $sql .= " t.close_note,";
911 $sql .= " t.tva,";
912 $sql .= " t.localtax1,";
913 $sql .= " t.localtax2,";
914 $sql .= " t.total_ht,";
915 $sql .= " t.total_tva,";
916 $sql .= " t.total_ttc,";
917 $sql .= " t.fk_statut as status,";
918 $sql .= " t.fk_user_author,";
919 $sql .= " t.fk_user_valid,";
920 $sql .= " t.fk_facture_source,";
921 $sql .= " t.vat_reverse_charge,";
922 $sql .= " t.fk_fac_rec_source,";
923 $sql .= " t.fk_projet as fk_project,";
924 $sql .= " t.fk_cond_reglement,";
925 $sql .= " t.fk_account,";
926 $sql .= " t.fk_mode_reglement,";
927 $sql .= " t.date_lim_reglement,";
928 $sql .= " t.note_private,";
929 $sql .= " t.note_public,";
930 $sql .= " t.model_pdf,";
931 $sql .= " t.last_main_doc,";
932 $sql .= " t.import_key,";
933 $sql .= " t.extraparams,";
934 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
935 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
936 $sql .= ' s.nom as socnom, s.rowid as socid,';
937 $sql .= ' t.fk_incoterms, t.location_incoterms,';
938 $sql .= " i.libelle as label_incoterms,";
939 $sql .= ' t.fk_transport_mode,';
940 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
941 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
942 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
943 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
944 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
945 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
946 if ($id) {
947 $sql .= " WHERE t.rowid = ".((int) $id);
948 } else {
949 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
950 if ($ref) {
951 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
952 }
953 if ($ref_ext) {
954 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
955 }
956 }
957
958 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
959 $resql = $this->db->query($sql);
960 if ($resql) {
961 if ($this->db->num_rows($resql)) {
962 $obj = $this->db->fetch_object($resql);
963
964 $this->id = $obj->rowid;
965 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
966
967 $this->ref_supplier = $obj->ref_supplier;
968 $this->ref_ext = $obj->ref_ext;
969 $this->entity = $obj->entity;
970 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
971 $this->subtype = $obj->subtype;
972 $this->socid = $obj->fk_soc;
973 $this->date = $this->db->jdate($obj->datef);
974 $this->date_pointoftax = $this->db->jdate($obj->date_pointoftax);
975 $this->date_creation = $this->db->jdate($obj->datec);
976 $this->datec = $this->db->jdate($obj->datec);
977 $this->date_modification = $this->db->jdate($obj->datem);
978 $this->tms = $this->db->jdate($obj->datem);
979 $this->libelle = $obj->label; // deprecated
980 $this->label = $obj->label;
981 $this->paye = $obj->paid;
982 $this->paid = $obj->paid;
983 $this->close_code = $obj->close_code;
984 $this->close_note = $obj->close_note;
985 $this->total_localtax1 = $obj->localtax1;
986 $this->total_localtax2 = $obj->localtax2;
987 $this->total_ht = $obj->total_ht;
988 $this->total_tva = $obj->total_tva;
989 $this->total_ttc = $obj->total_ttc;
990 $this->status = $obj->status;
991 $this->statut = $obj->status; // For backward compatibility
992 $this->fk_statut = $obj->status; // For backward compatibility
993 $this->user_creation_id = $obj->fk_user_author;
994 $this->author = $obj->fk_user_author; // deprecated
995 $this->user_validation_id = $obj->fk_user_valid;
996 $this->fk_user_valid = $obj->fk_user_valid;
997 $this->fk_facture_source = $obj->fk_facture_source;
998 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
999 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
1000 $this->fk_project = $obj->fk_project;
1001 $this->cond_reglement_id = $obj->fk_cond_reglement;
1002 $this->cond_reglement_code = $obj->cond_reglement_code;
1003 $this->cond_reglement_label = $obj->cond_reglement_label;
1004 $this->cond_reglement_doc = $obj->cond_reglement_doc;
1005 $this->fk_account = $obj->fk_account;
1006 $this->mode_reglement_id = $obj->fk_mode_reglement;
1007 $this->mode_reglement_code = $obj->mode_reglement_code;
1008 $this->mode_reglement = $obj->mode_reglement_label;
1009 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
1010 $this->note = $obj->note_private; // deprecated
1011 $this->note_private = $obj->note_private;
1012 $this->note_public = $obj->note_public;
1013 $this->model_pdf = $obj->model_pdf;
1014 $this->last_main_doc = $obj->last_main_doc;
1015 $this->import_key = $obj->import_key;
1016
1017 //Incoterms
1018 $this->fk_incoterms = $obj->fk_incoterms;
1019 $this->location_incoterms = $obj->location_incoterms;
1020 $this->label_incoterms = $obj->label_incoterms;
1021 $this->transport_mode_id = $obj->fk_transport_mode;
1022
1023 // Multicurrency
1024 $this->fk_multicurrency = $obj->fk_multicurrency;
1025 $this->multicurrency_code = $obj->multicurrency_code;
1026 $this->multicurrency_tx = $obj->multicurrency_tx;
1027 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1028 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1029 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1030
1031 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1032
1033 $this->socid = $obj->socid;
1034
1035 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1036
1037 // Retrieve all extrafield
1038 // fetch optionals attributes and labels
1039 $this->fetch_optionals();
1040
1041 $result = $this->fetch_lines();
1042 if ($result < 0) {
1043 $this->error = $this->db->lasterror();
1044 return -3;
1045 }
1046 } else {
1047 $this->error = 'Bill with id '.$id.' not found';
1048 dol_syslog(get_class($this).'::fetch '.$this->error);
1049 return 0;
1050 }
1051
1052 $this->db->free($resql);
1053 return 1;
1054 } else {
1055 $this->error = "Error ".$this->db->lasterror();
1056 return -1;
1057 }
1058 }
1059
1060
1061 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1067 public function fetch_lines()
1068 {
1069 // phpcs:enable
1070 $this->lines = array();
1071
1072 $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';
1073 $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';
1074 $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';
1075 $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';
1076 $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';
1077 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1078 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1079 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1080 $sql .= ' ORDER BY f.rang, f.rowid';
1081
1082 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1083
1084 $resql_rows = $this->db->query($sql);
1085 if ($resql_rows) {
1086 $num_rows = $this->db->num_rows($resql_rows);
1087 if ($num_rows) {
1088 $i = 0;
1089 while ($i < $num_rows) {
1090 $obj = $this->db->fetch_object($resql_rows);
1091
1092 $line = new SupplierInvoiceLine($this->db);
1093
1094 $line->id = $obj->rowid;
1095 $line->rowid = $obj->rowid;
1096
1097 $line->description = $obj->line_desc;
1098 $line->desc = $obj->line_desc;
1099 $line->date_start = $this->db->jdate($obj->date_start);
1100 $line->date_end = $this->db->jdate($obj->date_end);
1101
1102 $line->product_ref = $obj->product_ref;
1103 $line->ref = $obj->product_ref;
1104 $line->ref_supplier = $obj->ref_supplier;
1105 $line->libelle = $obj->label;
1106 $line->label = $obj->label;
1107 $line->product_barcode = $obj->product_barcode;
1108 $line->product_desc = $obj->product_desc;
1109 $line->subprice = $obj->pu_ht;
1110 $line->pu_ht = $obj->pu_ht; // deprecated
1111 $line->subprice_ttc = $obj->pu_ttc;
1112 $line->pu_ttc = $obj->pu_ttc; // deprcated
1113 $line->vat_src_code = $obj->vat_src_code;
1114 $line->tva_tx = $obj->tva_tx;
1115 $line->localtax1_tx = $obj->localtax1_tx;
1116 $line->localtax2_tx = $obj->localtax2_tx;
1117 $line->localtax1_type = $obj->localtax1_type;
1118 $line->localtax2_type = $obj->localtax2_type;
1119 $line->qty = $obj->qty;
1120 $line->remise_percent = $obj->remise_percent;
1121 $line->fk_remise_except = $obj->fk_remise_except;
1122 //$line->tva = $obj->total_tva; // deprecated
1123 $line->total_ht = $obj->total_ht;
1124 $line->total_ttc = $obj->total_ttc;
1125 $line->total_tva = $obj->total_tva;
1126 $line->total_localtax1 = $obj->total_localtax1;
1127 $line->total_localtax2 = $obj->total_localtax2;
1128 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1129 $line->fk_product = $obj->fk_product;
1130 $line->product_type = $obj->product_type;
1131 $line->product_label = $obj->label;
1132 $line->info_bits = $obj->info_bits;
1133 $line->fk_parent_line = $obj->fk_parent_line;
1134 $line->special_code = $obj->special_code;
1135 $line->rang = $obj->rang;
1136 $line->fk_unit = $obj->fk_unit;
1137
1138 $line->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1139
1140 // Accountancy
1141 $line->fk_code_ventilation = $obj->fk_code_ventilation;
1142
1143 // Multicurrency
1144 $line->fk_multicurrency = $obj->fk_multicurrency;
1145 $line->multicurrency_code = $obj->multicurrency_code;
1146 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1147 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1148 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1149 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1150
1151 // Extra fields
1152 $line->fetch_optionals();
1153
1154 $this->lines[$i] = $line;
1155
1156 $i++;
1157 }
1158 }
1159 $this->db->free($resql_rows);
1160 return 1;
1161 } else {
1162 $this->error = $this->db->error();
1163 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1164 return -3;
1165 }
1166 }
1167
1168
1176 public function update($user = null, $notrigger = 0)
1177 {
1178 global $langs;
1179 $error = 0;
1180
1181 // Clean parameters
1182 if (empty($this->type)) {
1183 $this->type = self::TYPE_STANDARD;
1184 }
1185 if (isset($this->ref)) {
1186 $this->ref = trim($this->ref);
1187 }
1188 if (isset($this->ref_supplier)) {
1189 $this->ref_supplier = trim($this->ref_supplier);
1190 }
1191 if (isset($this->ref_ext)) {
1192 $this->ref_ext = trim($this->ref_ext);
1193 }
1194 if (isset($this->entity)) {
1195 $this->entity = (int) $this->entity;
1196 }
1197 if (isset($this->type)) {
1198 $this->type = (int) $this->type;
1199 }
1200 if (isset($this->subtype)) {
1201 $this->subtype = (int) $this->subtype;
1202 }
1203 if (isset($this->socid)) {
1204 $this->socid = (int) $this->socid;
1205 }
1206 if (isset($this->label)) {
1207 $this->label = trim($this->label);
1208 }
1209 if (isset($this->paid)) {
1210 $this->paid = (int) (bool) $this->paid;
1211 $this->paye = $this->paid; // deprecated
1212 } elseif (isset($this->paye)) {
1213 $this->paid = (int) (bool) $this->paye;
1214 $this->paye = $this->paid; // deprecared
1215 }
1216 if (isset($this->close_code)) {
1217 $this->close_code = trim($this->close_code);
1218 }
1219 if (isset($this->close_note)) {
1220 $this->close_note = trim($this->close_note);
1221 }
1222 if (empty($this->total_ht)) {
1223 $this->total_ht = 0;
1224 }
1225 if (empty($this->total_tva)) {
1226 $this->total_tva = 0;
1227 }
1228 if (empty($this->total_localtax1)) {
1229 $this->total_localtax1 = 0;
1230 }
1231 if (empty($this->total_localtax2)) {
1232 $this->total_localtax2 = 0;
1233 }
1234 if (isset($this->total_ttc)) {
1235 $this->total_ttc = (float) $this->total_ttc;
1236 }
1237 if (isset($this->status)) {
1238 $this->status = (int) $this->status;
1239 $this->statut = $this->status;
1240 } elseif (isset($this->statut)) {
1241 $this->status = (int) $this->statut;
1242 $this->statut = $this->status;
1243 }
1244 if (isset($this->author)) { // TODO: user_creation_id?
1245 $this->author = (int) $this->author;
1246 }
1247 if (isset($this->fk_user_valid)) {
1248 $this->fk_user_valid = (int) $this->fk_user_valid;
1249 }
1250 if (isset($this->fk_facture_source)) {
1251 $this->fk_facture_source = (int) $this->fk_facture_source;
1252 }
1253 if (isset($this->fk_project)) {
1254 if (empty($this->fk_project)) {
1255 $this->fk_project = 0;
1256 } else {
1257 $this->fk_project = (int) $this->fk_project;
1258 }
1259 }
1260 if (isset($this->mode_reglement_id)) {
1261 $this->mode_reglement_id = (int) $this->mode_reglement_id;
1262 }
1263 if (isset($this->cond_reglement_id)) {
1264 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1265 }
1266 if (isset($this->note_private)) {
1267 $this->note_private = trim($this->note_private);
1268 $this->note = $this->note_private;
1269 }
1270 if (isset($this->note_public)) {
1271 $this->note_public = trim($this->note_public);
1272 }
1273 if (isset($this->model_pdf)) {
1274 $this->model_pdf = trim($this->model_pdf);
1275 }
1276 if (isset($this->import_key)) {
1277 $this->import_key = trim($this->import_key);
1278 }
1279
1280
1281 // Check parameters
1282 // Put here code to add control on parameters values
1283
1284 if (dol_strlen((string) $this->date_modification) == 0) {
1285 $this->tms = dol_now();
1286 }
1287
1288 // Update request
1289 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1290 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1291 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1292 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1293 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1294 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1295 $sql .= " subtype=".(isset($this->subtype) ? (int) $this->subtype : "null").",";
1296 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1297 $sql .= " datec=".(dol_strlen((string) $this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1298 $sql .= " datef=".(dol_strlen((string) $this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1299 $sql .= " date_pointoftax=".(dol_strlen((string) $this->date_pointoftax) != 0 ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null').",";
1300 if (dol_strlen((string) $this->date_modification) != 0) {
1301 $sql .= " tms=".(dol_strlen((string) $this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').",";
1302 } elseif (dol_strlen((string) $this->tms) != 0) { // For backward compatibility
1303 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1304 }
1305 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1306 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1307 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1308 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1309 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1310 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1311 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1312 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1313 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1314 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1315 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1316 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1317 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1318 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0).",";
1319 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1320 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
1321 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1322 $sql .= " date_lim_reglement=".(dol_strlen((string) $this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1323 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1324 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1325 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1326 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1327 $sql .= " WHERE rowid=".((int) $this->id);
1328
1329 $this->db->begin();
1330
1331 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1332 $resql = $this->db->query($sql);
1333
1334 if (!$resql) {
1335 $error++;
1336
1337 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1338 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1339 } else {
1340 $this->errors[] = "Error ".$this->db->lasterror();
1341 }
1342 }
1343
1344 if (!$error) {
1345 $result = $this->insertExtraFields();
1346 if ($result < 0) {
1347 $error++;
1348 }
1349 }
1350
1351 if (!$error && !$notrigger) {
1352 // Call trigger
1353 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1354 if ($result < 0) {
1355 $error++;
1356 }
1357 // End call triggers
1358 }
1359
1360 // Commit or rollback
1361 if ($error) {
1362 foreach ($this->errors as $errmsg) {
1363 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1364 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1365 }
1366 $this->db->rollback();
1367 return -1 * $error;
1368 } else {
1369 $this->db->commit();
1370 return 1;
1371 }
1372 }
1373
1374 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1381 public function insert_discount($idremise)
1382 {
1383 // phpcs:enable
1384 global $langs;
1385
1386 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1387 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1388
1389 $this->db->begin();
1390
1391 $remise = new DiscountAbsolute($this->db);
1392 $result = $remise->fetch($idremise);
1393
1394 if ($result > 0) {
1395 if ($this->socid > 0 && $remise->fk_soc != $this->socid) { // The discount must belong to the thirdparty of the invoice
1396 $this->error = $langs->trans("ErrorDiscountNotSameCompany");
1397 $this->db->rollback();
1398 return -6;
1399 }
1400 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1401 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1402 $this->db->rollback();
1403 return -5;
1404 }
1405
1406 $facligne = new SupplierInvoiceLine($this->db);
1407 $facligne->fk_facture_fourn = $this->id;
1408 $facligne->fk_remise_except = $remise->id;
1409 $facligne->desc = $remise->description; // Line description
1410 $facligne->vat_src_code = $remise->vat_src_code;
1411 $facligne->tva_tx = $remise->tva_tx;
1412 $facligne->localtax1_tx = $remise->localtax1_tx;
1413 $facligne->localtax1_type = $remise->localtax1_type;
1414 $facligne->localtax2_tx = $remise->localtax1_tx;
1415 $facligne->localtax2_type = $remise->localtax1_type;
1416 $facligne->subprice = -(float) $remise->amount_ht;
1417 $facligne->fk_product = 0; // Predefined Product ID
1418 $facligne->product_type = 0;
1419 $facligne->qty = 1;
1420 $facligne->remise_percent = 0;
1421 $facligne->rang = -1;
1422 $facligne->info_bits = 2;
1423
1424 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1425 $facligne->rang = 1;
1426 $linecount = count($this->lines);
1427 for ($ii = 1; $ii <= $linecount; $ii++) {
1428 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1429 }
1430 }
1431
1432 // Get buy/cost price of invoice that is source of discount
1433 if ($remise->fk_invoice_supplier_source > 0) {
1434 $srcinvoice = new FactureFournisseur($this->db);
1435 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1436 $totalcostpriceofinvoice = 0;
1437 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1438 $formmargin = new FormMargin($this->db);
1439 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1440 $facligne->pa_ht = $arraytmp['pa_total'];
1441 }
1442
1443 $facligne->total_ht = -(float) $remise->amount_ht;
1444 $facligne->total_tva = -(float) $remise->amount_tva;
1445 $facligne->total_ttc = -(float) $remise->amount_ttc;
1446 $facligne->total_localtax1 = -(float) $remise->total_localtax1;
1447 $facligne->total_localtax2 = -(float) $remise->total_localtax2;
1448
1449 // The discount line must carry the currency of the invoice, like any other line
1450 $facligne->fk_multicurrency = $this->fk_multicurrency;
1451 $facligne->multicurrency_code = $this->multicurrency_code;
1452 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1453 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1454 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1455 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1456
1457 $lineid = $facligne->insert();
1458 if ($lineid > 0) {
1459 $result = $this->update_price(1);
1460 if ($result > 0) {
1461 // Create link between discount and invoice line
1462 $result = $remise->link_to_invoice($lineid, 0);
1463 if ($result < 0) {
1464 $this->error = $remise->error;
1465 $this->db->rollback();
1466 return -4;
1467 }
1468
1469 $this->db->commit();
1470 return 1;
1471 } else {
1472 $this->error = $facligne->error;
1473 $this->db->rollback();
1474 return -1;
1475 }
1476 } else {
1477 $this->error = $facligne->error;
1478 $this->db->rollback();
1479 return -2;
1480 }
1481 } else {
1482 $this->db->rollback();
1483 return -3;
1484 }
1485 }
1486
1487
1495 public function delete(User $user, $notrigger = 0)
1496 {
1497 global $conf;
1498
1499 $rowid = $this->id;
1500
1501 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1502
1503 // Test to avoid invoice deletion (invoice transferred into accountancy, with payment, ...), same test as Facture::delete() does
1504 $result = $this->is_erasable();
1505 if ($result <= 0) {
1506 dol_syslog(get_class($this)."::delete refused, invoice is not erasable (code ".$result.")", LOG_DEBUG);
1507 return 0;
1508 }
1509
1510 $error = 0;
1511 $this->db->begin();
1512
1513 if (!$notrigger) {
1514 // Call trigger
1515 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1516 if ($result < 0) {
1517 $this->db->rollback();
1518 return -1;
1519 }
1520 // Fin appel triggers
1521 }
1522
1523 // Remove linked categories.
1524 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_invoice";
1525 $sql .= " WHERE fk_supplier_invoice = ".((int) $this->id);
1526
1527 $result = $this->db->query($sql);
1528 if (!$result) {
1529 $error++;
1530 $this->error = $this->db->lasterror();
1531 $this->errors[] = $this->error;
1532 }
1533
1534 if (!$error) {
1535 // If invoice was converted into a discount not yet consumed, we remove discount
1536 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1537 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1538 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1539 $resql = $this->db->query($sql);
1540
1541 // If invoice has consumned discounts
1542 $this->fetch_lines();
1543 $list_rowid_det = array();
1544 foreach ($this->lines as $key => $invoiceline) {
1545 $list_rowid_det[] = $invoiceline->id;
1546 }
1547
1548 // Consumned discounts are freed
1549 if (count($list_rowid_det)) {
1550 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1551 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1552 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1553
1554 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1555 if (!$this->db->query($sql)) {
1556 $error++;
1557 }
1558 }
1559 }
1560
1561 if (!$error) {
1562 $sql_main_table = MAIN_DB_PREFIX.'facture_fourn_det';
1563 $sql_ef_table = $sql_main_table."_extrafields";
1564 $sqlef = "DELETE FROM $sql_ef_table WHERE fk_object IN (SELECT rowid FROM ".$sql_main_table." WHERE fk_facture_fourn = ".((int) $rowid).")";
1565 $resqlef = $this->db->query($sqlef);
1566 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1567 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1568 $resql = $this->db->query($sql);
1569 if ($resqlef && $resql) {
1570 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1571 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1572 $resql2 = $this->db->query($sql);
1573 if (!$resql2) {
1574 $error++;
1575 }
1576 } else {
1577 $error++;
1578 }
1579 }
1580
1581 if (!$error) {
1582 // Delete linked object
1583 $res = $this->deleteObjectLinked();
1584 if ($res < 0) {
1585 $error++;
1586 }
1587 }
1588
1589 if (!$error) {
1590 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1591 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1592 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1593
1594 // We remove directory
1595 $ref = dol_sanitizeFileName($this->ref);
1596 if ($conf->fournisseur->facture->dir_output && !empty($ref)) {
1597 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1598
1599 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1600 $file = $dir."/".$ref.".pdf";
1601 if (file_exists($file)) {
1602 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1603 $this->error = 'ErrorFailToDeleteFile';
1604 $error++;
1605 }
1606 }
1607 if (file_exists($dir)) {
1608 $res = @dol_delete_dir_recursive($dir);
1609
1610 if (!$res) {
1611 $this->error = 'ErrorFailToDeleteDir';
1612 $error++;
1613 }
1614 }
1615 }
1616 }
1617
1618 // Remove extrafields
1619 if (!$error) {
1620 $result = $this->deleteExtraFields();
1621 if ($result < 0) {
1622 $error++;
1623 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1624 }
1625 }
1626
1627 if (!$error) {
1628 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1629 $this->db->commit();
1630 return 1;
1631 } else {
1632 $this->error = $this->db->lasterror();
1633 $this->db->rollback();
1634 return -$error;
1635 }
1636 }
1637
1638
1639 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1650 public function set_paid($user, $close_code = '', $close_note = '')
1651 {
1652 // phpcs:enable
1653 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1654 return $this->setPaid($user, $close_code, $close_note);
1655 }
1656
1665 public function setPaid($user, $close_code = '', $close_note = '')
1666 {
1667 $error = 0;
1668
1669 if ($this->paid != 1) {
1670 $this->db->begin();
1671
1672 $now = dol_now();
1673
1674 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1675
1676 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1677 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1678 if (!$close_code) {
1679 $sql .= ', paye=1';
1680 }
1681 if ($close_code) {
1682 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1683 }
1684 if ($close_note) {
1685 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1686 }
1687 $sql .= ', fk_user_closing = '.((int) $user->id);
1688 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1689 $sql .= ' WHERE rowid = '.((int) $this->id);
1690
1691 $resql = $this->db->query($sql);
1692 if ($resql) {
1693 // Call trigger
1694 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1695 if ($result < 0) {
1696 $error++;
1697 }
1698 // End call triggers
1699 } else {
1700 $error++;
1701 $this->error = $this->db->error();
1702 dol_print_error($this->db);
1703 }
1704
1705 if (!$error) {
1706 $this->db->commit();
1707 return 1;
1708 } else {
1709 $this->db->rollback();
1710 return -1;
1711 }
1712 } else {
1713 return 0;
1714 }
1715 }
1716
1717 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1728 public function set_unpaid($user)
1729 {
1730 // phpcs:enable
1731 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1732 return $this->setUnpaid($user);
1733 }
1734
1743 public function setUnpaid($user)
1744 {
1745 $error = 0;
1746
1747 $this->db->begin();
1748
1749 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1750 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1751 $sql .= ' date_closing=null,';
1752 $sql .= ' fk_user_closing=null';
1753 $sql .= ' WHERE rowid = '.((int) $this->id);
1754
1755 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1756 $resql = $this->db->query($sql);
1757 if ($resql) {
1758 // Call trigger
1759 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1760 if ($result < 0) {
1761 $error++;
1762 }
1763 // End call triggers
1764 } else {
1765 $error++;
1766 $this->error = $this->db->error();
1767 dol_print_error($this->db);
1768 }
1769
1770 if (!$error) {
1771 $this->db->commit();
1772 return 1;
1773 } else {
1774 $this->db->rollback();
1775 return -1;
1776 }
1777 }
1778
1789 public function setCanceled($user, $close_code = '', $close_note = '')
1790 {
1791 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1792
1793 $this->db->begin();
1794
1795 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1796 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1797 if ($close_code) {
1798 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1799 }
1800 if ($close_note) {
1801 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1802 }
1803 $sql .= " WHERE rowid = ".((int) $this->id);
1804
1805 $resql = $this->db->query($sql);
1806 if ($resql) {
1807 // Bound discounts are deducted from the invoice
1808 // as they have not been used since the invoice is abandoned.
1809 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1810 $sql .= ' SET fk_invoice_supplier = NULL';
1811 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1812
1813 $resql = $this->db->query($sql);
1814 if ($resql) {
1815 // Call trigger
1816 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1817 if ($result < 0) {
1818 $this->db->rollback();
1819 return -1;
1820 }
1821 // End call triggers
1822
1823 $this->db->commit();
1824 return 1;
1825 } else {
1826 $this->error = $this->db->error()." sql=".$sql;
1827 $this->db->rollback();
1828 return -1;
1829 }
1830 } else {
1831 $this->error = $this->db->error()." sql=".$sql;
1832 $this->db->rollback();
1833 return -2;
1834 }
1835 }
1836
1846 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1847 {
1848 global $mysoc, $conf, $langs;
1849
1850 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1851
1852 $now = dol_now();
1853
1854 $error = 0;
1855 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1856
1857 // Force to have object complete for checks
1858 $this->fetch_thirdparty();
1859 $this->fetch_lines();
1860
1861 // Check parameters
1862 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1863 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1864 return 0;
1865 }
1866 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ', '/').'/', $this->ref_supplier)) {
1867 $langs->load("errors");
1868 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1869 return -1;
1870 }
1871 if (count($this->lines) <= 0) {
1872 $langs->load("errors");
1873 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1874 return -1;
1875 }
1876
1877 // Check for mandatory fields in thirdparty (defined into setup)
1878 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1879 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1880 foreach ($array_to_check as $key) {
1881 $keymin = strtolower($key);
1882 if ($keymin == 'accountancy_code_supplier') {
1883 $keymin = 'code_compta_fournisseur';
1884 }
1885 if (!property_exists($this->thirdparty, $keymin)) {
1886 continue;
1887 }
1888 $vallabel = $this->thirdparty->$keymin;
1889
1890 $i = (int) preg_replace('/[^0-9]/', '', $key);
1891 if ($i > 0) {
1892 if ($this->thirdparty->isACompany()) {
1893 // Check for mandatory prof id (but only if country is other than ours)
1894 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1895 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1896 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1897 $langs->load("errors");
1898 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1899 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1900 return -1;
1901 }
1902 }
1903 }
1904 } else {
1905 if ($key == 'EMAIL') {
1906 // Check for mandatory
1907 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1908 $langs->load("errors");
1909 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1910 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1911 return -1;
1912 }
1913 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1914 // Check for mandatory
1915 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1916 $langs->load("errors");
1917 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1918 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1919 return -1;
1920 }
1921 }
1922 }
1923 }
1924 }
1925
1926 $this->db->begin();
1927
1928 // Define new ref
1929 if ($force_number) {
1930 $num = $force_number;
1931 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1932 $num = $this->getNextNumRef($this->thirdparty);
1933 } else {
1934 $num = (string) $this->ref;
1935 }
1936 $this->newref = dol_sanitizeFileName($num);
1937
1938 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1939 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1940 $sql .= " WHERE rowid = ".((int) $this->id);
1941
1942 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1943 $resql = $this->db->query($sql);
1944 if ($resql) {
1945 // If we increment the main product and its components upon validation of the supplier invoice
1946 if (isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1947 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1948 $langs->load("agenda");
1949
1950 $cpt = count($this->lines);
1951 for ($i = 0; $i < $cpt; $i++) {
1952 if ($this->lines[$i]->fk_product > 0) {
1953 $mouvP = new MouvementStock($this->db);
1954 $mouvP->origin = &$this;
1955 $mouvP->setOrigin($this->element, $this->id);
1956 // We increase stock for product
1957 $up_ht_disc = $this->lines[$i]->subprice;
1958 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1959 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1960 }
1962 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1963 } else {
1964 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1965 }
1966 if ($result < 0) {
1967 $this->setErrorsFromObject($mouvP);
1968 return -2;
1969 }
1970 }
1971 }
1972 }
1973
1974 // Triggers call
1975 if (empty($notrigger)) {
1976 // Call trigger
1977 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1978 if ($result < 0) {
1979 $error++;
1980 }
1981 // End call triggers
1982 }
1983
1984 if (!$error) {
1985 $this->oldref = $this->ref;
1986
1987 // Rename directory if dir was a temporary ref
1988 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1989 // Now we rename also files into index
1990 $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)."'";
1991 $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);
1992 $resql = $this->db->query($sql);
1993 if (!$resql) {
1994 $error++;
1995 $this->error = $this->db->lasterror();
1996 }
1997 $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)."'";
1998 $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);
1999 $resql = $this->db->query($sql);
2000 if (!$resql) {
2001 $error++;
2002 $this->error = $this->db->lasterror();
2003 }
2004
2005 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2006 $oldref = dol_sanitizeFileName($this->ref);
2007 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
2008 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
2009 if (!$error && file_exists($dirsource)) {
2010 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2011
2012 if (@rename($dirsource, $dirdest)) {
2013 dol_syslog("Rename ok");
2014 // Rename docs starting with $oldref with $this->newref
2015 $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, '/'));
2016 foreach ($listoffiles as $fileentry) {
2017 $dirsource = $fileentry['name'];
2018 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
2019 $dirsource = $fileentry['path'].'/'.$dirsource;
2020 $dirdest = $fileentry['path'].'/'.$dirdest;
2021 @rename($dirsource, $dirdest);
2022 }
2023 }
2024 }
2025 }
2026 }
2027
2028 // Set new ref and define current status
2029 if (!$error) {
2030 $this->ref = $this->newref;
2031 $this->statut = self::STATUS_VALIDATED;
2033 //$this->date_validation=$now; this is stored into log table
2034 }
2035
2036 if (!$error) {
2037 $this->db->commit();
2038 return 1;
2039 } else {
2040 $this->db->rollback();
2041 return -1;
2042 }
2043 } else {
2044 $this->error = $this->db->error();
2045 $this->db->rollback();
2046 return -1;
2047 }
2048 }
2049
2058 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
2059 {
2060 // phpcs:enable
2061 global $langs;
2062
2063 $error = 0;
2064
2065 if ($this->status == self::STATUS_DRAFT) {
2066 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
2067 return 0;
2068 }
2069
2070 dol_syslog(__METHOD__, LOG_DEBUG);
2071
2072 $this->db->begin();
2073
2074 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2075 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2076 $sql .= " WHERE rowid = ".((int) $this->id);
2077
2078 $result = $this->db->query($sql);
2079 if ($result) {
2080 $this->oldcopy = clone $this;
2081
2082 // If we increment the main product and its components upon supplier invoice validation, we decrement upon customer invoice validation
2083 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2084 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2085 $langs->load("agenda");
2086
2087 $cpt = count($this->lines);
2088 for ($i = 0; $i < $cpt; $i++) {
2089 if ($this->lines[$i]->fk_product > 0) {
2090 $mouvP = new MouvementStock($this->db);
2091 $mouvP->origin = &$this;
2092 $mouvP->setOrigin($this->element, $this->id);
2093 // We increase stock for product
2095 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2096 } else {
2097 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2098 }
2099 }
2100 }
2101 }
2102 // Triggers call
2103 if (empty($notrigger)) {
2104 // Call trigger
2105 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2106 if ($result < 0) {
2107 $error++;
2108 }
2109 // End call triggers
2110 }
2111 if ($error == 0) {
2112 $this->db->commit();
2113 return 1;
2114 } else {
2115 $this->db->rollback();
2116 return -1;
2117 }
2118 } else {
2119 $this->error = $this->db->error();
2120 $this->db->rollback();
2121 return -1;
2122 }
2123 }
2124
2125
2160 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 = '')
2161 {
2162 global $langs, $mysoc;
2163
2164 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);
2165 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2166
2167 if ($this->status == self::STATUS_DRAFT) {
2168 // Clean parameters
2169 if (empty($remise_percent)) {
2170 $remise_percent = 0;
2171 }
2172 if (empty($qty)) {
2173 $qty = 0;
2174 }
2175 if (empty($info_bits)) {
2176 $info_bits = 0;
2177 }
2178 if (empty($rang)) {
2179 $rang = 0;
2180 }
2181 if (empty($fk_code_ventilation)) {
2182 $fk_code_ventilation = 0;
2183 }
2184 if (empty($txtva)) {
2185 $txtva = 0;
2186 }
2187 if (empty($txlocaltax1)) {
2188 $txlocaltax1 = 0;
2189 }
2190 if (empty($txlocaltax2)) {
2191 $txlocaltax2 = 0;
2192 }
2193
2194 $remise_percent = price2num($remise_percent);
2195 $qty = price2num($qty);
2196 $pu = price2num($pu);
2197 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2198 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2199 }
2200 $txlocaltax1 = price2num($txlocaltax1);
2201 $txlocaltax2 = price2num($txlocaltax2);
2202
2203 if ($date_start && $date_end && $date_start > $date_end) {
2204 $langs->load("errors");
2205 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2206 return -1;
2207 }
2208
2209 $this->db->begin();
2210
2211 if ($fk_product > 0) {
2212 if (getDolGlobalInt('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2213 // Check quantity is enough
2214 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2215 $prod = new ProductFournisseur($this->db);
2216 if ($prod->fetch($fk_product) > 0) {
2217 $product_type = $prod->type;
2218 $label = $prod->label;
2219 $fk_prod_fourn_price = 0;
2220
2221 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2222 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2223 $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
2224 if ($result > 0) {
2225 if (empty($pu)) {
2226 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2227 }
2228 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2229 // is remise percent not keyed but present for the product we add it
2230 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2231 $remise_percent = $prod->remise_percent;
2232 }
2233 }
2234 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2235 $langs->load("errors");
2236 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2237 $this->db->rollback();
2238 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2239 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2240 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2241 return -1;
2242 }
2243 if ($result == -1) {
2244 $langs->load("errors");
2245 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2246 $this->db->rollback();
2247 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2248 return -1;
2249 }
2250 if ($result < -1) {
2251 $this->error = $prod->error;
2252 $this->db->rollback();
2253 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2254 return -1;
2255 }
2256 } else {
2257 $this->error = $prod->error;
2258 $this->db->rollback();
2259 return -1;
2260 }
2261 }
2262 } else {
2263 $product_type = $type;
2264 }
2265
2266 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2267 $pu = 0;
2268 }
2269
2270 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2271
2272 // Clean vat code
2273 $reg = array();
2274 $vat_src_code = '';
2275 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2276 $vat_src_code = $reg[1];
2277 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2278 }
2279
2280 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2281 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2282 // and this is done at the line level, which has its own VAT rate
2283
2284 $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);
2285 $total_ht = $tabprice[0];
2286 $total_tva = $tabprice[1];
2287 $total_ttc = $tabprice[2];
2288 $total_localtax1 = $tabprice[9];
2289 $total_localtax2 = $tabprice[10];
2290 $pu_ht = $tabprice[3];
2291
2292 // MultiCurrency
2293 $multicurrency_total_ht = $tabprice[16];
2294 $multicurrency_total_tva = $tabprice[17];
2295 $multicurrency_total_ttc = $tabprice[18];
2296 $pu_ht_devise = $tabprice[19];
2297
2298 // Check parameters
2299 if ($type < 0) {
2300 return -1;
2301 }
2302
2303 if ($rang < 0) {
2304 $rangmax = $this->line_max();
2305 $rang = $rangmax + 1;
2306 }
2307
2308 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2309 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2310 $apply_abs_price_on_credit_note = false;
2311 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2312 $apply_abs_price_on_credit_note = true;
2313 }
2314
2315 // Insert line
2316 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2317
2318 $supplierinvoiceline->context = $this->context;
2319
2320 $supplierinvoiceline->fk_facture_fourn = $this->id;
2321 //$supplierinvoiceline->label=$label; // deprecated
2322 $supplierinvoiceline->desc = $desc;
2323 $supplierinvoiceline->ref_supplier = $ref_supplier;
2324
2325 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2326
2327 $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
2328 // Only keep the TTC unit price when the line was entered including tax, so it acts as a reliable "TTC entry mode" marker.
2329 $supplierinvoiceline->subprice_ttc = ($price_base_type === 'TTC') ? ($apply_abs_price_on_credit_note ? -abs((float) $tabprice[5]) : (float) $tabprice[5]) : 0;
2330
2331 $supplierinvoiceline->vat_src_code = $vat_src_code;
2332 $supplierinvoiceline->tva_tx = $txtva;
2333 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2334 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2335 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2336 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2337
2338 $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
2339 $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
2340 $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
2341 $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
2342 $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
2343
2344 $supplierinvoiceline->fk_product = $fk_product;
2345 $supplierinvoiceline->product_type = $type;
2346 $supplierinvoiceline->remise_percent = $remise_percent;
2347 $supplierinvoiceline->date_start = $date_start;
2348 $supplierinvoiceline->date_end = $date_end;
2349 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2350 $supplierinvoiceline->rang = $rang;
2351 $supplierinvoiceline->info_bits = $info_bits;
2352 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2353
2354
2355 $supplierinvoiceline->special_code = (int) $special_code;
2356 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2357 $supplierinvoiceline->origin = $this->origin;
2358 $supplierinvoiceline->origin_type = $origin_type;
2359 $supplierinvoiceline->origin_id = $origin_id;
2360 $supplierinvoiceline->fk_unit = $fk_unit;
2361
2362 // Multicurrency
2363 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2364 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2365 $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
2366
2367 $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
2368 $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
2369 $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
2370
2371 if (is_array($array_options) && count($array_options) > 0) {
2372 $supplierinvoiceline->array_options = $array_options;
2373 }
2374
2375 $result = $supplierinvoiceline->insert($notrigger);
2376 if ($result > 0) {
2377 // Update denormalized fields at the order level
2378 $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.
2379
2380 if ($result > 0) {
2381 if (!isset($this->context['createfromclone'])) {
2382 if (!empty($fk_parent_line)) {
2383 // Always reorder if child line
2384 $this->line_order(true, 'DESC');
2385 } elseif ($rang > 0 && $rang <= count($this->lines)) {
2386 // Update all rank of all other lines starting from the same $ranktouse
2387 $linecount = count($this->lines);
2388 for ($ii = $rang; $ii <= $linecount; $ii++) {
2389 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2390 }
2391 }
2392
2393 $this->lines[] = $supplierinvoiceline;
2394 }
2395
2396 $this->db->commit();
2397 return $supplierinvoiceline->id;
2398 } else {
2399 $this->error = $this->db->error();
2400 $this->db->rollback();
2401 return -1;
2402 }
2403 } else {
2404 $this->error = $supplierinvoiceline->error;
2405 $this->errors = $supplierinvoiceline->errors;
2406 $this->db->rollback();
2407 return -2;
2408 }
2409 } else {
2410 return 0;
2411 }
2412 }
2413
2439 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)
2440 {
2441 global $mysoc, $langs;
2442
2443 if (!$this->isLineOfObject($id)) {
2444 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2445 return -1;
2446 }
2447
2448 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);
2449 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2450
2451 $pu = price2num($pu);
2452 $qty = price2num($qty);
2453 $remise_percent = (float) price2num($remise_percent);
2454 $pu_devise = price2num($pu_devise);
2455
2456 // Check parameters
2457 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2458 if ($type < 0) {
2459 return -1;
2460 }
2461
2462 if ($date_start && $date_end && $date_start > $date_end) {
2463 $langs->load("errors");
2464 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2465 return -1;
2466 }
2467
2468 // Clean parameters
2469 if (empty($vatrate)) {
2470 $vatrate = 0;
2471 }
2472 if (empty($txlocaltax1)) {
2473 $txlocaltax1 = 0;
2474 }
2475 if (empty($txlocaltax2)) {
2476 $txlocaltax2 = 0;
2477 }
2478
2479 $txlocaltax1 = (float) price2num($txlocaltax1);
2480 $txlocaltax2 = (float) price2num($txlocaltax2);
2481
2482 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2483 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2484 // and this is done at the line level, which has its own VAT rate
2485
2486 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2487
2488 $reg = array();
2489
2490 // Clean vat code
2491 $vat_src_code = '';
2492 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2493 $vat_src_code = $reg[1];
2494 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2495 }
2496
2497 $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);
2498 $total_ht = $tabprice[0];
2499 $total_tva = $tabprice[1];
2500 $total_ttc = $tabprice[2];
2501 $pu_ht = $tabprice[3];
2502 $pu_tva = $tabprice[4];
2503 $pu_ttc = $tabprice[5];
2504 $total_localtax1 = $tabprice[9];
2505 $total_localtax2 = $tabprice[10];
2506
2507 // MultiCurrency
2508 $multicurrency_total_ht = $tabprice[16];
2509 $multicurrency_total_tva = $tabprice[17];
2510 $multicurrency_total_ttc = $tabprice[18];
2511 $pu_ht_devise = $tabprice[19];
2512
2513 if (empty($info_bits)) {
2514 $info_bits = 0;
2515 }
2516
2517 // Fetch current line from the database and then clone the object and set it in $oldline property
2518 $line = new SupplierInvoiceLine($this->db);
2519 $line->fetch($id);
2520 $line->fetch_optionals();
2521
2522 $staticline = clone $line;
2523
2524 if ($idproduct) {
2525 $product = new Product($this->db);
2526 $result = $product->fetch($idproduct);
2527 $product_type = $product->type;
2528 } else {
2529 $idproduct = $staticline->fk_product;
2530 $product_type = $type;
2531 }
2532
2533 $line->oldline = $staticline;
2534 $line->context = $this->context;
2535
2536 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2537 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2538 $apply_abs_price_on_credit_note = false;
2539 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2540 $apply_abs_price_on_credit_note = true;
2541 }
2542
2543 $line->desc = $desc;
2544
2545 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2546
2547 $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
2548 // Only keep the TTC unit price when the line was entered including tax, so it acts as a reliable "TTC entry mode" marker.
2549 $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
2550
2551 $line->remise_percent = $remise_percent;
2552 $line->ref_supplier = $ref_supplier;
2553
2554 $line->date_start = $date_start;
2555 $line->date_end = $date_end;
2556
2557 $line->vat_src_code = $vat_src_code;
2558 $line->tva_tx = $vatrate;
2559 $line->localtax1_tx = $txlocaltax1;
2560 $line->localtax2_tx = $txlocaltax2;
2561 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2562 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2563
2564 $line->total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht);
2565 $line->total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva);
2566 $line->total_localtax1 = (float) $total_localtax1;
2567 $line->total_localtax2 = (float) $total_localtax2;
2568 $line->total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc);
2569
2570 $line->fk_product = $idproduct;
2571 $line->product_type = $product_type;
2572 $line->info_bits = $info_bits;
2573 $line->fk_unit = $fk_unit;
2574 $line->rang = $rang;
2575
2576 if (is_array($array_options) && count($array_options) > 0) {
2577 // We replace values in this->line->array_options only for entries defined into $array_options
2578 foreach ($array_options as $key => $value) {
2579 $line->array_options[$key] = $array_options[$key];
2580 }
2581 }
2582
2583 // Multicurrency
2584 $line->multicurrency_subprice = (float) $pu_ht_devise;
2585 $line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2586 $line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2587 $line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2588
2589 $res = $line->update($notrigger);
2590
2591 if ($res < 1) {
2592 $this->setErrorsFromObject($line);
2593 } else {
2594 // Update total price into invoice record
2595 $res = $this->update_price(1, 'auto', 0, $this->thirdparty);
2596 }
2597
2598 return $res;
2599 }
2600
2608 public function deleteLine($rowid, $notrigger = 0)
2609 {
2610 if (!$rowid) {
2611 $rowid = $this->id;
2612 }
2613
2614 $line = new SupplierInvoiceLine($this->db);
2615
2616 if ($line->fetch($rowid) < 1) {
2617 return -1;
2618 }
2619 if ($this->id > 0 && (int) $line->fk_facture_fourn !== (int) $this->id) {
2620 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2621 return -1;
2622 }
2623
2624 $this->db->begin();
2625
2626 // Free the discount linked to a line of invoice
2627 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2628 $sql .= ' SET fk_invoice_supplier_line = NULL';
2629 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2630
2631 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2632 $result = $this->db->query($sql);
2633 if (!$result) {
2634 $this->error = $this->db->error();
2635 $this->db->rollback();
2636 return -2;
2637 }
2638
2639 $res = $line->delete($notrigger);
2640
2641 if ($res < 1) {
2642 $this->errors[] = $line->error;
2643 $this->db->rollback();
2644 return -3;
2645 } else {
2646 $res = $this->update_price(1);
2647
2648 if ($res > 0) {
2649 $this->db->commit();
2650 return 1;
2651 } else {
2652 $this->db->rollback();
2653 $this->error = $this->db->lasterror();
2654 return -4;
2655 }
2656 }
2657 }
2658
2659
2666 public function info($id)
2667 {
2668 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2669 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2670 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2671 $sql .= ' WHERE c.rowid = '.((int) $id);
2672
2673 $result = $this->db->query($sql);
2674 if ($result) {
2675 if ($this->db->num_rows($result)) {
2676 $obj = $this->db->fetch_object($result);
2677
2678 $this->id = $obj->rowid;
2679
2680 $this->user_creation_id = $obj->fk_user_author;
2681 $this->user_validation_id = $obj->fk_user_valid;
2682 $this->user_modification_id = $obj->fk_user_modif;
2683 $this->date_creation = $this->db->jdate($obj->datec);
2684 $this->date_modification = $this->db->jdate($obj->datem);
2685 //$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)
2686 }
2687 $this->db->free($result);
2688 } else {
2689 dol_print_error($this->db);
2690 }
2691 }
2692
2693 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2702 public function list_replacable_supplier_invoices($socid = 0)
2703 {
2704 // phpcs:enable
2705 global $conf;
2706
2707 $return = array();
2708
2709 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2710 $sql .= " ff.rowid as rowidnext";
2711 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2712 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2713 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2714 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2715 $sql .= " AND f.entity = ".((int) $conf->entity);
2716 $sql .= " AND f.paye = 0"; // Not closed completely
2717 $sql .= " AND pf.fk_paiementfourn IS NULL"; // No payment already done
2718 $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)
2719 if ($socid > 0) {
2720 $sql .= " AND f.fk_soc = ".((int) $socid);
2721 }
2722 $sql .= " ORDER BY f.ref";
2723
2724 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2725 $resql = $this->db->query($sql);
2726 if ($resql) {
2727 while ($obj = $this->db->fetch_object($resql)) {
2728 $return[$obj->rowid] = array(
2729 'id' => $obj->rowid,
2730 'ref' => $obj->ref,
2731 'status' => $obj->fk_statut
2732 );
2733 }
2734 //print_r($return);
2735 return $return;
2736 } else {
2737 $this->error = $this->db->error();
2738 return -1;
2739 }
2740 }
2741
2742 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2751 public function list_qualified_avoir_supplier_invoices($socid = 0)
2752 {
2753 // phpcs:enable
2754 global $conf;
2755
2756 $return = array();
2757
2758 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye as paid, pf.fk_paiementfourn";
2759 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2760 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2761 $sql .= " WHERE f.entity = ".((int) $conf->entity);
2762 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2763 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2764 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2765 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2766 if ($socid > 0) {
2767 $sql .= " AND f.fk_soc = ".((int) $socid);
2768 }
2769 $sql .= " ORDER BY f.ref";
2770
2771 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2772 $resql = $this->db->query($sql);
2773 if ($resql) {
2774 while ($obj = $this->db->fetch_object($resql)) {
2775 $qualified = 0;
2776 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2777 $qualified = 1;
2778 }
2779 if ($obj->fk_statut == self::STATUS_CLOSED) {
2780 $qualified = 1;
2781 }
2782 if ($qualified) {
2783 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2784 $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);
2785 }
2786 }
2787
2788 return $return;
2789 } else {
2790 $this->error = $this->db->error();
2791 return -1;
2792 }
2793 }
2794
2795 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2802 public function load_board($user)
2803 {
2804 // phpcs:enable
2805 global $conf, $langs;
2806
2807 $now = dol_now();
2808
2809 // The count, the total and the number of late invoices are computed by the database: reading every unpaid invoice to
2810 // count them in PHP took seconds on the home page of an instance with a lot of unpaid invoices. An invoice is late when
2811 // it has a due date and that date is before now minus the warning delay, the rule of hasDelay() for a validated invoice.
2812 $sql = 'SELECT COUNT(ff.rowid) as nb, SUM(ff.total_ht) as total,';
2813 $sql .= " SUM(CASE WHEN ff.date_lim_reglement IS NOT NULL AND ff.date_lim_reglement < '".$this->db->idate($now - $conf->facture->fournisseur->warning_delay)."' THEN 1 ELSE 0 END) as nblate";
2814 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2815 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2816 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2817 }
2818 $sql .= ' WHERE ff.paye = 0';
2819 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2820 $sql .= " AND ff.entity = ".((int) $conf->entity);
2821 if ($user->socid) {
2822 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2823 }
2824
2825 $resql = $this->db->query($sql);
2826 if ($resql) {
2827 $langs->load("bills");
2828
2829 $response = new WorkboardResponse();
2830 $response->warning_delay = $conf->warning_delays['supplier_invoice'] / 60 / 60 / 24;
2831 $response->label = $langs->trans("SupplierBillsToPay");
2832 $response->labelShort = $langs->trans("StatusToPay");
2833
2834 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2835 $response->img = img_object($langs->trans("Bills"), "bill");
2836
2837 $obj = $this->db->fetch_object($resql);
2838 if ($obj) {
2839 $response->nbtodo = (int) $obj->nb;
2840 $response->total = (float) $obj->total;
2841 $response->nbtodolate = (int) $obj->nblate;
2842 if ($response->nbtodolate > 0) {
2843 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2844 }
2845 }
2846
2847 $this->db->free($resql);
2848 return $response;
2849 } else {
2850 dol_print_error($this->db);
2851 $this->error = $this->db->error();
2852 return -1;
2853 }
2854 }
2855
2863 public function getTooltipContentArray($params)
2864 {
2865 global $conf, $langs, $mysoc;
2866
2867 $langs->load('bills');
2868
2869 $datas = [];
2870 $moretitle = $params['moretitle'] ?? '';
2871
2872 $picto = $this->picto;
2873 if ($this->type == self::TYPE_REPLACEMENT) {
2874 $picto .= 'r'; // Replacement invoice
2875 }
2876 if ($this->type == self::TYPE_CREDIT_NOTE) {
2877 $picto .= 'a'; // Credit note
2878 }
2879 if ($this->type == self::TYPE_DEPOSIT) {
2880 $picto .= 'd'; // Deposit invoice
2881 }
2882
2883 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2884 if ($this->type == self::TYPE_REPLACEMENT) {
2885 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplacement").'</u>';
2886 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2887 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2888 } elseif ($this->type == self::TYPE_DEPOSIT) {
2889 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2890 }
2891 if (isset($this->status)) {
2892 $alreadypaid = -1;
2893 if (isset($this->totalpaid)) {
2894 $alreadypaid = $this->totalpaid;
2895 }
2896
2897 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2898 }
2899 if ($moretitle) {
2900 $datas['picto'] .= ' - '.$moretitle;
2901 }
2902 if (!empty($this->ref)) {
2903 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2904 }
2905 if (!empty($this->ref_supplier)) {
2906 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2907 }
2908 if (empty($params['nofetch'])) {
2909 $langs->load('companies');
2910 if (empty($this->thirdparty)) {
2911 $this->fetch_thirdparty();
2912 }
2913 if (is_object($this->thirdparty)) {
2914 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
2915 }
2916 }
2917 if (!empty($this->label)) {
2918 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2919 }
2920 if (!empty($this->date)) {
2921 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2922 }
2923 if (!empty($this->date_echeance)) {
2924 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2925 }
2926 if (!empty($this->total_ht)) {
2927 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2928 }
2929 if (!empty($this->total_tva)) {
2930 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2931 }
2932 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2933 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2934 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2935 }
2936 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2937 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2938 }
2939 if (!empty($this->revenuestamp)) {
2940 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2941 }
2942 if (!empty($this->total_ttc)) {
2943 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2944 }
2945 return $datas;
2946 }
2947
2961 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2962 {
2963 global $langs, $user, $hookmanager;
2964
2965 $result = '';
2966
2967 if ($option == 'withdraw') {
2968 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2969 } elseif ($option == 'document') {
2970 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2971 } else {
2972 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2973 }
2974
2975 if ($short) {
2976 return $url;
2977 }
2978
2979 if ($option !== 'nolink') {
2980 // Add param to save lastsearch_values or not
2981 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2982 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2983 $add_save_lastsearch_values = 1;
2984 }
2985 if ($add_save_lastsearch_values) {
2986 $url .= '&save_lastsearch_values=1';
2987 }
2988 }
2989
2990 $picto = $this->picto;
2991 if ($this->type == self::TYPE_REPLACEMENT) {
2992 $picto .= 'r'; // Replacement invoice
2993 }
2994 if ($this->type == self::TYPE_CREDIT_NOTE) {
2995 $picto .= 'a'; // Credit note
2996 }
2997 if ($this->type == self::TYPE_DEPOSIT) {
2998 $picto .= 'd'; // Deposit invoice
2999 }
3000
3001 $params = [
3002 'id' => $this->id,
3003 'objecttype' => $this->element,
3004 'option' => $option,
3005 'moretitle' => $moretitle,
3006 ];
3007 $classfortooltip = 'classfortooltip';
3008 $dataparams = '';
3009 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3010 $classfortooltip = 'classforajaxtooltip';
3011 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3012 $label = '';
3013 } else {
3014 $label = $this->getTooltipContent($params);
3015 }
3016
3017 $ref = $this->ref;
3018 if (empty($ref)) {
3019 $ref = $this->id;
3020 }
3021
3022 $linkclose = '';
3023 if (empty($notooltip)) {
3024 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3025 $label = $langs->trans("ShowSupplierInvoice");
3026 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
3027 }
3028 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
3029 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3030 }
3031
3032 $linkstart = '<a href="'.$url.'"';
3033 $linkstart .= $linkclose.'>';
3034 $linkend = '</a>';
3035
3036 $result .= $linkstart;
3037 if ($withpicto) {
3038 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
3039 }
3040 if ($withpicto != 2) {
3041 $result .= ($max ? dol_trunc($ref, $max) : $ref);
3042 }
3043 $result .= $linkend;
3044
3045 if ($addlinktonotes) {
3046 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3047 if ($txttoshow) {
3048 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3049 $result .= ' <span class="note inline-block">';
3050 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3051 $result .= img_picto('', 'note');
3052 $result .= '</a>';
3053 $result .= '</span>';
3054 }
3055 }
3056 global $action;
3057 $hookmanager->initHooks(array($this->element . 'dao'));
3058 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3059 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3060 if ($reshook > 0) {
3061 $result = $hookmanager->resPrint;
3062 } else {
3063 $result .= $hookmanager->resPrint;
3064 }
3065 return $result;
3066 }
3067
3078 public function setCategories($categories)
3079 {
3080 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3081 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_INVOICE);
3082 }
3083
3092 public function getNextNumRef($soc, $mode = 'next')
3093 {
3094 global $langs, $conf;
3095 $langs->load("orders");
3096
3097 // Clean parameters (if not defined or using deprecated value)
3098 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
3099 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
3100 }
3101
3102 $mybool = false;
3103
3104 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
3105 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
3106
3107 // Include file with class
3108 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3109
3110 foreach ($dirmodels as $reldir) {
3111 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
3112
3113 // Load file with numbering class (if found)
3114 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3115 }
3116
3117 if (!$mybool) {
3118 dol_print_error(null, "Failed to include file ".$file);
3119 return '';
3120 }
3121
3122 $obj = new $classname();
3123 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3124 $numref = "";
3125 $numref = $obj->getNextValue($soc, $this, $mode);
3126
3127 if ($numref != "") {
3128 return $numref;
3129 } else {
3130 $this->error = $obj->error;
3131 return -1;
3132 }
3133 }
3134
3135
3144 public function initAsSpecimen($option = '')
3145 {
3146 global $langs, $conf;
3147 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3148
3149 $now = dol_now();
3150
3151 // Load array of products prodids
3152 $num_prods = 0;
3153 $prodids = array();
3154
3155 $sql = "SELECT rowid";
3156 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3157 $sql .= " WHERE entity IN (".getEntity('product').")";
3158 $sql .= $this->db->plimit(100);
3159
3160 $resql = $this->db->query($sql);
3161 if ($resql) {
3162 $num_prods = $this->db->num_rows($resql);
3163 $i = 0;
3164 while ($i < $num_prods) {
3165 $i++;
3166 $row = $this->db->fetch_row($resql);
3167 $prodids[$i] = $row[0];
3168 }
3169 }
3170
3171 // Initialise parameters
3172 $this->id = 0;
3173 $this->ref = 'SPECIMEN';
3174 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3175 $this->specimen = 1;
3176 $this->socid = 1;
3177 $this->date = $now;
3178 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3179 $this->cond_reglement_code = 'RECEP';
3180 $this->mode_reglement_code = 'CHQ';
3181
3182 $this->note_public = 'This is a comment (public)';
3183 $this->note_private = 'This is a comment (private)';
3184
3185 $this->multicurrency_tx = 1;
3186 $this->multicurrency_code = $conf->currency;
3187
3188 $xnbp = 0;
3189 if (empty($option) || $option != 'nolines') {
3190 // Lines
3191 $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)
3192 while ($xnbp < $nbp) {
3193 $line = new SupplierInvoiceLine($this->db);
3194 $line->desc = $langs->trans("Description")." ".$xnbp;
3195 $line->qty = 1;
3196 $line->subprice = 100;
3197 $line->price = 100;
3198 $line->tva_tx = 19.6;
3199 $line->localtax1_tx = 0;
3200 $line->localtax2_tx = 0;
3201 if ($xnbp == 2) {
3202 $line->total_ht = 50;
3203 $line->total_ttc = 59.8;
3204 $line->total_tva = 9.8;
3205 $line->remise_percent = 50;
3206 } else {
3207 $line->total_ht = 100;
3208 $line->total_ttc = 119.6;
3209 $line->total_tva = 19.6;
3210 $line->remise_percent = 0;
3211 }
3212
3213 if ($num_prods > 0) {
3214 $prodid = mt_rand(1, $num_prods);
3215 $line->fk_product = $prodids[$prodid];
3216 }
3217 $line->product_type = 0;
3218
3219 $this->lines[$xnbp] = $line;
3220
3221 $this->total_ht += $line->total_ht;
3222 $this->total_tva += $line->total_tva;
3223 $this->total_ttc += $line->total_ttc;
3224
3225 $xnbp++;
3226 }
3227 }
3228
3229 $this->total_ht = $xnbp * 100;
3230 $this->total_tva = $xnbp * 19.6;
3231 $this->total_ttc = $xnbp * 119.6;
3232
3233 return 1;
3234 }
3235
3241 public function loadStateBoard()
3242 {
3243 global $conf, $user;
3244
3245 $this->nb = array();
3246
3247 $clause = "WHERE";
3248
3249 $sql = "SELECT count(f.rowid) as nb";
3250 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3251 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3252 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3253 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3254 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3255 $clause = "AND";
3256 }
3257 $sql .= " ".$clause." f.entity = ".((int) $conf->entity);
3258
3259 $resql = $this->db->query($sql);
3260 if ($resql) {
3261 while ($obj = $this->db->fetch_object($resql)) {
3262 $this->nb["supplier_invoices"] = $obj->nb;
3263 }
3264 $this->db->free($resql);
3265 return 1;
3266 } else {
3267 dol_print_error($this->db);
3268 $this->error = $this->db->error();
3269 return -1;
3270 }
3271 }
3272
3281 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3282 {
3283 global $langs, $hookmanager;
3284
3285 $error = 0;
3286
3287 $object = new FactureFournisseur($this->db);
3288
3289 $this->db->begin();
3290
3291 // Load source object
3292 $object->fetch($fromid);
3293 $objFrom = clone $object;
3294
3295 $object->id = 0;
3296 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3297 $object->status = self::STATUS_DRAFT;
3298
3299 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3300
3301 // Clear fields
3302 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3303 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3304 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3305 $object->fk_facture_source = 0;
3306 $object->date_creation = '';
3307 $object->date_validation = '';
3308 $object->date = (empty($this->date) ? dol_now() : $this->date);
3309 $object->ref_client = '';
3310 $object->close_code = '';
3311 $object->close_note = '';
3312 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3313 $object->note_private = '';
3314 $object->note_public = '';
3315 }
3316
3317 $object->date_echeance = $object->calculate_date_lim_reglement();
3318
3319 // Loop on each line of new invoice
3320 foreach ($object->lines as $i => $line) {
3321 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3322 unset($object->lines[$i]);
3323 }
3324 }
3325
3326 // Create clone
3327 $object->context['createfromclone'] = 'createfromclone';
3328 $result = $object->create($user);
3329
3330 // Other options
3331 if ($result < 0) {
3332 $this->error = $object->error;
3333 $this->errors = $object->errors;
3334 $error++;
3335 }
3336
3337 if (!$error) {
3338 // Hook of thirdparty module
3339 if (is_object($hookmanager)) {
3340 $parameters = array('objFrom' => $objFrom);
3341 $action = '';
3342 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
3343 if ($reshook < 0) {
3344 $this->setErrorsFromObject($hookmanager);
3345 $error++;
3346 }
3347 }
3348 }
3349
3350 unset($object->context['createfromclone']);
3351
3352 // End
3353 if (!$error) {
3354 $this->db->commit();
3355 return $object->id;
3356 } else {
3357 $this->db->rollback();
3358 return -1;
3359 }
3360 }
3361
3373 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3374 {
3375 global $langs;
3376
3377 $langs->load("suppliers");
3378 $outputlangs->load("products");
3379
3380 // Set the model on the model name to use
3381 if (empty($modele)) {
3382 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3383 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3384 } else {
3385 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3386 }
3387 } elseif ($modele == 'auto') {
3388 $modele = 'canelle';
3389 }
3390
3391 if (empty($modele)) {
3392 return 0;
3393 } else {
3394 $modelpath = "core/modules/supplier_invoice/doc/";
3395
3396 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3397 }
3398 }
3399
3404 public function getRights()
3405 {
3406 global $user;
3407
3408 return $user->hasRight("fournisseur", "facture");
3409 }
3410
3419 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3420 {
3421 $tables = array(
3422 'facture_fourn'
3423 );
3424
3425 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3426 }
3427
3436 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3437 {
3438 $tables = array(
3439 'facture_fourn_det'
3440 );
3441
3442 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3443 }
3444
3450 public function hasDelay()
3451 {
3452 global $conf;
3453
3454 $now = dol_now();
3455
3456 if (!$this->date_echeance) {
3457 return false;
3458 }
3459
3460 $status = isset($this->status) ? $this->status : $this->statut;
3461
3462 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3463 }
3464
3470 public function isCreditNoteUsed()
3471 {
3472 $isUsed = false;
3473
3474 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3475 $resql = $this->db->query($sql);
3476 if (!empty($resql)) {
3477 $obj = $this->db->fetch_object($resql);
3478 if (!empty($obj->fk_invoice_supplier)) {
3479 $isUsed = true;
3480 }
3481 }
3482
3483 return $isUsed;
3484 }
3492 public function getKanbanView($option = '', $arraydata = null)
3493 {
3494 global $langs;
3495
3496 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3497
3498 $picto = $this->picto;
3499 if ($this->type == self::TYPE_REPLACEMENT) {
3500 $picto .= 'r'; // Replacement invoice
3501 }
3502 if ($this->type == self::TYPE_CREDIT_NOTE) {
3503 $picto .= 'a'; // Credit note
3504 }
3505 if ($this->type == self::TYPE_DEPOSIT) {
3506 $picto .= 'd'; // Deposit invoice
3507 }
3508
3509 $return = '<div class="box-flex-item box-flex-grow-zero">';
3510 $return .= '<div class="info-box info-box-sm">';
3511 $return .= '<span class="info-box-icon bg-infobox-action">';
3512 $return .= img_picto('', $picto);
3513 $return .= '</span>';
3514 $return .= '<div class="info-box-content">';
3515 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3516 if ($selected >= 0) {
3517 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3518 }
3519 if (!empty($arraydata['thirdparty'])) {
3520 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3521 }
3522 if (!empty($this->date)) {
3523 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3524 }
3525 if (!empty($this->total_ht)) {
3526 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3527 $return .= ' '.$langs->trans("HT");
3528 $return .= '</span>';
3529 }
3530 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3531 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3532
3533 $return .= '</div>';
3534 $return .= '</div>';
3535 $return .= '</div>';
3536
3537 return $return;
3538 }
3539
3546 public function setVATReverseCharge($vatreversecharge)
3547 {
3548 if (!$this->table_element) {
3549 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3550 return -1;
3551 }
3552
3553 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3554
3555 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3556 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3557 $sql .= " WHERE rowid=".((int) $this->id);
3558
3559 if ($this->db->query($sql)) {
3560 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3561 return 1;
3562 } else {
3563 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3564 $this->error = $this->db->error();
3565 return 0;
3566 }
3567 }
3568
3580 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3581 {
3582 global $conf, $langs, $user;
3583
3584 $this->output = '';
3585 $this->error = '';
3586 $nbMailSend = 0;
3587
3588 $error = 0;
3589 $errorsMsg = array();
3590
3591 $langs->load('bills');
3592
3593 if (!isModEnabled(!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3594 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3595 return 0;
3596 }
3597 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3598 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3599 return 0;
3600 }
3601
3602 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3603 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3604 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3605 $formmail = new FormMail($this->db);
3606
3607 $now = dol_now();
3608 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3609
3610 $tmpinvoice = new FactureFournisseur($this->db);
3611
3612 // Label of the event recorded once a reminder is sent. It is also used to not send the same reminder twice the same day.
3613 $labelreminderok = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3614
3615 dol_syslog(__METHOD__." start", LOG_INFO);
3616
3617 // Select all action comm reminder
3618 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3619 if (!empty($paymentmode) && $paymentmode != 'all') {
3620 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3621 }
3622 $sql .= " WHERE f.paye = 0"; // Only unpaid
3623 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3624 if ($datetouse == 'invoicedate') {
3625 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3626 } else {
3627 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3628 }
3629 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3630 if (!empty($paymentmode) && $paymentmode != 'all') {
3631 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3632 }
3633 $sql .= " AND f.type <> ".self::TYPE_CREDIT_NOTE; // A credit note is not something to pay
3634 $sql .= " AND f.total_ttc > 0";
3635 // Exclude invoices for which the same reminder was already sent today
3636 $sql .= " AND NOT EXISTS (SELECT a.id FROM ".MAIN_DB_PREFIX."actioncomm as a";
3637 $sql .= " WHERE a.elementtype = 'invoice_supplier' AND a.fk_element = f.rowid AND a.code = 'AC_EMAIL'";
3638 $sql .= " AND a.label = '".$this->db->escape($labelreminderok)."'";
3639 $sql .= " AND a.datep >= '".$this->db->idate(dol_get_first_hour($now))."')";
3640 if ($datetouse == 'invoicedate') {
3641 $sql .= $this->db->order("datef", "ASC");
3642 } else {
3643 $sql .= $this->db->order("date_lim_reglement", "ASC");
3644 }
3645
3646 $resql = $this->db->query($sql);
3647
3648 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3649 if ($datetouse == 'invoicedate') {
3650 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3651 } else {
3652 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3653 }
3654 if (!empty($paymentmode) && $paymentmode != 'all') {
3655 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3656 }
3657 $this->output .= '<br>';
3658
3659 if ($resql) {
3660 while ($obj = $this->db->fetch_object($resql)) {
3661 $errorforinvoice = 0; // An error on one invoice must not prevent to process the next ones
3662 // Load event
3663 $res = $tmpinvoice->fetch($obj->id);
3664 if ($res > 0) {
3665 $tmpinvoice->fetch_thirdparty();
3666
3667 $outputlangs = new Translate('', $conf);
3668 if ($tmpinvoice->thirdparty->default_lang) {
3669 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3670 $outputlangs->loadLangs(array("main", "suppliers"));
3671 } else {
3672 $outputlangs = $langs;
3673 }
3674
3675 // Select email template according to language of recipient
3676 $templateId = 0;
3677 $templateLabel = '';
3678 if (empty($template) || $template == 'EmailTemplateCode') {
3679 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3680 } else {
3681 if (is_numeric($template)) {
3682 $templateId = $template;
3683 } else {
3684 $templateLabel = $template;
3685 }
3686 }
3687
3688 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3689 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3690 $langs->load("errors");
3691 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3692 return 0;
3693 }
3694
3695 // PREPARE EMAIL
3696 $errormesg = '';
3697
3698 // Make substitution in email content
3699 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $tmpinvoice);
3700
3701 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3702
3703 // Topic
3704 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3705
3706 // Content
3707 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3708
3709 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3710
3711 // Recipient
3712 $to = array();
3713 if ($forcerecipient) { // If a recipient was forced
3714 $to = array($forcerecipient);
3715 } else {
3716 $res = $tmpinvoice->fetch_thirdparty();
3717 $recipient = $tmpinvoice->thirdparty;
3718 if ($res > 0) {
3719 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3720 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3721 foreach ($tmparraycontact as $data_email) {
3722 if (!empty($data_email['email'])) {
3723 $to[] = $data_email['email'];
3724 }
3725 }
3726 }
3727 if (empty($to) && !empty($recipient->email)) {
3728 $to[] = $recipient->email;
3729 }
3730 if (empty($to)) {
3731 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3732 $error++;
3733 $errorforinvoice++;
3734 }
3735 } else {
3736 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3737 $error++;
3738 $errorforinvoice++;
3739 }
3740 }
3741
3742 // Sender
3743 $email_from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3744 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3745 $email_from = (string) $arraymessage->email_from;
3746 }
3747 if (empty($email_from)) {
3748 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3749 $error++;
3750 $errorforinvoice++;
3751 }
3752
3753 if (!$errorforinvoice && !empty($to)) {
3754 $this->db->begin();
3755
3756 $to = implode(',', $to);
3757 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3758 $to = $to.','.$arraymessage->email_to;
3759 }
3760
3761 // Errors Recipient
3762 $errors_to = getDolGlobalString('MAIN_MAIL_ERRORS_TO');
3763
3764 $trackid = 'inv'.$tmpinvoice->id;
3765 $sendcontext = 'standard';
3766
3767 $email_tocc = '';
3768 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3769 $email_tocc = (string) $arraymessage->email_tocc;
3770 }
3771
3772 $email_tobcc = '';
3773 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3774 $email_tobcc = (string) $arraymessage->email_tobcc;
3775 }
3776
3777 // Mail Creation
3778 $cMailFile = new CMailFile($sendTopic, $to, $email_from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3779
3780 // Sending Mail
3781 if ($cMailFile->sendfile()) {
3782 $nbMailSend++;
3783
3784 // Add a line into event table
3785 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3786
3787 // Insert record of emails sent
3788 $actioncomm = new ActionComm($this->db);
3789
3790 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3791 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3792 $actioncomm->contact_id = 0;
3793
3794 $actioncomm->code = 'AC_EMAIL';
3795 $actioncomm->label = $labelreminderok;
3796 $actioncomm->note_private = $sendContent;
3797 $actioncomm->fk_project = $tmpinvoice->fk_project;
3798 $actioncomm->datep = dol_now();
3799 $actioncomm->datef = $actioncomm->datep;
3800 $actioncomm->percentage = -1; // Not applicable
3801 $actioncomm->authorid = $user->id; // User saving action
3802 $actioncomm->userownerid = $user->id; // Owner of action
3803 // Fields when action is an email (content should be added into note)
3804 $actioncomm->email_msgid = $cMailFile->msgid;
3805 $actioncomm->email_subject = $sendTopic;
3806 $actioncomm->email_from = $email_from;
3807 $actioncomm->email_sender = '';
3808 $actioncomm->email_to = $to;
3809 //$actioncomm->email_tocc = $sendtocc;
3810 //$actioncomm->email_tobcc = $sendtobcc;
3811 //$actioncomm->email_subject = $subject;
3812 $actioncomm->errors_to = $errors_to;
3813
3814 $actioncomm->elementtype = 'invoice_supplier';
3815 $actioncomm->elementid = $tmpinvoice->id;
3816 $actioncomm->fk_element = $tmpinvoice->id;
3817
3818 //$actioncomm->extraparams = $extraparams;
3819
3820 $actioncomm->create($user);
3821 } else {
3822 $errormesg = $cMailFile->error.' : '.$to;
3823 $error++;
3824 $errorforinvoice++;
3825
3826 // Add a line into event table
3827 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3828
3829 // Insert record of emails sent
3830 $actioncomm = new ActionComm($this->db);
3831
3832 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3833 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3834 $actioncomm->contact_id = 0;
3835
3836 $actioncomm->code = 'AC_EMAIL';
3837 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3838 $actioncomm->note_private = $errormesg;
3839 $actioncomm->fk_project = $tmpinvoice->fk_project;
3840 $actioncomm->datep = dol_now();
3841 $actioncomm->datef = $actioncomm->datep;
3842 $actioncomm->percentage = -1; // Not applicable
3843 $actioncomm->authorid = $user->id; // User saving action
3844 $actioncomm->userownerid = $user->id; // Owner of action
3845 // Fields when action is an email (content should be added into note)
3846 $actioncomm->email_msgid = $cMailFile->msgid;
3847 $actioncomm->email_from = $email_from;
3848 $actioncomm->email_sender = '';
3849 $actioncomm->email_to = $to;
3850 //$actioncomm->email_tocc = $sendtocc;
3851 //$actioncomm->email_tobcc = $sendtobcc;
3852 //$actioncomm->email_subject = $subject;
3853 $actioncomm->errors_to = $errors_to;
3854
3855 //$actioncomm->extraparams = $extraparams;
3856
3857 $actioncomm->create($user);
3858 }
3859
3860 $this->db->commit(); // We always commit
3861 }
3862
3863 if ($errormesg) {
3864 $errorsMsg[] = $errormesg;
3865 }
3866 } else {
3867 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3868 $error++;
3869 }
3870 }
3871 } else {
3872 $error++;
3873 }
3874
3875 if (!$error) {
3876 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3877
3878 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3879
3880 return 0;
3881 } else {
3882 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3883
3884 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3885
3886 return $error;
3887 }
3888 }
3889}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
$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.
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -> yes (1) I...
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.
getTooltipContent($params)
getTooltipContent
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
isLineOfObject($lineid)
Check that a line belongs to this object, using $this->table_element_line and $this->fk_element.
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:676
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:
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.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
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:492