dolibarr  20.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-2020 Juanjo Menent <jmenent@2byte.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-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
14  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
15  * Copyright (C) 2018-2024 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 MDW <mdeweerd@users.noreply.github.com>
19  *
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License as published by
22  * the Free Software Foundation; either version 3 of the License, or
23  * (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program. If not, see <https://www.gnu.org/licenses/>.
32  */
33 
40 require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
44 
45 if (isModEnabled('accounting')) {
46  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
47  require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
48 }
49 
54 {
58  public $element = 'invoice_supplier';
59 
63  public $table_element = 'facture_fourn';
64 
68  public $table_element_line = 'facture_fourn_det';
69 
73  public $fk_element = 'fk_facture_fourn';
74 
78  public $picto = 'supplier_invoice';
79 
84  public $ismultientitymanaged = 1;
85 
90  public $restrictiononfksoc = 1;
91 
95  protected $table_ref_field = 'ref';
96 
100  public $rowid;
101 
105  public $ref;
106 
110  public $ref_supplier;
111 
116  public $libelle;
120  public $label;
121 
122  //Check constants for types
123  public $type = self::TYPE_STANDARD;
124 
131  public $statut;
132 
138  public $status;
139 
146  public $fk_statut;
147 
153  public $paye;
158  public $paid;
159 
164  public $author;
165 
171  public $datec;
172 
178  public $date_echeance;
179 
184  public $amount = 0;
189  public $remise = 0;
190 
195  public $tva;
196 
197  // Warning: Do not set default value into property definition. it must stay null.
198  // For example to avoid to have substitution done when object is generic and not yet defined.
200  public $localtax1;
202  public $localtax2;
204  public $total_ht;
206  public $total_tva;
208  public $total_localtax1;
210  public $total_localtax2;
212  public $total_ttc;
213 
219  public $note;
221  public $note_private;
223  public $note_public;
225  public $propalid;
226 
230  public $fk_account; // default bank account
231 
235  public $transport_mode_id;
236 
240  public $vat_reverse_charge;
241 
242  public $extraparams = array();
243 
248  public $lines = array();
249 
254  public $fournisseur;
255 
257 
260  public $fk_facture_source;
261 
263  public $fac_rec;
265  public $fk_fac_rec_source;
266 
267  public $fields = array(
268  'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
269  'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
270  'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
271  'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
272  'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
273  'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
274  'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
275  'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
276  'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
277  'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
278  'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
279  'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
280  'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
281  'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
282  'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
283  'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
284  'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
285  'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
286  'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
287  'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
288  'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
289  'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
290  'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
291  'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
292  'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
293  'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
294  'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
295  'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
296  'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
297  'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
298  'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
299  'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
300  'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
301  'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
302  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
303  'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
304  'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
305  'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
306  'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
307  'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
308  'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
309  'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
310  'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
311  'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
312  'date_pointoftax' => array('type' => 'date', 'label' => 'Date pointoftax', 'enabled' => 1, 'visible' => -1, 'position' => 235),
313  'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
314  'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
315  'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
316  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
317  );
318 
319 
323  const TYPE_STANDARD = 0;
324 
328  const TYPE_REPLACEMENT = 1;
329 
333  const TYPE_CREDIT_NOTE = 2;
334 
338  const TYPE_DEPOSIT = 3;
339 
343  const STATUS_DRAFT = 0;
344 
348  const STATUS_VALIDATED = 1;
349 
357  const STATUS_CLOSED = 2;
358 
366  const STATUS_ABANDONED = 3;
367 
368  const CLOSECODE_DISCOUNTVAT = 'discount_vat';
369  const CLOSECODE_BADCREDIT = 'badsupplier';
370  const CLOSECODE_ABANDONED = 'abandon';
371  const CLOSECODE_REPLACED = 'replaced';
372 
378  public function __construct($db)
379  {
380  $this->db = $db;
381  }
382 
389  public function create($user)
390  {
391  global $langs, $conf, $hookmanager;
392 
393  $error = 0;
394  $now = dol_now();
395 
396  // Clean parameters
397  if (isset($this->ref_supplier)) {
398  $this->ref_supplier = trim($this->ref_supplier);
399  }
400  if (empty($this->type)) {
401  $this->type = self::TYPE_STANDARD;
402  }
403  if (empty($this->date)) {
404  $this->date = $now;
405  }
406 
407  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
408  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
409  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
410  } else {
411  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
412  }
413  if (empty($this->fk_multicurrency)) {
414  $this->multicurrency_code = $conf->currency;
415  $this->fk_multicurrency = 0;
416  $this->multicurrency_tx = 1;
417  }
418 
419  $this->db->begin();
420 
421  // Create invoice from a template recurring invoice
422  if ($this->fac_rec > 0) {
423  $this->fk_fac_rec_source = $this->fac_rec;
424 
425  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
426  $_facrec = new FactureFournisseurRec($this->db);
427  $result = $_facrec->fetch($this->fac_rec);
428  $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
429 
430  // Define some dates
431  if (!empty($_facrec->frequency)) {
432  $originaldatewhen = $_facrec->date_when;
433  $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
434  $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
435  $this->socid = $_facrec->socid;
436  } else {
437  $originaldatewhen = 0;
438  $nextdatewhen = 0;
439  $previousdaynextdatewhen = 0;
440  }
441 
442  $this->entity = $_facrec->entity; // Invoice created in same entity than template
443 
444  // Fields coming from GUI
445  // @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
446  // set by posted page with $object->xxx = ... and this section should be removed.
447  $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
448  $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
449  $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
450  $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
451  $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
452  $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
453  $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
454 
455  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
456  $this->total_ht = $_facrec->total_ht;
457  $this->total_ttc = $_facrec->total_ttc;
458 
459  // Fields always coming from template
460  $this->fk_incoterms = $_facrec->fk_incoterms;
461  $this->location_incoterms = $_facrec->location_incoterms;
462 
463  // Clean parameters
464  if (! $this->type) {
465  $this->type = self::TYPE_STANDARD;
466  }
467  if (!empty(GETPOST('ref_supplier'))) {
468  $this->ref_supplier = trim($this->ref_supplier);
469  } else {
470  $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
471  }
472  $this->note_public = trim($this->note_public);
473  $this->note_private = trim($this->note_private);
474  $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
475 
476  $this->array_options = $_facrec->array_options;
477 
478  if (! $this->mode_reglement_id) {
479  $this->mode_reglement_id = 0;
480  }
481  $this->status = self::STATUS_DRAFT;
482  $this->statut = self::STATUS_DRAFT; // deprecated
483 
484  $this->linked_objects = $_facrec->linkedObjectsIds;
485  // We do not add link to template invoice or next invoice will be linked to all generated invoices
486  //$this->linked_objects['facturerec'][0] = $this->fac_rec;
487 
488  $forceduedate = $this->calculate_date_lim_reglement();
489 
490  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
491  if ($_facrec->frequency > 0) {
492  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
493  if (empty($_facrec->date_when)) {
494  $_facrec->date_when = $now;
495  }
496  $next_date = $_facrec->getNextDate(); // Calculate next date
497  $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
498  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
499  $result = $_facrec->setNextDate($next_date, 1);
500  }
501 
502  // Define lang of customer
503  $outputlangs = $langs;
504  $newlang = '';
505 
506  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
507  $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
508  }
509  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) {
510  $newlang = $this->default_lang; // for thirdparty
511  }
512  if (!empty($newlang)) {
513  $outputlangs = new Translate("", $conf);
514  $outputlangs->setDefaultLang($newlang);
515  } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
516  $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
517  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
518  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
519  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
520  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
521  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
522  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
523  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
524  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
525  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
526  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
527  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
528  $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
529  $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
530  $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
531 
532  complete_substitutions_array($substitutionarray, $outputlangs);
533 
534  $this->note_public = make_substitutions($this->note_public, $substitutionarray);
535  $this->note_private = make_substitutions($this->note_private, $substitutionarray);
536  }
537 
538  // Define due date if not already defined
539  if (!empty($forceduedate)) {
540  $this->date_echeance = $forceduedate;
541  }
542 
543  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
544  $sql .= "ref";
545  $sql .= ", ref_supplier";
546  $sql .= ", ref_ext";
547  $sql .= ", entity";
548  $sql .= ", type";
549  $sql .= ", subtype";
550  $sql .= ", libelle";
551  $sql .= ", fk_soc";
552  $sql .= ", datec";
553  $sql .= ", datef";
554  $sql .= ", vat_reverse_charge";
555  $sql .= ", fk_projet";
556  $sql .= ", fk_cond_reglement";
557  $sql .= ", fk_mode_reglement";
558  $sql .= ", fk_account";
559  $sql .= ", note_private";
560  $sql .= ", note_public";
561  $sql .= ", fk_user_author";
562  $sql .= ", date_lim_reglement";
563  $sql .= ", fk_incoterms, location_incoterms";
564  $sql .= ", fk_multicurrency";
565  $sql .= ", multicurrency_code";
566  $sql .= ", multicurrency_tx";
567  $sql .= ", fk_facture_source";
568  $sql .= ", fk_fac_rec_source";
569  $sql .= ")";
570  $sql .= " VALUES (";
571  $sql .= "'(PROV)'";
572  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
573  $sql .= ", '".$this->db->escape($this->ref_ext)."'";
574  $sql .= ", ".((int) $conf->entity);
575  $sql .= ", '".$this->db->escape($this->type)."'";
576  $sql .= ", ".((int) $this->subtype);
577  $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
578  $sql .= ", ".((int) $this->socid);
579  $sql .= ", '".$this->db->idate($now)."'";
580  $sql .= ", '".$this->db->idate($this->date)."'";
581  $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0);
582  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
583  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
584  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
585  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
586  $sql .= ", '".$this->db->escape($this->note_private)."'";
587  $sql .= ", '".$this->db->escape($this->note_public)."'";
588  $sql .= ", ".((int) $user->id).",";
589  $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
590  $sql .= ", ".(int) $this->fk_incoterms;
591  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
592  $sql .= ", ".(int) $this->fk_multicurrency;
593  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
594  $sql .= ", ".(float) $this->multicurrency_tx;
595  $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
596  $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
597  $sql .= ")";
598 
599  dol_syslog(get_class($this)."::create", LOG_DEBUG);
600  $resql = $this->db->query($sql);
601  if ($resql) {
602  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
603 
604  // Update ref with new one
605  $this->ref = '(PROV'.$this->id.')';
606  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
607 
608  dol_syslog(get_class($this)."::create", LOG_DEBUG);
609  $resql = $this->db->query($sql);
610  if (!$resql) {
611  $error++;
612  }
613 
614  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
615  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
616  }
617 
618  // Add object linked
619  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
620  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
621  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, ...))
622  foreach ($tmp_origin_id as $origin_id) {
623  $ret = $this->add_object_linked($origin, $origin_id);
624  if (!$ret) {
625  dol_print_error($this->db);
626  $error++;
627  }
628  }
629  } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
630  $origin_id = $tmp_origin_id;
631  $ret = $this->add_object_linked($origin, $origin_id);
632  if (!$ret) {
633  dol_print_error($this->db);
634  $error++;
635  }
636  }
637  }
638  }
639 
640  if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
641  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
642  foreach ($this->lines as $i => $val) {
643  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
644  $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').')';
645 
646  $resql_insert = $this->db->query($sql);
647  if ($resql_insert) {
648  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
649 
650  $res = $this->updateline(
651  $idligne,
652  $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
653  $this->lines[$i]->subprice,
654  $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
655  $this->lines[$i]->localtax1_tx,
656  $this->lines[$i]->localtax2_tx,
657  $this->lines[$i]->qty,
658  $this->lines[$i]->fk_product,
659  'HT',
660  (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
661  $this->lines[$i]->product_type,
662  $this->lines[$i]->remise_percent,
663  false,
664  $this->lines[$i]->date_start,
665  $this->lines[$i]->date_end,
666  $this->lines[$i]->array_options,
667  $this->lines[$i]->fk_unit,
668  $this->lines[$i]->multicurrency_subprice,
669  $this->lines[$i]->ref_supplier
670  );
671  } else {
672  $this->error = $this->db->lasterror();
673  $this->db->rollback();
674  return -5;
675  }
676  }
677  } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
678  dol_syslog("There is ".count($this->lines)." lines that are array lines");
679  foreach ($this->lines as $i => $val) {
680  $line = $this->lines[$i];
681 
682  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
683  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
684  if (!is_object($line)) {
685  $line = (object) $line;
686  }
687 
688  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
689  $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').')';
690 
691  $resql_insert = $this->db->query($sql);
692  if ($resql_insert) {
693  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
694 
695  $this->updateline(
696  $idligne,
697  $line->desc ? $line->desc : $line->description,
698  $line->subprice,
699  $line->tva_tx,
700  $line->localtax1_tx,
701  $line->localtax2_tx,
702  $line->qty,
703  $line->fk_product,
704  'HT',
705  (!empty($line->info_bits) ? $line->info_bits : ''),
706  $line->product_type,
707  $line->remise_percent,
708  0,
709  $line->date_start,
710  $line->date_end,
711  $line->array_options,
712  $line->fk_unit,
713  $line->multicurrency_subprice,
714  $line->ref_supplier
715  );
716  } else {
717  $this->error = $this->db->lasterror();
718  $this->db->rollback();
719  return -5;
720  }
721  }
722  }
723 
724  /*
725  * Insert lines of template invoices
726  */
727  if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
728  foreach ($_facrec->lines as $i => $val) {
729  if ($_facrec->lines[$i]->fk_product) {
730  $prod = new Product($this->db);
731  $res = $prod->fetch($_facrec->lines[$i]->fk_product);
732  }
733 
734  // For line from template invoice, we use data from template invoice
735  /*
736  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
737  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
738  if (empty($tva_tx)) $tva_npr=0;
739  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
740  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
741  */
742  $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
743  $tva_npr = $_facrec->lines[$i]->info_bits;
744  if (empty($tva_tx)) {
745  $tva_npr = 0;
746  }
747  $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
748  $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
749 
750  $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
751  $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
752 
753  // If buyprice not defined from template invoice, we try to guess the best value
754  if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
755  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
756  $producttmp = new ProductFournisseur($this->db);
757  $producttmp->fetch($_facrec->lines[$i]->fk_product);
758 
759  // If margin module defined on costprice, we try the costprice
760  // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
761  // else we get the best supplier price
762  if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
763  $buyprice = $producttmp->cost_price;
764  } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
765  $buyprice = $producttmp->pmp;
766  } else {
767  if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
768  if ($producttmp->product_fourn_price_id > 0) {
769  $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
770  }
771  }
772  }
773  }
774 
775  $result_insert = $this->addline(
776  $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
777  $_facrec->lines[$i]->pu_ht,
778  $tva_tx,
779  $localtax1_tx,
780  $localtax2_tx,
781  $_facrec->lines[$i]->qty,
782  $_facrec->lines[$i]->fk_product,
783  $_facrec->lines[$i]->remise_percent,
784  ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
785  ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
786  0,
787  $_facrec->lines[$i]->info_bits,
788  'HT',
789  0,
790  $_facrec->lines[$i]->rang,
791  false,
792  $_facrec->lines[$i]->array_options,
793  $_facrec->lines[$i]->fk_unit,
794  0,
795  0,
796  $_facrec->lines[$i]->ref_supplier,
797  $_facrec->lines[$i]->special_code,
798  0,
799  0
800  );
801  if ($result_insert < 0) {
802  $error++;
803  $this->error = $this->db->error();
804  break;
805  }
806  }
807  }
808 
809 
810  // Update total price
811  $result = $this->update_price(1);
812  if ($result > 0) {
813  // Actions on extra fields
814  if (!$error) {
815  $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
816  if ($result < 0) {
817  $error++;
818  }
819  }
820 
821  if (!$error) {
822  // Call trigger
823  $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
824  if ($result < 0) {
825  $error++;
826  }
827  // End call triggers
828  }
829 
830  if (!$error) {
831  $this->db->commit();
832  return $this->id;
833  } else {
834  $this->db->rollback();
835  return -4;
836  }
837  } else {
838  $this->error = $langs->trans('FailedToUpdatePrice');
839  $this->db->rollback();
840  return -3;
841  }
842  } else {
843  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
844  $this->error = $langs->trans('ErrorRefAlreadyExists');
845  $this->db->rollback();
846  return -1;
847  } else {
848  $this->error = $this->db->lasterror();
849  $this->db->rollback();
850  return -2;
851  }
852  }
853  }
854 
863  public function fetch($id = 0, $ref = '', $ref_ext = '')
864  {
865  if (empty($id) && empty($ref) && empty($ref_ext)) {
866  return -1;
867  }
868 
869  $sql = "SELECT";
870  $sql .= " t.rowid,";
871  $sql .= " t.ref,";
872  $sql .= " t.ref_supplier,";
873  $sql .= " t.ref_ext,";
874  $sql .= " t.entity,";
875  $sql .= " t.type,";
876  $sql .= " t.subtype,";
877  $sql .= " t.fk_soc,";
878  $sql .= " t.datec,";
879  $sql .= " t.datef,";
880  $sql .= " t.tms,";
881  $sql .= " t.libelle as label,";
882  $sql .= " t.paye,";
883  $sql .= " t.close_code,";
884  $sql .= " t.close_note,";
885  $sql .= " t.tva,";
886  $sql .= " t.localtax1,";
887  $sql .= " t.localtax2,";
888  $sql .= " t.total_ht,";
889  $sql .= " t.total_tva,";
890  $sql .= " t.total_ttc,";
891  $sql .= " t.fk_statut as status,";
892  $sql .= " t.fk_user_author,";
893  $sql .= " t.fk_user_valid,";
894  $sql .= " t.fk_facture_source,";
895  $sql .= " t.vat_reverse_charge,";
896  $sql .= " t.fk_fac_rec_source,";
897  $sql .= " t.fk_projet as fk_project,";
898  $sql .= " t.fk_cond_reglement,";
899  $sql .= " t.fk_account,";
900  $sql .= " t.fk_mode_reglement,";
901  $sql .= " t.date_lim_reglement,";
902  $sql .= " t.note_private,";
903  $sql .= " t.note_public,";
904  $sql .= " t.model_pdf,";
905  $sql .= " t.last_main_doc,";
906  $sql .= " t.import_key,";
907  $sql .= " t.extraparams,";
908  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
909  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
910  $sql .= ' s.nom as socnom, s.rowid as socid,';
911  $sql .= ' t.fk_incoterms, t.location_incoterms,';
912  $sql .= " i.libelle as label_incoterms,";
913  $sql .= ' t.fk_transport_mode,';
914  $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
915  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
916  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
917  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
918  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
919  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
920  if ($id) {
921  $sql .= " WHERE t.rowid = ".((int) $id);
922  } else {
923  $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
924  if ($ref) {
925  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
926  }
927  if ($ref_ext) {
928  $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
929  }
930  }
931 
932  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
933  $resql = $this->db->query($sql);
934  if ($resql) {
935  if ($this->db->num_rows($resql)) {
936  $obj = $this->db->fetch_object($resql);
937 
938  $this->id = $obj->rowid;
939  $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
940 
941  $this->ref_supplier = $obj->ref_supplier;
942  $this->ref_ext = $obj->ref_ext;
943  $this->entity = $obj->entity;
944  $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
945  $this->subtype = (int) $obj->subtype;
946  $this->socid = $obj->fk_soc;
947  $this->datec = $this->db->jdate($obj->datec);
948  $this->date = $this->db->jdate($obj->datef);
949  //$this->datep = $this->db->jdate($obj->datef);
950  $this->tms = $this->db->jdate($obj->tms);
951  $this->libelle = $obj->label; // deprecated
952  $this->label = $obj->label;
953  $this->paye = $obj->paye;
954  $this->paid = $obj->paye;
955  $this->close_code = $obj->close_code;
956  $this->close_note = $obj->close_note;
957  $this->total_localtax1 = $obj->localtax1;
958  $this->total_localtax2 = $obj->localtax2;
959  $this->total_ht = $obj->total_ht;
960  $this->total_tva = $obj->total_tva;
961  $this->total_ttc = $obj->total_ttc;
962  $this->status = $obj->status;
963  $this->statut = $obj->status; // For backward compatibility
964  $this->fk_statut = $obj->status; // For backward compatibility
965  $this->user_creation_id = $obj->fk_user_author;
966  $this->author = $obj->fk_user_author; // deprecated
967  $this->user_validation_id = $obj->fk_user_valid;
968  $this->fk_facture_source = $obj->fk_facture_source;
969  $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
970  $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
971  $this->fk_project = $obj->fk_project;
972  $this->cond_reglement_id = $obj->fk_cond_reglement;
973  $this->cond_reglement_code = $obj->cond_reglement_code;
974  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
975  $this->cond_reglement_label = $obj->cond_reglement_label;
976  $this->cond_reglement_doc = $obj->cond_reglement_doc;
977  $this->fk_account = $obj->fk_account;
978  $this->mode_reglement_id = $obj->fk_mode_reglement;
979  $this->mode_reglement_code = $obj->mode_reglement_code;
980  $this->mode_reglement = $obj->mode_reglement_label;
981  $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
982  $this->note = $obj->note_private; // deprecated
983  $this->note_private = $obj->note_private;
984  $this->note_public = $obj->note_public;
985  $this->model_pdf = $obj->model_pdf;
986  $this->last_main_doc = $obj->last_main_doc;
987  $this->import_key = $obj->import_key;
988 
989  //Incoterms
990  $this->fk_incoterms = $obj->fk_incoterms;
991  $this->location_incoterms = $obj->location_incoterms;
992  $this->label_incoterms = $obj->label_incoterms;
993  $this->transport_mode_id = $obj->fk_transport_mode;
994 
995  // Multicurrency
996  $this->fk_multicurrency = $obj->fk_multicurrency;
997  $this->multicurrency_code = $obj->multicurrency_code;
998  $this->multicurrency_tx = $obj->multicurrency_tx;
999  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1000  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1001  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1002 
1003  $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1004 
1005  $this->socid = $obj->socid;
1006 
1007  // Retrieve all extrafield
1008  // fetch optionals attributes and labels
1009  $this->fetch_optionals();
1010 
1011  $result = $this->fetch_lines();
1012  if ($result < 0) {
1013  $this->error = $this->db->lasterror();
1014  return -3;
1015  }
1016  } else {
1017  $this->error = 'Bill with id '.$id.' not found';
1018  dol_syslog(get_class($this).'::fetch '.$this->error);
1019  return 0;
1020  }
1021 
1022  $this->db->free($resql);
1023  return 1;
1024  } else {
1025  $this->error = "Error ".$this->db->lasterror();
1026  return -1;
1027  }
1028  }
1029 
1030 
1031  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1037  public function fetch_lines()
1038  {
1039  // phpcs:enable
1040  $this->lines = array();
1041 
1042  $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';
1043  $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';
1044  $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';
1045  $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';
1046  $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';
1047  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1048  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1049  $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1050  $sql .= ' ORDER BY f.rang, f.rowid';
1051 
1052  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1053 
1054  $resql_rows = $this->db->query($sql);
1055  if ($resql_rows) {
1056  $num_rows = $this->db->num_rows($resql_rows);
1057  if ($num_rows) {
1058  $i = 0;
1059  while ($i < $num_rows) {
1060  $obj = $this->db->fetch_object($resql_rows);
1061 
1062  $line = new SupplierInvoiceLine($this->db);
1063 
1064  $line->id = $obj->rowid;
1065  $line->rowid = $obj->rowid;
1066  $line->description = $obj->line_desc;
1067  $line->desc = $obj->line_desc;
1068  $line->date_start = $obj->date_start;
1069  $line->date_end = $obj->date_end;
1070  $line->product_ref = $obj->product_ref;
1071  $line->ref = $obj->product_ref;
1072  $line->ref_supplier = $obj->ref_supplier;
1073  $line->libelle = $obj->label;
1074  $line->label = $obj->label;
1075  $line->product_barcode = $obj->product_barcode;
1076  $line->product_desc = $obj->product_desc;
1077  $line->subprice = $obj->pu_ht;
1078  $line->pu_ht = $obj->pu_ht;
1079  $line->pu_ttc = $obj->pu_ttc;
1080  $line->vat_src_code = $obj->vat_src_code;
1081  $line->tva_tx = $obj->tva_tx;
1082  $line->localtax1_tx = $obj->localtax1_tx;
1083  $line->localtax2_tx = $obj->localtax2_tx;
1084  $line->localtax1_type = $obj->localtax1_type;
1085  $line->localtax2_type = $obj->localtax2_type;
1086  $line->qty = $obj->qty;
1087  $line->remise_percent = $obj->remise_percent;
1088  $line->fk_remise_except = $obj->fk_remise_except;
1089  //$line->tva = $obj->total_tva; // deprecated
1090  $line->total_ht = $obj->total_ht;
1091  $line->total_ttc = $obj->total_ttc;
1092  $line->total_tva = $obj->total_tva;
1093  $line->total_localtax1 = $obj->total_localtax1;
1094  $line->total_localtax2 = $obj->total_localtax2;
1095  $line->fk_facture_fourn = $obj->fk_facture_fourn;
1096  $line->fk_product = $obj->fk_product;
1097  $line->product_type = $obj->product_type;
1098  $line->product_label = $obj->label;
1099  $line->info_bits = $obj->info_bits;
1100  $line->fk_parent_line = $obj->fk_parent_line;
1101  $line->special_code = $obj->special_code;
1102  $line->rang = $obj->rang;
1103  $line->fk_unit = $obj->fk_unit;
1104 
1105  // Accountancy
1106  $line->fk_accounting_account = $obj->fk_code_ventilation;
1107 
1108  // Multicurrency
1109  $line->fk_multicurrency = $obj->fk_multicurrency;
1110  $line->multicurrency_code = $obj->multicurrency_code;
1111  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1112  $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1113  $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1114  $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1115 
1116  // Extra fields
1117  $line->fetch_optionals();
1118 
1119  $this->lines[$i] = $line;
1120 
1121  $i++;
1122  }
1123  }
1124  $this->db->free($resql_rows);
1125  return 1;
1126  } else {
1127  $this->error = $this->db->error();
1128  dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1129  return -3;
1130  }
1131  }
1132 
1133 
1141  public function update($user = null, $notrigger = 0)
1142  {
1143  global $langs;
1144  $error = 0;
1145 
1146  // Clean parameters
1147  if (empty($this->type)) {
1148  $this->type = self::TYPE_STANDARD;
1149  }
1150  if (isset($this->ref)) {
1151  $this->ref = trim($this->ref);
1152  }
1153  if (isset($this->ref_supplier)) {
1154  $this->ref_supplier = trim($this->ref_supplier);
1155  }
1156  if (isset($this->ref_ext)) {
1157  $this->ref_ext = trim($this->ref_ext);
1158  }
1159  if (isset($this->entity)) {
1160  $this->entity = (int) $this->entity;
1161  }
1162  if (isset($this->type)) {
1163  $this->type = (int) $this->type;
1164  }
1165  if (isset($this->subtype)) {
1166  $this->subtype = (int) $this->subtype;
1167  }
1168  if (isset($this->socid)) {
1169  $this->socid = (int) $this->socid;
1170  }
1171  if (isset($this->label)) {
1172  $this->label = trim($this->label);
1173  }
1174  if (isset($this->paid)) {
1175  $this->paid = (int) (bool) $this->paye;
1176  $this->paye = $this->paid;
1177  } elseif (isset($this->paye)) {
1178  $this->paid = (int) (bool) $this->paye;
1179  $this->paye = $this->paid;
1180  }
1181  if (isset($this->close_code)) {
1182  $this->close_code = trim($this->close_code);
1183  }
1184  if (isset($this->close_note)) {
1185  $this->close_note = trim($this->close_note);
1186  }
1187  if (empty($this->total_ht)) {
1188  $this->total_ht = 0;
1189  }
1190  if (empty($this->total_tva)) {
1191  $this->total_tva = 0;
1192  }
1193  if (isset($this->total_ttc)) {
1194  $this->total_ttc = (float) $this->total_ttc;
1195  }
1196  if (isset($this->status)) {
1197  $this->status = (int) $this->status;
1198  $this->statut = $this->status;
1199  } elseif (isset($this->statut)) {
1200  $this->status = (int) $this->statut;
1201  $this->statut = $this->status;
1202  }
1203  if (isset($this->author)) { // TODO: user_creation_id?
1204  $this->author = (int) $this->author;
1205  }
1206  if (isset($this->fk_user_valid)) {
1207  $this->fk_user_valid = trim($this->fk_user_valid);
1208  }
1209  if (isset($this->fk_facture_source)) {
1210  $this->fk_facture_source = (int) $this->fk_facture_source;
1211  }
1212  if (isset($this->fk_project)) {
1213  if (empty($this->fk_project)) {
1214  $this->fk_project = 0;
1215  } else {
1216  $this->fk_project = (int) $this->fk_project;
1217  }
1218  }
1219  if (isset($this->cond_reglement_id)) {
1220  $this->cond_reglement_id = (int) $this->cond_reglement_id;
1221  }
1222  if (isset($this->note_private)) {
1223  $this->note_private = trim($this->note_private);
1224  $this->note = $this->note_private;
1225  }
1226  if (isset($this->note_public)) {
1227  $this->note_public = trim($this->note_public);
1228  }
1229  if (isset($this->model_pdf)) {
1230  $this->model_pdf = trim($this->model_pdf);
1231  }
1232  if (isset($this->import_key)) {
1233  $this->import_key = trim($this->import_key);
1234  }
1235 
1236 
1237  // Check parameters
1238  // Put here code to add control on parameters values
1239 
1240  // Update request
1241  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1242  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1243  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1244  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1245  $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1246  $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1247  $sql .= " subtype=".((int) $this->subtype).",";
1248  $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1249  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1250  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1251  if (dol_strlen($this->tms) != 0) {
1252  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1253  }
1254  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1255  $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1256  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1257  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1258  $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1259  $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1260  $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1261  $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1262  $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1263  $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1264  $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1265  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1266  $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1267  $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1268  $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1269  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1270  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1271  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1272  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1273  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1274  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1275  $sql .= " WHERE rowid=".((int) $this->id);
1276 
1277  $this->db->begin();
1278 
1279  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1280  $resql = $this->db->query($sql);
1281 
1282  if (!$resql) {
1283  $error++;
1284 
1285  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1286  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1287  } else {
1288  $this->errors[] = "Error ".$this->db->lasterror();
1289  }
1290  }
1291 
1292  if (!$error) {
1293  $result = $this->insertExtraFields();
1294  if ($result < 0) {
1295  $error++;
1296  }
1297  }
1298 
1299  if (!$error) {
1300  if (!$notrigger) {
1301  // Call trigger
1302  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1303  if ($result < 0) {
1304  $error++;
1305  }
1306  // End call triggers
1307  }
1308  }
1309 
1310  // Commit or rollback
1311  if ($error) {
1312  foreach ($this->errors as $errmsg) {
1313  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1314  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1315  }
1316  $this->db->rollback();
1317  return -1 * $error;
1318  } else {
1319  $this->db->commit();
1320  return 1;
1321  }
1322  }
1323 
1324  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1331  public function insert_discount($idremise)
1332  {
1333  // phpcs:enable
1334  global $conf, $langs;
1335 
1336  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1337  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1338 
1339  $this->db->begin();
1340 
1341  $remise = new DiscountAbsolute($this->db);
1342  $result = $remise->fetch($idremise);
1343 
1344  if ($result > 0) {
1345  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1346  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1347  $this->db->rollback();
1348  return -5;
1349  }
1350 
1351  $facligne = new SupplierInvoiceLine($this->db);
1352  $facligne->fk_facture_fourn = $this->id;
1353  $facligne->fk_remise_except = $remise->id;
1354  $facligne->desc = $remise->description; // Description ligne
1355  $facligne->vat_src_code = $remise->vat_src_code;
1356  $facligne->tva_tx = $remise->tva_tx;
1357  $facligne->subprice = -$remise->amount_ht;
1358  $facligne->fk_product = 0; // Id produit predefini
1359  $facligne->product_type = 0;
1360  $facligne->qty = 1;
1361  $facligne->remise_percent = 0;
1362  $facligne->rang = -1;
1363  $facligne->info_bits = 2;
1364 
1365  if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1366  $facligne->rang = 1;
1367  $linecount = count($this->lines);
1368  for ($ii = 1; $ii <= $linecount; $ii++) {
1369  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1370  }
1371  }
1372 
1373  // Get buy/cost price of invoice that is source of discount
1374  if ($remise->fk_invoice_supplier_source > 0) {
1375  $srcinvoice = new FactureFournisseur($this->db);
1376  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1377  $totalcostpriceofinvoice = 0;
1378  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1379  $formmargin = new FormMargin($this->db);
1380  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1381  $facligne->pa_ht = $arraytmp['pa_total'];
1382  }
1383 
1384  $facligne->total_ht = -$remise->amount_ht;
1385  $facligne->total_tva = -$remise->amount_tva;
1386  $facligne->total_ttc = -$remise->amount_ttc;
1387 
1388  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1389  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1390  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1391  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1392 
1393  $lineid = $facligne->insert();
1394  if ($lineid > 0) {
1395  $result = $this->update_price(1);
1396  if ($result > 0) {
1397  // Create link between discount and invoice line
1398  $result = $remise->link_to_invoice($lineid, 0);
1399  if ($result < 0) {
1400  $this->error = $remise->error;
1401  $this->db->rollback();
1402  return -4;
1403  }
1404 
1405  $this->db->commit();
1406  return 1;
1407  } else {
1408  $this->error = $facligne->error;
1409  $this->db->rollback();
1410  return -1;
1411  }
1412  } else {
1413  $this->error = $facligne->error;
1414  $this->db->rollback();
1415  return -2;
1416  }
1417  } else {
1418  $this->db->rollback();
1419  return -3;
1420  }
1421  }
1422 
1423 
1431  public function delete(User $user, $notrigger = 0)
1432  {
1433  global $conf;
1434 
1435  $rowid = $this->id;
1436 
1437  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1438 
1439  // TODO Test if there is at least on payment. If yes, refuse to delete.
1440 
1441  $error = 0;
1442  $this->db->begin();
1443 
1444  if (!$error && !$notrigger) {
1445  // Call trigger
1446  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1447  if ($result < 0) {
1448  $this->db->rollback();
1449  return -1;
1450  }
1451  // Fin appel triggers
1452  }
1453 
1454  if (!$error) {
1455  // If invoice was converted into a discount not yet consumed, we remove discount
1456  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1457  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1458  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1459  $resql = $this->db->query($sql);
1460 
1461  // If invoice has consumned discounts
1462  $this->fetch_lines();
1463  $list_rowid_det = array();
1464  foreach ($this->lines as $key => $invoiceline) {
1465  $list_rowid_det[] = $invoiceline->id;
1466  }
1467 
1468  // Consumned discounts are freed
1469  if (count($list_rowid_det)) {
1470  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1471  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1472  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1473 
1474  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1475  if (!$this->db->query($sql)) {
1476  $error++;
1477  }
1478  }
1479  }
1480 
1481  if (!$error) {
1482  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1483  $ef = $main."_extrafields";
1484  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1485  $resqlef = $this->db->query($sqlef);
1486  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1487  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1488  $resql = $this->db->query($sql);
1489  if ($resqlef && $resql) {
1490  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1491  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1492  $resql2 = $this->db->query($sql);
1493  if (!$resql2) {
1494  $error++;
1495  }
1496  } else {
1497  $error++;
1498  }
1499  }
1500 
1501  if (!$error) {
1502  // Delete linked object
1503  $res = $this->deleteObjectLinked();
1504  if ($res < 0) {
1505  $error++;
1506  }
1507  }
1508 
1509  if (!$error) {
1510  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1511  $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1512  $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1513 
1514  // We remove directory
1515  if ($conf->fournisseur->facture->dir_output) {
1516  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1517 
1518  $ref = dol_sanitizeFileName($this->ref);
1519  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1520  $file = $dir."/".$ref.".pdf";
1521  if (file_exists($file)) {
1522  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1523  $this->error = 'ErrorFailToDeleteFile';
1524  $error++;
1525  }
1526  }
1527  if (file_exists($dir)) {
1528  $res = @dol_delete_dir_recursive($dir);
1529 
1530  if (!$res) {
1531  $this->error = 'ErrorFailToDeleteDir';
1532  $error++;
1533  }
1534  }
1535  }
1536  }
1537 
1538  // Remove extrafields
1539  if (!$error) {
1540  $result = $this->deleteExtraFields();
1541  if ($result < 0) {
1542  $error++;
1543  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1544  }
1545  }
1546 
1547  if (!$error) {
1548  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1549  $this->db->commit();
1550  return 1;
1551  } else {
1552  $this->error = $this->db->lasterror();
1553  $this->db->rollback();
1554  return -$error;
1555  }
1556  }
1557 
1558 
1559  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1570  public function set_paid($user, $close_code = '', $close_note = '')
1571  {
1572  // phpcs:enable
1573  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1574  return $this->setPaid($user, $close_code, $close_note);
1575  }
1576 
1585  public function setPaid($user, $close_code = '', $close_note = '')
1586  {
1587  $error = 0;
1588 
1589  if ($this->paid != 1) {
1590  $this->db->begin();
1591 
1592  $now = dol_now();
1593 
1594  dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1595 
1596  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1597  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1598  if (!$close_code) {
1599  $sql .= ', paye=1';
1600  }
1601  if ($close_code) {
1602  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1603  }
1604  if ($close_note) {
1605  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1606  }
1607  $sql .= ', fk_user_closing = '.((int) $user->id);
1608  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1609  $sql .= ' WHERE rowid = '.((int) $this->id);
1610 
1611  $resql = $this->db->query($sql);
1612  if ($resql) {
1613  // Call trigger
1614  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1615  if ($result < 0) {
1616  $error++;
1617  }
1618  // End call triggers
1619  } else {
1620  $error++;
1621  $this->error = $this->db->error();
1622  dol_print_error($this->db);
1623  }
1624 
1625  if (!$error) {
1626  $this->db->commit();
1627  return 1;
1628  } else {
1629  $this->db->rollback();
1630  return -1;
1631  }
1632  } else {
1633  return 0;
1634  }
1635  }
1636 
1637  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1648  public function set_unpaid($user)
1649  {
1650  // phpcs:enable
1651  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1652  return $this->setUnpaid($user);
1653  }
1654 
1663  public function setUnpaid($user)
1664  {
1665  $error = 0;
1666 
1667  $this->db->begin();
1668 
1669  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1670  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1671  $sql .= ' date_closing=null,';
1672  $sql .= ' fk_user_closing=null';
1673  $sql .= ' WHERE rowid = '.((int) $this->id);
1674 
1675  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1676  $resql = $this->db->query($sql);
1677  if ($resql) {
1678  // Call trigger
1679  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1680  if ($result < 0) {
1681  $error++;
1682  }
1683  // End call triggers
1684  } else {
1685  $error++;
1686  $this->error = $this->db->error();
1687  dol_print_error($this->db);
1688  }
1689 
1690  if (!$error) {
1691  $this->db->commit();
1692  return 1;
1693  } else {
1694  $this->db->rollback();
1695  return -1;
1696  }
1697  }
1698 
1709  public function setCanceled($user, $close_code = '', $close_note = '')
1710  {
1711  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1712 
1713  $this->db->begin();
1714 
1715  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1716  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1717  if ($close_code) {
1718  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1719  }
1720  if ($close_note) {
1721  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1722  }
1723  $sql .= " WHERE rowid = ".((int) $this->id);
1724 
1725  $resql = $this->db->query($sql);
1726  if ($resql) {
1727  // Bound discounts are deducted from the invoice
1728  // as they have not been used since the invoice is abandoned.
1729  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1730  $sql .= ' SET fk_invoice_supplier = NULL';
1731  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1732 
1733  $resql = $this->db->query($sql);
1734  if ($resql) {
1735  // Call trigger
1736  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1737  if ($result < 0) {
1738  $this->db->rollback();
1739  return -1;
1740  }
1741  // End call triggers
1742 
1743  $this->db->commit();
1744  return 1;
1745  } else {
1746  $this->error = $this->db->error()." sql=".$sql;
1747  $this->db->rollback();
1748  return -1;
1749  }
1750  } else {
1751  $this->error = $this->db->error()." sql=".$sql;
1752  $this->db->rollback();
1753  return -2;
1754  }
1755  }
1756 
1766  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1767  {
1768  global $mysoc, $conf, $langs;
1769 
1770  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1771 
1772  $now = dol_now();
1773 
1774  $error = 0;
1775  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1776 
1777  // Force to have object complete for checks
1778  $this->fetch_thirdparty();
1779  $this->fetch_lines();
1780 
1781  // Check parameters
1782  if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1783  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1784  return 0;
1785  }
1786  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1787  $langs->load("errors");
1788  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1789  return -1;
1790  }
1791  if (count($this->lines) <= 0) {
1792  $langs->load("errors");
1793  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1794  return -1;
1795  }
1796 
1797  // Check for mandatory fields in thirdparty (defined into setup)
1798  if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1799  $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1800  foreach ($array_to_check as $key) {
1801  $keymin = strtolower($key);
1802  if ($keymin == 'accountancy_code_supplier') {
1803  $keymin = 'code_compta_fournisseur';
1804  }
1805  if (!property_exists($this->thirdparty, $keymin)) {
1806  continue;
1807  }
1808  $vallabel = $this->thirdparty->$keymin;
1809 
1810  $i = (int) preg_replace('/[^0-9]/', '', $key);
1811  if ($i > 0) {
1812  if ($this->thirdparty->isACompany()) {
1813  // Check for mandatory prof id (but only if country is other than ours)
1814  if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1815  $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1816  if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1817  $langs->load("errors");
1818  $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1819  dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1820  return -1;
1821  }
1822  }
1823  }
1824  } else {
1825  if ($key == 'EMAIL') {
1826  // Check for mandatory
1827  if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1828  $langs->load("errors");
1829  $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1830  dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1831  return -1;
1832  }
1833  } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1834  // Check for mandatory
1835  if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1836  $langs->load("errors");
1837  $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1838  dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1839  return -1;
1840  }
1841  }
1842  }
1843  }
1844  }
1845 
1846  $this->db->begin();
1847 
1848  // Define new ref
1849  if ($force_number) {
1850  $num = $force_number;
1851  } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1852  $num = $this->getNextNumRef($this->thirdparty);
1853  } else {
1854  $num = $this->ref;
1855  }
1856  $this->newref = dol_sanitizeFileName($num);
1857 
1858  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1859  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1860  $sql .= " WHERE rowid = ".((int) $this->id);
1861 
1862  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1863  $resql = $this->db->query($sql);
1864  if ($resql) {
1865  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1866  if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1867  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1868  $langs->load("agenda");
1869 
1870  $cpt = count($this->lines);
1871  for ($i = 0; $i < $cpt; $i++) {
1872  if ($this->lines[$i]->fk_product > 0) {
1873  $mouvP = new MouvementStock($this->db);
1874  $mouvP->origin = &$this;
1875  $mouvP->setOrigin($this->element, $this->id);
1876  // We increase stock for product
1877  $up_ht_disc = $this->lines[$i]->subprice;
1878  if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1879  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1880  }
1882  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1883  } else {
1884  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1885  }
1886  if ($result < 0) {
1887  $this->error = $mouvP->error;
1888  if (count($mouvP->errors)) {
1889  $this->errors = $mouvP->errors;
1890  }
1891  return -2;
1892  }
1893  }
1894  }
1895  }
1896 
1897  // Triggers call
1898  if (!$error && empty($notrigger)) {
1899  // Call trigger
1900  $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1901  if ($result < 0) {
1902  $error++;
1903  }
1904  // End call triggers
1905  }
1906 
1907  if (!$error) {
1908  $this->oldref = $this->ref;
1909 
1910  // Rename directory if dir was a temporary ref
1911  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1912  // Now we rename also files into index
1913  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1914  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1915  $resql = $this->db->query($sql);
1916  if (!$resql) {
1917  $error++;
1918  $this->error = $this->db->lasterror();
1919  }
1920  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1921  $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1922  $resql = $this->db->query($sql);
1923  if (!$resql) {
1924  $error++;
1925  $this->error = $this->db->lasterror();
1926  }
1927 
1928  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1929  $oldref = dol_sanitizeFileName($this->ref);
1930  $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1931  $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1932  if (!$error && file_exists($dirsource)) {
1933  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1934 
1935  if (@rename($dirsource, $dirdest)) {
1936  dol_syslog("Rename ok");
1937  // Rename docs starting with $oldref with $this->newref
1938  $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, '/'));
1939  foreach ($listoffiles as $fileentry) {
1940  $dirsource = $fileentry['name'];
1941  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1942  $dirsource = $fileentry['path'].'/'.$dirsource;
1943  $dirdest = $fileentry['path'].'/'.$dirdest;
1944  @rename($dirsource, $dirdest);
1945  }
1946  }
1947  }
1948  }
1949  }
1950 
1951  // Set new ref and define current statut
1952  if (!$error) {
1953  $this->ref = $this->newref;
1954  $this->statut = self::STATUS_VALIDATED;
1955  $this->status = self::STATUS_VALIDATED;
1956  //$this->date_validation=$now; this is stored into log table
1957  }
1958 
1959  if (!$error) {
1960  $this->db->commit();
1961  return 1;
1962  } else {
1963  $this->db->rollback();
1964  return -1;
1965  }
1966  } else {
1967  $this->error = $this->db->error();
1968  $this->db->rollback();
1969  return -1;
1970  }
1971  }
1972 
1981  public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
1982  {
1983  // phpcs:enable
1984  global $conf, $langs;
1985 
1986  $error = 0;
1987 
1988  if ($this->status == self::STATUS_DRAFT) {
1989  dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1990  return 0;
1991  }
1992 
1993  dol_syslog(__METHOD__, LOG_DEBUG);
1994 
1995  $this->db->begin();
1996 
1997  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1998  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1999  $sql .= " WHERE rowid = ".((int) $this->id);
2000 
2001  $result = $this->db->query($sql);
2002  if ($result) {
2003  if (!$error) {
2004  $this->oldcopy = clone $this;
2005  }
2006 
2007  // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
2008  if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2009  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2010  $langs->load("agenda");
2011 
2012  $cpt = count($this->lines);
2013  for ($i = 0; $i < $cpt; $i++) {
2014  if ($this->lines[$i]->fk_product > 0) {
2015  $mouvP = new MouvementStock($this->db);
2016  $mouvP->origin = &$this;
2017  $mouvP->setOrigin($this->element, $this->id);
2018  // We increase stock for product
2020  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2021  } else {
2022  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2023  }
2024  }
2025  }
2026  }
2027  // Triggers call
2028  if (!$error && empty($notrigger)) {
2029  // Call trigger
2030  $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2031  if ($result < 0) {
2032  $error++;
2033  }
2034  // End call triggers
2035  }
2036  if ($error == 0) {
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 
2050 
2084  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)
2085  {
2086  global $langs, $mysoc;
2087 
2088  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);
2089  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2090 
2091  if ($this->status == self::STATUS_DRAFT) {
2092  // Clean parameters
2093  if (empty($remise_percent)) {
2094  $remise_percent = 0;
2095  }
2096  if (empty($qty)) {
2097  $qty = 0;
2098  }
2099  if (empty($info_bits)) {
2100  $info_bits = 0;
2101  }
2102  if (empty($rang)) {
2103  $rang = 0;
2104  }
2105  if (empty($fk_code_ventilation)) {
2106  $fk_code_ventilation = 0;
2107  }
2108  if (empty($txtva)) {
2109  $txtva = 0;
2110  }
2111  if (empty($txlocaltax1)) {
2112  $txlocaltax1 = 0;
2113  }
2114  if (empty($txlocaltax2)) {
2115  $txlocaltax2 = 0;
2116  }
2117 
2118  $remise_percent = price2num($remise_percent);
2119  $qty = price2num($qty);
2120  $pu = price2num($pu);
2121  if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2122  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2123  }
2124  $txlocaltax1 = price2num($txlocaltax1);
2125  $txlocaltax2 = price2num($txlocaltax2);
2126 
2127  if ($date_start && $date_end && $date_start > $date_end) {
2128  $langs->load("errors");
2129  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2130  return -1;
2131  }
2132 
2133  $this->db->begin();
2134 
2135  if ($fk_product > 0) {
2136  if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) {
2137  // Check quantity is enough
2138  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2139  $prod = new ProductFournisseur($this->db);
2140  if ($prod->fetch($fk_product) > 0) {
2141  $product_type = $prod->type;
2142  $label = $prod->label;
2143  $fk_prod_fourn_price = 0;
2144 
2145  // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2146  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2147  $result = $prod->get_buyprice($fk_prod_fourn_price, $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
2148  if ($result > 0) {
2149  if (empty($pu)) {
2150  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2151  }
2152  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2153  // is remise percent not keyed but present for the product we add it
2154  if ($remise_percent == 0 && $prod->remise_percent != 0) {
2155  $remise_percent = $prod->remise_percent;
2156  }
2157  }
2158  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2159  $langs->load("errors");
2160  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2161  $this->db->rollback();
2162  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2163  //$pu = $prod->fourn_pu; // We do not overwrite unit price
2164  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2165  return -1;
2166  }
2167  if ($result == -1) {
2168  $langs->load("errors");
2169  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2170  $this->db->rollback();
2171  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2172  return -1;
2173  }
2174  if ($result < -1) {
2175  $this->error = $prod->error;
2176  $this->db->rollback();
2177  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2178  return -1;
2179  }
2180  } else {
2181  $this->error = $prod->error;
2182  $this->db->rollback();
2183  return -1;
2184  }
2185  }
2186  } else {
2187  $product_type = $type;
2188  }
2189 
2190  if (isModEnabled("multicurrency") && $pu_devise > 0) {
2191  $pu = 0;
2192  }
2193 
2194  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2195 
2196  // Clean vat code
2197  $reg = array();
2198  $vat_src_code = '';
2199  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2200  $vat_src_code = $reg[1];
2201  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2202  }
2203 
2204  // Calcul du total TTC et de la TVA pour la ligne a partir de
2205  // qty, pu, remise_percent et txtva
2206  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2207  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2208 
2209  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2210  $total_ht = $tabprice[0];
2211  $total_tva = $tabprice[1];
2212  $total_ttc = $tabprice[2];
2213  $total_localtax1 = $tabprice[9];
2214  $total_localtax2 = $tabprice[10];
2215  $pu_ht = $tabprice[3];
2216 
2217  // MultiCurrency
2218  $multicurrency_total_ht = $tabprice[16];
2219  $multicurrency_total_tva = $tabprice[17];
2220  $multicurrency_total_ttc = $tabprice[18];
2221  $pu_ht_devise = $tabprice[19];
2222 
2223  // Check parameters
2224  if ($type < 0) {
2225  return -1;
2226  }
2227 
2228  if ($rang < 0) {
2229  $rangmax = $this->line_max();
2230  $rang = $rangmax + 1;
2231  }
2232 
2233  // Insert line
2234  $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2235 
2236  $supplierinvoiceline->context = $this->context;
2237 
2238  $supplierinvoiceline->fk_facture_fourn = $this->id;
2239  //$supplierinvoiceline->label=$label; // deprecated
2240  $supplierinvoiceline->desc = $desc;
2241  $supplierinvoiceline->ref_supplier = $ref_supplier;
2242 
2243  $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2244  $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2245 
2246  $supplierinvoiceline->vat_src_code = $vat_src_code;
2247  $supplierinvoiceline->tva_tx = $txtva;
2248  $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2249  $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2250  $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2251  $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2252 
2253  $supplierinvoiceline->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht); // For credit note and if qty is negative, total is negative
2254  $supplierinvoiceline->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva); // For credit note and if qty is negative, total is negative
2255  $supplierinvoiceline->total_localtax1 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax1) : $total_localtax1); // For credit note and if qty is negative, total is negative
2256  $supplierinvoiceline->total_localtax2 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax2) : $total_localtax2); // For credit note and if qty is negative, total is negative
2257  $supplierinvoiceline->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc); // For credit note and if qty is negative, total is negative
2258 
2259  $supplierinvoiceline->fk_product = $fk_product;
2260  $supplierinvoiceline->product_type = $type;
2261  $supplierinvoiceline->remise_percent = $remise_percent;
2262  $supplierinvoiceline->date_start = $date_start;
2263  $supplierinvoiceline->date_end = $date_end;
2264  $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2265  $supplierinvoiceline->rang = $rang;
2266  $supplierinvoiceline->info_bits = $info_bits;
2267  $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2268 
2269 
2270  $supplierinvoiceline->special_code = (int) $special_code;
2271  $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2272  $supplierinvoiceline->origin = $this->origin;
2273  $supplierinvoiceline->origin_id = $origin_id;
2274  $supplierinvoiceline->fk_unit = $fk_unit;
2275 
2276  // Multicurrency
2277  $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2278  $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2279  $supplierinvoiceline->multicurrency_subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht_devise) : $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2280 
2281  $supplierinvoiceline->multicurrency_total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ht) : $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2282  $supplierinvoiceline->multicurrency_total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_tva) : $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2283  $supplierinvoiceline->multicurrency_total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ttc) : $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2284 
2285  if (is_array($array_options) && count($array_options) > 0) {
2286  $supplierinvoiceline->array_options = $array_options;
2287  }
2288 
2289  $result = $supplierinvoiceline->insert($notrigger);
2290  if ($result > 0) {
2291  // Reorder if child line
2292  if (!empty($fk_parent_line)) {
2293  $this->line_order(true, 'DESC');
2294  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2295  $linecount = count($this->lines);
2296  for ($ii = $rang; $ii <= $linecount; $ii++) {
2297  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2298  }
2299  }
2300 
2301  // Mise a jour information denormalisees au niveau de la facture meme
2302  $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.
2303  if ($result > 0) {
2304  $this->db->commit();
2305  return $supplierinvoiceline->id;
2306  } else {
2307  $this->error = $this->db->error();
2308  $this->db->rollback();
2309  return -1;
2310  }
2311  } else {
2312  $this->error = $supplierinvoiceline->error;
2313  $this->errors = $supplierinvoiceline->errors;
2314  $this->db->rollback();
2315  return -2;
2316  }
2317  } else {
2318  return 0;
2319  }
2320  }
2321 
2347  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)
2348  {
2349  global $mysoc, $langs;
2350 
2351  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);
2352  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2353 
2354  $pu = price2num($pu);
2355  $qty = price2num($qty);
2356  $remise_percent = (float) price2num($remise_percent);
2357  $pu_devise = price2num($pu_devise);
2358 
2359  // Check parameters
2360  //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2361  if ($type < 0) {
2362  return -1;
2363  }
2364 
2365  if ($date_start && $date_end && $date_start > $date_end) {
2366  $langs->load("errors");
2367  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2368  return -1;
2369  }
2370 
2371  // Clean parameters
2372  if (empty($vatrate)) {
2373  $vatrate = 0;
2374  }
2375  if (empty($txlocaltax1)) {
2376  $txlocaltax1 = 0;
2377  }
2378  if (empty($txlocaltax2)) {
2379  $txlocaltax2 = 0;
2380  }
2381 
2382  $txlocaltax1 = (float) price2num($txlocaltax1);
2383  $txlocaltax2 = (float) price2num($txlocaltax2);
2384 
2385  // Calcul du total TTC et de la TVA pour la ligne a partir de
2386  // qty, pu, remise_percent et txtva
2387  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2388  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2389 
2390  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2391 
2392  $reg = array();
2393 
2394  // Clean vat code
2395  $vat_src_code = '';
2396  if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2397  $vat_src_code = $reg[1];
2398  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2399  }
2400 
2401  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2402  $total_ht = $tabprice[0];
2403  $total_tva = $tabprice[1];
2404  $total_ttc = $tabprice[2];
2405  $pu_ht = $tabprice[3];
2406  $pu_tva = $tabprice[4];
2407  $pu_ttc = $tabprice[5];
2408  $total_localtax1 = $tabprice[9];
2409  $total_localtax2 = $tabprice[10];
2410 
2411  // MultiCurrency
2412  $multicurrency_total_ht = $tabprice[16];
2413  $multicurrency_total_tva = $tabprice[17];
2414  $multicurrency_total_ttc = $tabprice[18];
2415  $pu_ht_devise = $tabprice[19];
2416 
2417  if (empty($info_bits)) {
2418  $info_bits = 0;
2419  }
2420 
2421  //Fetch current line from the database and then clone the object and set it in $oldline property
2422  $line = new SupplierInvoiceLine($this->db);
2423  $line->fetch($id);
2424  $line->fetch_optionals();
2425 
2426  $staticline = clone $line;
2427 
2428  if ($idproduct) {
2429  $product = new Product($this->db);
2430  $result = $product->fetch($idproduct);
2431  $product_type = $product->type;
2432  } else {
2433  $idproduct = $staticline->fk_product;
2434  $product_type = $type;
2435  }
2436 
2437  $line->oldline = $staticline;
2438  $line->context = $this->context;
2439 
2440  $line->description = $desc;
2441  $line->desc = $desc;
2442 
2443  $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2444  $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2445  $line->pu_ht = $line->subprice; // deprecated
2446  $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2447 
2448  $line->remise_percent = $remise_percent;
2449  $line->ref_supplier = $ref_supplier;
2450 
2451  $line->date_start = $date_start;
2452  $line->date_end = $date_end;
2453 
2454  $line->vat_src_code = $vat_src_code;
2455  $line->tva_tx = $vatrate;
2456  $line->localtax1_tx = $txlocaltax1;
2457  $line->localtax2_tx = $txlocaltax2;
2458  $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2459  $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2460 
2461  $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht);
2462  $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva);
2463  $line->total_localtax1 = $total_localtax1;
2464  $line->total_localtax2 = $total_localtax2;
2465  $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc);
2466 
2467  $line->fk_product = $idproduct;
2468  $line->product_type = $product_type;
2469  $line->info_bits = $info_bits;
2470  $line->fk_unit = $fk_unit;
2471  $line->rang = $rang;
2472 
2473  if (is_array($array_options) && count($array_options) > 0) {
2474  // We replace values in this->line->array_options only for entries defined into $array_options
2475  foreach ($array_options as $key => $value) {
2476  $line->array_options[$key] = $array_options[$key];
2477  }
2478  }
2479 
2480  // Multicurrency
2481  $line->multicurrency_subprice = $pu_ht_devise;
2482  $line->multicurrency_total_ht = $multicurrency_total_ht;
2483  $line->multicurrency_total_tva = $multicurrency_total_tva;
2484  $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2485 
2486  $res = $line->update($notrigger);
2487 
2488  if ($res < 1) {
2489  $this->errors[] = $line->error;
2490  } else {
2491  // Update total price into invoice record
2492  $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2493  }
2494 
2495  return $res;
2496  }
2497 
2505  public function deleteLine($rowid, $notrigger = 0)
2506  {
2507  if (!$rowid) {
2508  $rowid = $this->id;
2509  }
2510 
2511  $this->db->begin();
2512 
2513  // Free the discount linked to a line of invoice
2514  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2515  $sql .= ' SET fk_invoice_supplier_line = NULL';
2516  $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2517 
2518  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2519  $result = $this->db->query($sql);
2520  if (!$result) {
2521  $this->error = $this->db->error();
2522  $this->db->rollback();
2523  return -2;
2524  }
2525 
2526  $line = new SupplierInvoiceLine($this->db);
2527 
2528  if ($line->fetch($rowid) < 1) {
2529  return -1;
2530  }
2531 
2532  $res = $line->delete($notrigger);
2533 
2534  if ($res < 1) {
2535  $this->errors[] = $line->error;
2536  $this->db->rollback();
2537  return -3;
2538  } else {
2539  $res = $this->update_price(1);
2540 
2541  if ($res > 0) {
2542  $this->db->commit();
2543  return 1;
2544  } else {
2545  $this->db->rollback();
2546  $this->error = $this->db->lasterror();
2547  return -4;
2548  }
2549  }
2550  }
2551 
2552 
2559  public function info($id)
2560  {
2561  $sql = 'SELECT c.rowid, datec, tms as datem, ';
2562  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2563  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2564  $sql .= ' WHERE c.rowid = '.((int) $id);
2565 
2566  $result = $this->db->query($sql);
2567  if ($result) {
2568  if ($this->db->num_rows($result)) {
2569  $obj = $this->db->fetch_object($result);
2570 
2571  $this->id = $obj->rowid;
2572 
2573  $this->user_creation_id = $obj->fk_user_author;
2574  $this->user_validation_id = $obj->fk_user_valid;
2575  $this->user_modification_id = $obj->fk_user_modif;
2576  $this->date_creation = $this->db->jdate($obj->datec);
2577  $this->date_modification = $this->db->jdate($obj->datem);
2578  //$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)
2579  }
2580  $this->db->free($result);
2581  } else {
2582  dol_print_error($this->db);
2583  }
2584  }
2585 
2586  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2595  public function list_replacable_supplier_invoices($socid = 0)
2596  {
2597  // phpcs:enable
2598  global $conf;
2599 
2600  $return = array();
2601 
2602  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2603  $sql .= " ff.rowid as rowidnext";
2604  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2605  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2606  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2607  $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2608  $sql .= " AND f.entity = ".$conf->entity;
2609  $sql .= " AND f.paye = 0"; // Pas classee payee completement
2610  $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2611  $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement
2612  if ($socid > 0) {
2613  $sql .= " AND f.fk_soc = ".((int) $socid);
2614  }
2615  $sql .= " ORDER BY f.ref";
2616 
2617  dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2618  $resql = $this->db->query($sql);
2619  if ($resql) {
2620  while ($obj = $this->db->fetch_object($resql)) {
2621  $return[$obj->rowid] = array(
2622  'id' => $obj->rowid,
2623  'ref' => $obj->ref,
2624  'status' => $obj->fk_statut
2625  );
2626  }
2627  //print_r($return);
2628  return $return;
2629  } else {
2630  $this->error = $this->db->error();
2631  return -1;
2632  }
2633  }
2634 
2635  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2645  public function list_qualified_avoir_supplier_invoices($socid = 0)
2646  {
2647  // phpcs:enable
2648  global $conf;
2649 
2650  $return = array();
2651 
2652  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn";
2653  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2654  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2655  $sql .= " WHERE f.entity = ".$conf->entity;
2656  $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2657  $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2658  $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2659  $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2660  if ($socid > 0) {
2661  $sql .= " AND f.fk_soc = ".((int) $socid);
2662  }
2663  $sql .= " ORDER BY f.ref";
2664 
2665  dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2666  $resql = $this->db->query($sql);
2667  if ($resql) {
2668  while ($obj = $this->db->fetch_object($resql)) {
2669  $qualified = 0;
2670  if ($obj->fk_statut == self::STATUS_VALIDATED) {
2671  $qualified = 1;
2672  }
2673  if ($obj->fk_statut == self::STATUS_CLOSED) {
2674  $qualified = 1;
2675  }
2676  if ($qualified) {
2677  $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2678  $return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot);
2679  }
2680  }
2681 
2682  return $return;
2683  } else {
2684  $this->error = $this->db->error();
2685  return -1;
2686  }
2687  }
2688 
2689  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2696  public function load_board($user)
2697  {
2698  // phpcs:enable
2699  global $conf, $langs;
2700 
2701  $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2702  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2703  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
2704  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2705  }
2706  $sql .= ' WHERE ff.paye = 0';
2707  $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2708  $sql .= " AND ff.entity = ".$conf->entity;
2709  if ($user->socid) {
2710  $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2711  }
2712 
2713  $resql = $this->db->query($sql);
2714  if ($resql) {
2715  $langs->load("bills");
2716  $now = dol_now();
2717 
2718  $response = new WorkboardResponse();
2719  $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2720  $response->label = $langs->trans("SupplierBillsToPay");
2721  $response->labelShort = $langs->trans("StatusToPay");
2722 
2723  $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2724  $response->img = img_object($langs->trans("Bills"), "bill");
2725 
2726  $facturestatic = new FactureFournisseur($this->db);
2727 
2728  while ($obj = $this->db->fetch_object($resql)) {
2729  $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2730  $facturestatic->statut = $obj->status; // For backward compatibility
2731  $facturestatic->status = $obj->status;
2732 
2733  $response->nbtodo++;
2734  $response->total += $obj->total_ht;
2735 
2736  if ($facturestatic->hasDelay()) {
2737  $response->nbtodolate++;
2738  $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2739  }
2740  }
2741 
2742  $this->db->free($resql);
2743  return $response;
2744  } else {
2745  dol_print_error($this->db);
2746  $this->error = $this->db->error();
2747  return -1;
2748  }
2749  }
2750 
2758  public function getTooltipContentArray($params)
2759  {
2760  global $conf, $langs, $mysoc;
2761 
2762  $langs->load('bills');
2763 
2764  $datas = [];
2765  $moretitle = $params['moretitle'] ?? '';
2766  $picto = $this->picto;
2767  if ($this->type == self::TYPE_REPLACEMENT) {
2768  $picto .= 'r'; // Replacement invoice
2769  }
2770  if ($this->type == self::TYPE_CREDIT_NOTE) {
2771  $picto .= 'a'; // Credit note
2772  }
2773  if ($this->type == self::TYPE_DEPOSIT) {
2774  $picto .= 'd'; // Deposit invoice
2775  }
2776 
2777  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2778  if ($this->type == self::TYPE_REPLACEMENT) {
2779  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2780  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2781  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2782  } elseif ($this->type == self::TYPE_DEPOSIT) {
2783  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2784  }
2785  if (isset($this->status)) {
2786  $alreadypaid = -1;
2787  if (isset($this->alreadypaid)) {
2788  $alreadypaid = $this->alreadypaid;
2789  }
2790 
2791  $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2792  }
2793  if ($moretitle) {
2794  $datas['picto'] .= ' - '.$moretitle;
2795  }
2796  if (!empty($this->ref)) {
2797  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2798  }
2799  if (!empty($this->ref_supplier)) {
2800  $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2801  }
2802  if (!empty($this->label)) {
2803  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2804  }
2805  if (!empty($this->date)) {
2806  $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2807  }
2808  if (!empty($this->date_echeance)) {
2809  $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2810  }
2811  if (!empty($this->total_ht)) {
2812  $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2813  }
2814  if (!empty($this->total_tva)) {
2815  $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2816  }
2817  if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2818  // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2819  $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2820  }
2821  if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2822  $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2823  }
2824  if (!empty($this->revenuestamp)) {
2825  $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2826  }
2827  if (!empty($this->total_ttc)) {
2828  $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2829  }
2830  return $datas;
2831  }
2832 
2846  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2847  {
2848  global $langs, $conf, $user, $hookmanager;
2849 
2850  $result = '';
2851 
2852  if ($option == 'withdraw') {
2853  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2854  } elseif ($option == 'document') {
2855  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2856  } else {
2857  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2858  }
2859 
2860  if ($short) {
2861  return $url;
2862  }
2863 
2864  if ($option !== 'nolink') {
2865  // Add param to save lastsearch_values or not
2866  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2867  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2868  $add_save_lastsearch_values = 1;
2869  }
2870  if ($add_save_lastsearch_values) {
2871  $url .= '&save_lastsearch_values=1';
2872  }
2873  }
2874 
2875  $picto = $this->picto;
2876  if ($this->type == self::TYPE_REPLACEMENT) {
2877  $picto .= 'r'; // Replacement invoice
2878  }
2879  if ($this->type == self::TYPE_CREDIT_NOTE) {
2880  $picto .= 'a'; // Credit note
2881  }
2882  if ($this->type == self::TYPE_DEPOSIT) {
2883  $picto .= 'd'; // Deposit invoice
2884  }
2885  $params = [
2886  'id' => $this->id,
2887  'objecttype' => $this->element,
2888  'option' => $option,
2889  'moretitle' => $moretitle,
2890  ];
2891  $classfortooltip = 'classfortooltip';
2892  $dataparams = '';
2893  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2894  $classfortooltip = 'classforajaxtooltip';
2895  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2896  $label = '';
2897  } else {
2898  $label = implode($this->getTooltipContentArray($params));
2899  }
2900 
2901  $ref = $this->ref;
2902  if (empty($ref)) {
2903  $ref = $this->id;
2904  }
2905 
2906  $linkclose = '';
2907  if (empty($notooltip)) {
2908  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2909  $label = $langs->trans("ShowSupplierInvoice");
2910  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2911  }
2912  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2913  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2914  }
2915 
2916  $linkstart = '<a href="'.$url.'"';
2917  $linkstart .= $linkclose.'>';
2918  $linkend = '</a>';
2919 
2920  $result .= $linkstart;
2921  if ($withpicto) {
2922  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2923  }
2924  if ($withpicto != 2) {
2925  $result .= ($max ? dol_trunc($ref, $max) : $ref);
2926  }
2927  $result .= $linkend;
2928 
2929  if ($addlinktonotes) {
2930  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2931  if ($txttoshow) {
2932  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2933  $result .= ' <span class="note inline-block">';
2934  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2935  $result .= img_picto('', 'note');
2936  $result .= '</a>';
2937  $result .= '</span>';
2938  }
2939  }
2940  global $action;
2941  $hookmanager->initHooks(array($this->element . 'dao'));
2942  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2943  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2944  if ($reshook > 0) {
2945  $result = $hookmanager->resPrint;
2946  } else {
2947  $result .= $hookmanager->resPrint;
2948  }
2949  return $result;
2950  }
2951 
2960  public function getNextNumRef($soc, $mode = 'next')
2961  {
2962  global $db, $langs, $conf;
2963  $langs->load("orders");
2964 
2965  // Clean parameters (if not defined or using deprecated value)
2966  if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
2967  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2968  }
2969 
2970  $mybool = false;
2971 
2972  $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
2973  $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
2974 
2975  // Include file with class
2976  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2977 
2978  foreach ($dirmodels as $reldir) {
2979  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2980 
2981  // Load file with numbering class (if found)
2982  $mybool = ((bool) @include_once $dir.$file) || $mybool;
2983  }
2984 
2985  if (!$mybool) {
2986  dol_print_error(null, "Failed to include file ".$file);
2987  return '';
2988  }
2989 
2990  $obj = new $classname();
2991  '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
2992  $numref = "";
2993  $numref = $obj->getNextValue($soc, $this, $mode);
2994 
2995  if ($numref != "") {
2996  return $numref;
2997  } else {
2998  $this->error = $obj->error;
2999  return -1;
3000  }
3001  }
3002 
3003 
3012  public function initAsSpecimen($option = '')
3013  {
3014  global $langs, $conf;
3015  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3016 
3017  $now = dol_now();
3018 
3019  // Load array of products prodids
3020  $num_prods = 0;
3021  $prodids = array();
3022 
3023  $sql = "SELECT rowid";
3024  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3025  $sql .= " WHERE entity IN (".getEntity('product').")";
3026  $sql .= $this->db->plimit(100);
3027 
3028  $resql = $this->db->query($sql);
3029  if ($resql) {
3030  $num_prods = $this->db->num_rows($resql);
3031  $i = 0;
3032  while ($i < $num_prods) {
3033  $i++;
3034  $row = $this->db->fetch_row($resql);
3035  $prodids[$i] = $row[0];
3036  }
3037  }
3038 
3039  // Initialise parameters
3040  $this->id = 0;
3041  $this->ref = 'SPECIMEN';
3042  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3043  $this->specimen = 1;
3044  $this->socid = 1;
3045  $this->date = $now;
3046  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3047  $this->cond_reglement_code = 'RECEP';
3048  $this->mode_reglement_code = 'CHQ';
3049 
3050  $this->note_public = 'This is a comment (public)';
3051  $this->note_private = 'This is a comment (private)';
3052 
3053  $this->multicurrency_tx = 1;
3054  $this->multicurrency_code = $conf->currency;
3055 
3056  $xnbp = 0;
3057  if (empty($option) || $option != 'nolines') {
3058  // Lines
3059  $nbp = 5;
3060  while ($xnbp < $nbp) {
3061  $line = new SupplierInvoiceLine($this->db);
3062  $line->desc = $langs->trans("Description")." ".$xnbp;
3063  $line->qty = 1;
3064  $line->subprice = 100;
3065  $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice
3066  $line->price = 100;
3067  $line->tva_tx = 19.6;
3068  $line->localtax1_tx = 0;
3069  $line->localtax2_tx = 0;
3070  if ($xnbp == 2) {
3071  $line->total_ht = 50;
3072  $line->total_ttc = 59.8;
3073  $line->total_tva = 9.8;
3074  $line->remise_percent = 50;
3075  } else {
3076  $line->total_ht = 100;
3077  $line->total_ttc = 119.6;
3078  $line->total_tva = 19.6;
3079  $line->remise_percent = 0;
3080  }
3081 
3082  if ($num_prods > 0) {
3083  $prodid = mt_rand(1, $num_prods);
3084  $line->fk_product = $prodids[$prodid];
3085  }
3086  $line->product_type = 0;
3087 
3088  $this->lines[$xnbp] = $line;
3089 
3090  $this->total_ht += $line->total_ht;
3091  $this->total_tva += $line->total_tva;
3092  $this->total_ttc += $line->total_ttc;
3093 
3094  $xnbp++;
3095  }
3096  }
3097 
3098  $this->total_ht = $xnbp * 100;
3099  $this->total_tva = $xnbp * 19.6;
3100  $this->total_ttc = $xnbp * 119.6;
3101 
3102  return 1;
3103  }
3104 
3110  public function loadStateBoard()
3111  {
3112  global $conf, $user;
3113 
3114  $this->nb = array();
3115 
3116  $clause = "WHERE";
3117 
3118  $sql = "SELECT count(f.rowid) as nb";
3119  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3120  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3121  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3122  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3123  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3124  $clause = "AND";
3125  }
3126  $sql .= " ".$clause." f.entity = ".$conf->entity;
3127 
3128  $resql = $this->db->query($sql);
3129  if ($resql) {
3130  while ($obj = $this->db->fetch_object($resql)) {
3131  $this->nb["supplier_invoices"] = $obj->nb;
3132  }
3133  $this->db->free($resql);
3134  return 1;
3135  } else {
3136  dol_print_error($this->db);
3137  $this->error = $this->db->error();
3138  return -1;
3139  }
3140  }
3141 
3150  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3151  {
3152  global $conf, $langs;
3153 
3154  $error = 0;
3155 
3156  $object = new FactureFournisseur($this->db);
3157 
3158  $this->db->begin();
3159 
3160  // Load source object
3161  $object->fetch($fromid);
3162  $object->id = 0;
3163  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3164  $object->status = self::STATUS_DRAFT;
3165 
3166  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3167 
3168  // Clear fields
3169  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3170  $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3171  $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3172  $object->fk_facture_source = 0;
3173  $object->date_creation = '';
3174  $object->date_validation = '';
3175  $object->date = (empty($this->date) ? dol_now() : $this->date);
3176  $object->ref_client = '';
3177  $object->close_code = '';
3178  $object->close_note = '';
3179  if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3180  $object->note_private = '';
3181  $object->note_public = '';
3182  }
3183 
3184  $object->date_echeance = $object->calculate_date_lim_reglement();
3185 
3186  // Loop on each line of new invoice
3187  foreach ($object->lines as $i => $line) {
3188  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3189  unset($object->lines[$i]);
3190  }
3191  }
3192 
3193  // Create clone
3194  $object->context['createfromclone'] = 'createfromclone';
3195  $result = $object->create($user);
3196 
3197  // Other options
3198  if ($result < 0) {
3199  $this->error = $object->error;
3200  $this->errors = $object->errors;
3201  $error++;
3202  }
3203 
3204  if (!$error) {
3205  }
3206 
3207  unset($object->context['createfromclone']);
3208 
3209  // End
3210  if (!$error) {
3211  $this->db->commit();
3212  return $object->id;
3213  } else {
3214  $this->db->rollback();
3215  return -1;
3216  }
3217  }
3218 
3230  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3231  {
3232  global $langs;
3233 
3234  $langs->load("suppliers");
3235  $outputlangs->load("products");
3236 
3237  // Set the model on the model name to use
3238  if (empty($modele)) {
3239  if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3240  $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3241  } else {
3242  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3243  }
3244  }
3245 
3246  if (empty($modele)) {
3247  return 0;
3248  } else {
3249  $modelpath = "core/modules/supplier_invoice/doc/";
3250 
3251  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3252  }
3253  }
3254 
3259  public function getRights()
3260  {
3261  global $user;
3262 
3263  return $user->hasRight("fournisseur", "facture");
3264  }
3265 
3274  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3275  {
3276  $tables = array(
3277  'facture_fourn'
3278  );
3279 
3280  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3281  }
3282 
3291  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3292  {
3293  $tables = array(
3294  'facture_fourn_det'
3295  );
3296 
3297  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3298  }
3299 
3305  public function hasDelay()
3306  {
3307  global $conf;
3308 
3309  $now = dol_now();
3310 
3311  if (!$this->date_echeance) {
3312  return false;
3313  }
3314 
3315  $status = isset($this->status) ? $this->status : $this->statut;
3316 
3317  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3318  }
3319 
3325  public function isCreditNoteUsed()
3326  {
3327  $isUsed = false;
3328 
3329  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3330  $resql = $this->db->query($sql);
3331  if (!empty($resql)) {
3332  $obj = $this->db->fetch_object($resql);
3333  if (!empty($obj->fk_invoice_supplier)) {
3334  $isUsed = true;
3335  }
3336  }
3337 
3338  return $isUsed;
3339  }
3347  public function getKanbanView($option = '', $arraydata = null)
3348  {
3349  global $langs;
3350 
3351  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3352 
3353  $return = '<div class="box-flex-item box-flex-grow-zero">';
3354  $return .= '<div class="info-box info-box-sm">';
3355  $return .= '<span class="info-box-icon bg-infobox-action">';
3356  $return .= img_picto('', $this->picto);
3357  $return .= '</span>';
3358  $return .= '<div class="info-box-content">';
3359  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3360  if ($selected >= 0) {
3361  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3362  }
3363  if (!empty($arraydata['thirdparty'])) {
3364  $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3365  }
3366  if (property_exists($this, 'date')) {
3367  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3368  }
3369  if (property_exists($this, 'total_ht')) {
3370  $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3371  $return .= ' '.$langs->trans("HT");
3372  $return .= '</span>';
3373  }
3374  if (method_exists($this, 'getLibStatut')) {
3375  $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3376  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3377  }
3378  $return .= '</div>';
3379  $return .= '</div>';
3380  $return .= '</div>';
3381  return $return;
3382  }
3383 
3390  public function setVATReverseCharge($vatreversecharge)
3391  {
3392  if (!$this->table_element) {
3393  dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3394  return -1;
3395  }
3396 
3397  dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3398 
3399  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3400  $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3401  $sql .= " WHERE rowid=".((int) $this->id);
3402 
3403  if ($this->db->query($sql)) {
3404  $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3405  return 1;
3406  } else {
3407  dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3408  $this->error = $this->db->error();
3409  return 0;
3410  }
3411  }
3412 
3424  public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3425  {
3426  global $conf, $langs, $user;
3427 
3428  $this->output = '';
3429  $this->error = '';
3430  $nbMailSend = 0;
3431 
3432  $error = 0;
3433  $errorsMsg = array();
3434 
3435  $langs->load('bills');
3436 
3437  if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3438  $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3439  return 0;
3440  }
3441  if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3442  $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3443  return 0;
3444  }
3445 
3446  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3447  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3448  require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3449  $formmail = new FormMail($this->db);
3450 
3451  $now = dol_now();
3452  $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3453 
3454  $tmpinvoice = new FactureFournisseur($this->db);
3455 
3456  dol_syslog(__METHOD__." start", LOG_INFO);
3457 
3458  // Select all action comm reminder
3459  $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3460  if (!empty($paymentmode) && $paymentmode != 'all') {
3461  $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3462  }
3463  $sql .= " WHERE f.paye = 0"; // Only unpaid
3464  $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3465  if ($datetouse == 'invoicedate') {
3466  $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3467  } else {
3468  $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3469  }
3470  $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3471  if (!empty($paymentmode) && $paymentmode != 'all') {
3472  $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3473  }
3474  // TODO Add a filter to check there is no payment started yet
3475  if ($datetouse == 'invoicedate') {
3476  $sql .= $this->db->order("datef", "ASC");
3477  } else {
3478  $sql .= $this->db->order("date_lim_reglement", "ASC");
3479  }
3480 
3481  $resql = $this->db->query($sql);
3482 
3483  $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3484  if ($datetouse == 'invoicedate') {
3485  $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3486  } else {
3487  $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3488  }
3489  if (!empty($paymentmode) && $paymentmode != 'all') {
3490  $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3491  }
3492  $this->output .= '<br>';
3493 
3494  if ($resql) {
3495  while ($obj = $this->db->fetch_object($resql)) {
3496  if (!$error) {
3497  // Load event
3498  $res = $tmpinvoice->fetch($obj->id);
3499  if ($res > 0) {
3500  $tmpinvoice->fetch_thirdparty();
3501 
3502  $outputlangs = new Translate('', $conf);
3503  if ($tmpinvoice->thirdparty->default_lang) {
3504  $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3505  $outputlangs->loadLangs(array("main", "suppliers"));
3506  } else {
3507  $outputlangs = $langs;
3508  }
3509 
3510  // Select email template according to language of recipient
3511  $templateId = 0;
3512  $templateLabel = '';
3513  if (empty($template) || $template == 'EmailTemplateCode') {
3514  $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3515  } else {
3516  if (is_numeric($template)) {
3517  $templateId = $template;
3518  } else {
3519  $templateLabel = $template;
3520  }
3521  }
3522 
3523  $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3524  if (is_numeric($arraymessage) && $arraymessage <= 0) {
3525  $langs->load("errors");
3526  $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3527  return 0;
3528  }
3529 
3530  // PREPARE EMAIL
3531  $errormesg = '';
3532 
3533  // Make substitution in email content
3534  $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice);
3535 
3536  complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3537 
3538  // Topic
3539  $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3540 
3541  // Content
3542  $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3543 
3544  $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3545 
3546  // Recipient
3547  $to = array();
3548  if ($forcerecipient) { // If a recipient was forced
3549  $to = array($forcerecipient);
3550  } else {
3551  $res = $tmpinvoice->fetch_thirdparty();
3552  $recipient = $tmpinvoice->thirdparty;
3553  if ($res > 0) {
3554  $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3555  if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3556  foreach ($tmparraycontact as $data_email) {
3557  if (!empty($data_email['email'])) {
3558  $to[] = $data_email['email'];
3559  }
3560  }
3561  }
3562  if (empty($to) && !empty($recipient->email)) {
3563  $to[] = $recipient->email;
3564  }
3565  if (empty($to)) {
3566  $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3567  $error++;
3568  }
3569  } else {
3570  $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3571  $error++;
3572  }
3573  }
3574 
3575  // Sender
3576  $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3577  if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3578  $from = $arraymessage->email_from;
3579  }
3580  if (empty($from)) {
3581  $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3582  $error++;
3583  }
3584 
3585  if (!$error && !empty($to)) {
3586  $this->db->begin();
3587 
3588  $to = implode(',', $to);
3589  if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3590  $to = $to.','.$arraymessage->email_to;
3591  }
3592 
3593  // Errors Recipient
3594  $errors_to = $conf->global->MAIN_MAIL_ERRORS_TO;
3595 
3596  $trackid = 'inv'.$tmpinvoice->id;
3597  $sendcontext = 'standard';
3598 
3599  $email_tocc = '';
3600  if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3601  $email_tocc = $arraymessage->email_tocc;
3602  }
3603 
3604  $email_tobcc = '';
3605  if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3606  $email_tobcc = $arraymessage->email_tobcc;
3607  }
3608 
3609  // Mail Creation
3610  $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3611 
3612  // Sending Mail
3613  if ($cMailFile->sendfile()) {
3614  $nbMailSend++;
3615 
3616  // Add a line into event table
3617  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3618 
3619  // Insert record of emails sent
3620  $actioncomm = new ActionComm($this->db);
3621 
3622  $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3623  $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3624  $actioncomm->contact_id = 0;
3625 
3626  $actioncomm->code = 'AC_EMAIL';
3627  $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3628  $actioncomm->note_private = $sendContent;
3629  $actioncomm->fk_project = $tmpinvoice->fk_project;
3630  $actioncomm->datep = dol_now();
3631  $actioncomm->datef = $actioncomm->datep;
3632  $actioncomm->percentage = -1; // Not applicable
3633  $actioncomm->authorid = $user->id; // User saving action
3634  $actioncomm->userownerid = $user->id; // Owner of action
3635  // Fields when action is an email (content should be added into note)
3636  $actioncomm->email_msgid = $cMailFile->msgid;
3637  $actioncomm->email_subject = $sendTopic;
3638  $actioncomm->email_from = $from;
3639  $actioncomm->email_sender = '';
3640  $actioncomm->email_to = $to;
3641  //$actioncomm->email_tocc = $sendtocc;
3642  //$actioncomm->email_tobcc = $sendtobcc;
3643  //$actioncomm->email_subject = $subject;
3644  $actioncomm->errors_to = $errors_to;
3645 
3646  $actioncomm->elementtype = 'invoice_supplier';
3647  $actioncomm->fk_element = $tmpinvoice->id;
3648 
3649  //$actioncomm->extraparams = $extraparams;
3650 
3651  $actioncomm->create($user);
3652  } else {
3653  $errormesg = $cMailFile->error.' : '.$to;
3654  $error++;
3655 
3656  // Add a line into event table
3657  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3658 
3659  // Insert record of emails sent
3660  $actioncomm = new ActionComm($this->db);
3661 
3662  $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3663  $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3664  $actioncomm->contact_id = 0;
3665 
3666  $actioncomm->code = 'AC_EMAIL';
3667  $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3668  $actioncomm->note_private = $errormesg;
3669  $actioncomm->fk_project = $tmpinvoice->fk_project;
3670  $actioncomm->datep = dol_now();
3671  $actioncomm->datef = $actioncomm->datep;
3672  $actioncomm->percentage = -1; // Not applicable
3673  $actioncomm->authorid = $user->id; // User saving action
3674  $actioncomm->userownerid = $user->id; // Owner of action
3675  // Fields when action is an email (content should be added into note)
3676  $actioncomm->email_msgid = $cMailFile->msgid;
3677  $actioncomm->email_from = $from;
3678  $actioncomm->email_sender = '';
3679  $actioncomm->email_to = $to;
3680  //$actioncomm->email_tocc = $sendtocc;
3681  //$actioncomm->email_tobcc = $sendtobcc;
3682  //$actioncomm->email_subject = $subject;
3683  $actioncomm->errors_to = $errors_to;
3684 
3685  //$actioncomm->extraparams = $extraparams;
3686 
3687  $actioncomm->create($user);
3688  }
3689 
3690  $this->db->commit(); // We always commit
3691  }
3692 
3693  if ($errormesg) {
3694  $errorsMsg[] = $errormesg;
3695  }
3696  } else {
3697  $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3698  $error++;
3699  }
3700  }
3701  }
3702  } else {
3703  $error++;
3704  }
3705 
3706  if (!$error) {
3707  $this->output .= 'Nb of emails sent : '.$nbMailSend;
3708 
3709  dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3710 
3711  return 0;
3712  } else {
3713  $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3714 
3715  dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3716 
3717  return $error;
3718  }
3719  }
3720 }
3721 
3722 
3723 
3728 {
3732  public $element = 'facture_fourn_det';
3733 
3737  public $table_element = 'facture_fourn_det';
3738 
3742  public $oldline;
3743 
3748  public $ref;
3749 
3754  public $product_ref;
3755 
3761  public $ref_supplier;
3762 
3767  public $product_desc;
3768 
3775  public $pu_ht;
3776 
3781  public $subprice;
3782 
3787  public $pu_ttc;
3788 
3789 
3794  public $fk_facture_fourn;
3795 
3801  public $label;
3802 
3808  public $description;
3809 
3810  public $date_start;
3811  public $date_end;
3812 
3816  public $fk_code_ventilation;
3817 
3821  public $skip_update_total; // Skip update price total for special lines
3822 
3826  public $situation_percent;
3827 
3831  public $fk_prev_id;
3832 
3837  public $vat_src_code;
3838 
3843  public $tva_tx;
3844 
3849  public $localtax1_tx;
3850 
3855  public $localtax2_tx;
3856 
3861  public $qty;
3862 
3867  public $remise_percent;
3868 
3873  public $pa_ht;
3874 
3879  public $total_ht;
3880 
3885  public $total_ttc;
3886 
3891  public $total_tva;
3892 
3897  public $total_localtax1;
3898 
3903  public $total_localtax2;
3904 
3908  public $fk_product;
3909 
3914  public $product_type;
3915 
3920  public $product_label;
3921 
3928  public $info_bits;
3929 
3934  public $fk_remise_except;
3935 
3939  public $fk_parent_line;
3940 
3944  public $special_code;
3945 
3949  public $rang;
3950 
3955  public $localtax1_type;
3956 
3961  public $localtax2_type;
3962 
3963 
3969  public function __construct($db)
3970  {
3971  $this->db = $db;
3972  }
3973 
3980  public function fetch($rowid)
3981  {
3982  $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.tva_tx';
3983  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3984  $sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_facture_fourn, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
3985  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3986  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3987  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3988  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3989  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3990  $sql .= ' ORDER BY f.rang, f.rowid';
3991 
3992  $query = $this->db->query($sql);
3993 
3994  if (!$query) {
3995  $this->errors[] = $this->db->error();
3996  return -1;
3997  }
3998 
3999  if (!$this->db->num_rows($query)) {
4000  return 0;
4001  }
4002 
4003  $obj = $this->db->fetch_object($query);
4004 
4005  $this->id = $obj->rowid;
4006  $this->rowid = $obj->rowid;
4007  $this->fk_facture_fourn = $obj->fk_facture_fourn;
4008  $this->description = $obj->line_desc;
4009  $this->desc = $obj->line_desc;
4010  $this->date_start = $obj->date_start;
4011  $this->date_end = $obj->date_end;
4012  $this->product_ref = $obj->product_ref;
4013  $this->ref_supplier = $obj->ref_supplier;
4014  $this->product_desc = $obj->product_desc;
4015 
4016  $this->subprice = $obj->pu_ht;
4017  $this->pu_ht = $this->subprice;
4018  $this->pu_ttc = $obj->pu_ttc;
4019  $this->tva_tx = $obj->tva_tx;
4020  $this->localtax1_tx = $obj->localtax1_tx;
4021  $this->localtax2_tx = $obj->localtax2_tx;
4022  $this->localtax1_type = $obj->localtax1_type;
4023  $this->localtax2_type = $obj->localtax2_type;
4024 
4025  $this->qty = $obj->qty;
4026  $this->remise_percent = $obj->remise_percent;
4027  $this->fk_remise_except = $obj->fk_remise_except;
4028  //$this->tva = $obj->total_tva; // deprecated
4029  $this->total_ht = $obj->total_ht;
4030  $this->total_tva = $obj->total_tva;
4031  $this->total_localtax1 = $obj->total_localtax1;
4032  $this->total_localtax2 = $obj->total_localtax2;
4033  $this->total_ttc = $obj->total_ttc;
4034  $this->fk_product = $obj->fk_product;
4035  $this->product_type = $obj->product_type;
4036  $this->product_label = $obj->product_label;
4037  $this->label = $obj->product_label;
4038  $this->info_bits = $obj->info_bits;
4039  $this->fk_parent_line = $obj->fk_parent_line;
4040  $this->special_code = $obj->special_code;
4041  $this->rang = $obj->rang;
4042  $this->fk_unit = $obj->fk_unit;
4043 
4044  $this->multicurrency_subprice = $obj->multicurrency_subprice;
4045  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
4046  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
4047  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
4048 
4049  $this->fetch_optionals();
4050 
4051  return 1;
4052  }
4053 
4060  public function delete($notrigger = 0)
4061  {
4062  global $user;
4063 
4064  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
4065 
4066  $error = 0;
4067 
4068  $this->db->begin();
4069 
4070  if (!$notrigger) {
4071  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
4072  $error++;
4073  }
4074  }
4075 
4076  $this->deleteObjectLinked();
4077 
4078  // Remove extrafields
4079  if (!$error) {
4080  $result = $this->deleteExtraFields();
4081  if ($result < 0) {
4082  $error++;
4083  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4084  }
4085  }
4086 
4087  if (!$error) {
4088  // Supprime ligne
4089  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
4090  $sql .= " WHERE rowid = ".((int) $this->id);
4091  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4092  $resql = $this->db->query($sql);
4093  if (!$resql) {
4094  $error++;
4095  $this->error = $this->db->lasterror();
4096  }
4097  }
4098 
4099  if (!$error) {
4100  $this->db->commit();
4101  return 1;
4102  } else {
4103  $this->db->rollback();
4104  return -1;
4105  }
4106  }
4107 
4114  public function update($notrigger = 0)
4115  {
4116  global $conf;
4117 
4118  $pu = price2num($this->subprice);
4119  $qty = price2num($this->qty);
4120 
4121  // Check parameters
4122  if (empty($this->qty)) {
4123  $this->qty = 0;
4124  }
4125 
4126  if ($this->product_type < 0) {
4127  return -1;
4128  }
4129 
4130  // Clean parameters
4131  if (empty($this->remise_percent)) {
4132  $this->remise_percent = 0;
4133  }
4134  if (empty($this->tva_tx)) {
4135  $this->tva_tx = 0;
4136  }
4137  if (empty($this->localtax1_tx)) {
4138  $this->localtax1_tx = 0;
4139  }
4140  if (empty($this->localtax2_tx)) {
4141  $this->localtax2_tx = 0;
4142  }
4143 
4144  if (empty($this->pa_ht)) {
4145  $this->pa_ht = 0;
4146  }
4147  if (empty($this->multicurrency_subprice)) {
4148  $this->multicurrency_subprice = 0;
4149  }
4150  if (empty($this->multicurrency_total_ht)) {
4151  $this->multicurrency_total_ht = 0;
4152  }
4153  if (empty($this->multicurrency_total_tva)) {
4154  $this->multicurrency_total_tva = 0;
4155  }
4156  if (empty($this->multicurrency_total_ttc)) {
4157  $this->multicurrency_total_ttc = 0;
4158  }
4159 
4160  $fk_product = (int) $this->fk_product;
4161  $fk_unit = (int) $this->fk_unit;
4162 
4163  $this->db->begin();
4164 
4165  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4166  $sql .= " description = '".$this->db->escape(empty($this->description) ? $this->desc : $this->description)."'";
4167  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
4168  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
4169  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
4170  $sql .= ", pu_ht = ".price2num($this->subprice);
4171  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
4172  $sql .= ", qty = ".price2num($this->qty);
4173  $sql .= ", remise_percent = ".price2num($this->remise_percent);
4174  if ($this->fk_remise_except > 0) {
4175  $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
4176  } else {
4177  $sql .= ", fk_remise_except=null";
4178  }
4179  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4180  $sql .= ", tva_tx = ".price2num($this->tva_tx);
4181  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
4182  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
4183  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
4184  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
4185  $sql .= ", total_ht = ".price2num($this->total_ht);
4186  $sql .= ", tva= ".price2num($this->total_tva);
4187  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
4188  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
4189  $sql .= ", total_ttc = ".price2num($this->total_ttc);
4190  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
4191  $sql .= ", product_type = ".((int) $this->product_type);
4192  $sql .= ", info_bits = ".((int) $this->info_bits);
4193  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
4194 
4195  if (!empty($this->rang)) {
4196  $sql .= ", rang=".((int) $this->rang);
4197  }
4198 
4199  // Multicurrency
4200  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4201  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4202  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4203  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4204 
4205  $sql .= " WHERE rowid = ".((int) $this->id);
4206 
4207  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4208  $resql = $this->db->query($sql);
4209 
4210  if (!$resql) {
4211  $this->db->rollback();
4212  $this->error = $this->db->lasterror();
4213  return -1;
4214  }
4215 
4216  $this->rowid = $this->id;
4217  $error = 0;
4218 
4219  if (!$error) {
4220  $result = $this->insertExtraFields();
4221  if ($result < 0) {
4222  $error++;
4223  }
4224  }
4225 
4226  if (!$error && !$notrigger) {
4227  global $langs, $user;
4228 
4229  // Call trigger
4230  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
4231  $this->db->rollback();
4232  return -1;
4233  }
4234  // End call triggers
4235  }
4236 
4237  if ($error) {
4238  $this->db->rollback();
4239  return -1;
4240  }
4241 
4242  $this->db->commit();
4243  return 1;
4244  }
4245 
4253  public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
4254  {
4255  global $user, $langs;
4256 
4257  $error = 0;
4258 
4259  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4260 
4261  // Clean parameters
4262  $this->desc = trim($this->desc);
4263  if (empty($this->tva_tx)) {
4264  $this->tva_tx = 0;
4265  }
4266  if (empty($this->localtax1_tx)) {
4267  $this->localtax1_tx = 0;
4268  }
4269  if (empty($this->localtax2_tx)) {
4270  $this->localtax2_tx = 0;
4271  }
4272  if (empty($this->localtax1_type)) {
4273  $this->localtax1_type = 0.0;
4274  }
4275  if (empty($this->localtax2_type)) {
4276  $this->localtax2_type = 0.0;
4277  }
4278  if (empty($this->total_tva)) {
4279  $this->total_tva = 0;
4280  }
4281  if (empty($this->total_localtax1)) {
4282  $this->total_localtax1 = 0;
4283  }
4284  if (empty($this->total_localtax2)) {
4285  $this->total_localtax2 = 0;
4286  }
4287  if (empty($this->rang)) {
4288  $this->rang = 0;
4289  }
4290  if (empty($this->remise_percent)) {
4291  $this->remise_percent = 0;
4292  }
4293  if (empty($this->info_bits)) {
4294  $this->info_bits = 0;
4295  }
4296  if (empty($this->subprice)) {
4297  $this->subprice = 0;
4298  }
4299  if (empty($this->special_code)) {
4300  $this->special_code = 0;
4301  }
4302  if (empty($this->fk_parent_line)) {
4303  $this->fk_parent_line = 0;
4304  }
4305  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
4306  $this->situation_percent = 100;
4307  }
4308 
4309  if (empty($this->pa_ht)) {
4310  $this->pa_ht = 0;
4311  }
4312  if (empty($this->multicurrency_subprice)) {
4313  $this->multicurrency_subprice = 0;
4314  }
4315  if (empty($this->multicurrency_total_ht)) {
4316  $this->multicurrency_total_ht = 0;
4317  }
4318  if (empty($this->multicurrency_total_tva)) {
4319  $this->multicurrency_total_tva = 0;
4320  }
4321  if (empty($this->multicurrency_total_ttc)) {
4322  $this->multicurrency_total_ttc = 0;
4323  }
4324 
4325 
4326  // Check parameters
4327  if ($this->product_type < 0) {
4328  $this->error = 'ErrorProductTypeMustBe0orMore';
4329  return -1;
4330  }
4331  if (!empty($this->fk_product) && $this->fk_product > 0) {
4332  // Check product exists
4333  $result = Product::isExistingObject('product', $this->fk_product);
4334  if ($result <= 0) {
4335  $this->error = 'ErrorProductIdDoesNotExists';
4336  return -1;
4337  }
4338  }
4339 
4340  $this->db->begin();
4341 
4342  // Insertion dans base de la ligne
4343  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
4344  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
4345  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4346  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
4347  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
4348  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
4349  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4350  $sql .= ')';
4351  $sql .= " VALUES (".$this->fk_facture_fourn.",";
4352  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4353  $product_label
4354  = !empty($this->product_label)
4355  ? $this->product_label :
4356  (!empty($this->label) ? $this->label : null);
4357  $sql .= " ".(!empty($product_label) ? "'".$this->db->escape($product_label)."'" : "null").",";
4358  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
4359  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
4360  $sql .= " ".price2num($this->qty).",";
4361 
4362  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4363  $sql .= " ".price2num($this->tva_tx).",";
4364  $sql .= " ".price2num($this->localtax1_tx).",";
4365  $sql .= " ".price2num($this->localtax2_tx).",";
4366  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4367  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4368  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4369  $sql .= " ".((int) $this->product_type).",";
4370  $sql .= " ".price2num($this->remise_percent).",";
4371  $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
4372  $sql .= " ".price2num($this->subprice).",";
4373  $sql .= " ".(!empty($this->qty) ? price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
4374  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4375  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4376  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
4377  $sql .= ' '.((int) $this->rang).',';
4378  $sql .= ' '.((int) $this->special_code).',';
4379  $sql .= " ".((int) $this->info_bits).",";
4380  $sql .= " ".price2num($this->total_ht).",";
4381  $sql .= " ".price2num($this->total_tva).",";
4382  $sql .= " ".price2num($this->total_ttc).",";
4383  $sql .= " ".price2num($this->total_localtax1).",";
4384  $sql .= " ".price2num($this->total_localtax2);
4385  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4386  $sql .= ", ".(int) $this->fk_multicurrency;
4387  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4388  $sql .= ", ".price2num($this->multicurrency_subprice);
4389  $sql .= ", ".price2num($this->multicurrency_total_ht);
4390  $sql .= ", ".price2num($this->multicurrency_total_tva);
4391  $sql .= ", ".price2num($this->multicurrency_total_ttc);
4392  $sql .= ')';
4393 
4394  $resql = $this->db->query($sql);
4395  if ($resql) {
4396  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4397  $this->rowid = $this->id; // backward compatibility
4398 
4399  if (!$error) {
4400  $result = $this->insertExtraFields();
4401  if ($result < 0) {
4402  $error++;
4403  }
4404  }
4405 
4406  // Si fk_remise_except defini, on lie la remise a la facture
4407  // ce qui la flague comme "consommee".
4408  if ($this->fk_remise_except) {
4409  $discount = new DiscountAbsolute($this->db);
4410  $result = $discount->fetch($this->fk_remise_except);
4411  if ($result >= 0) {
4412  // Check if discount was found
4413  if ($result > 0) {
4414  // Check if discount not already affected to another invoice
4415  if ($discount->fk_facture_line > 0) {
4416  if (empty($noerrorifdiscountalreadylinked)) {
4417  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4418  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4419  $this->db->rollback();
4420  return -3;
4421  }
4422  } else {
4423  $result = $discount->link_to_invoice($this->id, 0);
4424  if ($result < 0) {
4425  $this->error = $discount->error;
4426  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4427  $this->db->rollback();
4428  return -3;
4429  }
4430  }
4431  } else {
4432  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4433  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4434  $this->db->rollback();
4435  return -3;
4436  }
4437  } else {
4438  $this->error = $discount->error;
4439  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4440  $this->db->rollback();
4441  return -3;
4442  }
4443  }
4444 
4445  if (!$error && !$notrigger) {
4446  // Call trigger
4447  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4448  if ($result < 0) {
4449  $this->db->rollback();
4450  return -2;
4451  }
4452  // End call triggers
4453  }
4454 
4455  $this->db->commit();
4456  return $this->id;
4457  } else {
4458  $this->error = $this->db->error();
4459  $this->db->rollback();
4460  return -2;
4461  }
4462  }
4463 
4464  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4470  public function update_total()
4471  {
4472  // phpcs:enable
4473  $this->db->begin();
4474 
4475  // Mise a jour ligne en base
4476  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4477  $sql .= " total_ht = ".price2num($this->total_ht);
4478  $sql .= ", tva= ".price2num($this->total_tva);
4479  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4480  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4481  $sql .= ", total_ttc = ".price2num($this->total_ttc);
4482  $sql .= " WHERE rowid = ".((int) $this->id);
4483 
4484  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4485 
4486  $resql = $this->db->query($sql);
4487  if ($resql) {
4488  $this->db->commit();
4489  return 1;
4490  } else {
4491  $this->error = $this->db->error();
4492  $this->db->rollback();
4493  return -2;
4494  }
4495  }
4496 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition: security.php:604
$object ref
Definition: info.php:79
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Superclass for invoices classes.
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
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.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
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.
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)
Adds an invoice line (associated with no predefined product/service) The parameters are already suppo...
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 clicable 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 clicable name (with picto eventually)
getTooltipContentArray($params)
getTooltipContentArray
setPaid($user, $close_code='', $close_note='')
Tag invoice as a paid invoice.
update($user=null, $notrigger=0)
Update database.
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 into 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 permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new Form...
Class permettant la generation de composants html autre Only common components are here.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage line invoices.
fetch($rowid)
Retrieves a supplier invoice line.
update($notrigger=0)
Update a supplier invoice line.
insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
Insert line into database.
update_total()
Mise a jour de l'objet ligne de commande en base.
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
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:654
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:123
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1589
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.
Definition: files.lib.php:1438
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:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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_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.
dol_now($mode='auto')
Return date for now.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getDolGlobalString($key, $default='')
Return 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)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
div float
Buy price without taxes.
Definition: style.css.php:959
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:88
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:122