dolibarr  18.0.6
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-2023 Frédéric France <frederic.france@netlogic.fr>
16  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
17  *
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program. If not, see <https://www.gnu.org/licenses/>.
30  */
31 
38 include_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
39 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
40 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
42 
43 if (isModEnabled('accounting')) {
44  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
45  require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
46 }
47 
52 {
56  public $element = 'invoice_supplier';
57 
61  public $table_element = 'facture_fourn';
62 
66  public $table_element_line = 'facture_fourn_det';
67 
71  public $fk_element = 'fk_facture_fourn';
72 
76  public $picto = 'supplier_invoice';
77 
82  public $ismultientitymanaged = 1;
83 
88  public $restrictiononfksoc = 1;
89 
93  protected $table_ref_field = 'ref';
94 
98  public $rowid;
99 
103  public $ref;
104 
108  public $ref_supplier;
109 
113  public $label;
114 
115  public $socid;
116 
117  //Check constants for types
118  public $type = self::TYPE_STANDARD;
119 
125  public $statut;
126 
132  public $status;
133 
139  public $close_code;
140 
145  public $close_note;
146 
151  public $paye;
152 
153  public $author;
154 
160  public $datec;
161 
167  public $tms;
168 
174  public $date;
175 
181  public $date_echeance;
182 
187  public $amount = 0;
192  public $remise = 0;
193 
198  public $tva;
199 
200  // Warning: Do not set default value into property defintion. it must stay null.
201  // For example to avoid to have substition done when object is generic and not yet defined.
202  public $localtax1;
203  public $localtax2;
204  public $total_ht;
205  public $total_tva;
206  public $total_localtax1;
207  public $total_localtax2;
208  public $total_ttc;
209 
214  public $note;
215 
216  public $note_private;
217  public $note_public;
218  public $propalid;
219 
220  public $cond_reglement_id;
221  public $cond_reglement_code;
222  public $cond_reglement_label;
223  public $cond_reglement_doc;
224 
228  public $fk_account; // default bank account
229 
230  public $mode_reglement_id;
231  public $mode_reglement_code;
232 
236  public $transport_mode_id;
237 
241  public $vat_reverse_charge;
242 
243  public $extraparams = array();
244 
249  public $lines = array();
250 
254  public $fournisseur;
255 
256  // Multicurrency
260  public $fk_multicurrency;
261 
262  public $multicurrency_code;
263  public $multicurrency_tx;
264  public $multicurrency_total_ht;
265  public $multicurrency_total_tva;
266  public $multicurrency_total_ttc;
268 
271  public $fk_facture_source;
272 
273  public $fac_rec;
274 
275 
276  public $fields = array(
277  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
278  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
279  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefSupplier', 'enabled'=>1, 'visible'=>-1, 'position'=>20),
280  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>25, 'index'=>1),
281  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>30),
282  'type' =>array('type'=>'smallint(6)', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
283  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>-1, 'notnull'=>1, 'position'=>40),
284  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
285  'datef' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
286  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>55),
287  'libelle' =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
288  'paye' =>array('type'=>'smallint(6)', 'label'=>'Paye', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
289  'amount' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
290  'remise' =>array('type'=>'double(24,8)', 'label'=>'Discount', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
291  'close_code' =>array('type'=>'varchar(16)', 'label'=>'CloseCode', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
292  'close_note' =>array('type'=>'varchar(128)', 'label'=>'CloseNote', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
293  'tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
294  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
295  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>100),
296  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
297  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'TotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
298  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
299  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
300  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>130),
301  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
302  'fk_facture_source' =>array('type'=>'integer', 'label'=>'Fk facture source', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
303  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>145),
304  'fk_account' =>array('type'=>'integer', 'label'=>'Account', 'enabled'=>'isModEnabled("banque")', 'visible'=>-1, 'position'=>150),
305  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
306  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
307  'date_lim_reglement' =>array('type'=>'date', 'label'=>'DateLimReglement', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
308  'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>170),
309  'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>175),
310  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>180),
311  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
312  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
313  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
314  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'MulticurrencyId', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
315  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCode', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
316  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
317  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
318  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
319  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>230),
320  'date_pointoftax' =>array('type'=>'date', 'label'=>'Date pointoftax', 'enabled'=>1, 'visible'=>-1, 'position'=>235),
321  'date_valid' =>array('type'=>'date', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>240),
322  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>245),
323  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
324  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
325  );
326 
327 
331  const TYPE_STANDARD = 0;
332 
336  const TYPE_REPLACEMENT = 1;
337 
341  const TYPE_CREDIT_NOTE = 2;
342 
346  const TYPE_DEPOSIT = 3;
347 
351  const STATUS_DRAFT = 0;
352 
356  const STATUS_VALIDATED = 1;
357 
365  const STATUS_CLOSED = 2;
366 
374  const STATUS_ABANDONED = 3;
375 
376  const CLOSECODE_DISCOUNTVAT = 'discount_vat';
377  const CLOSECODE_BADCREDIT = 'badsupplier';
378  const CLOSECODE_ABANDONED = 'abandon';
379  const CLOSECODE_REPLACED = 'replaced';
380 
386  public function __construct($db)
387  {
388  $this->db = $db;
389  }
390 
397  public function create($user)
398  {
399  global $langs, $conf, $hookmanager;
400 
401  $error = 0;
402  $now = dol_now();
403 
404  // Clean parameters
405  if (isset($this->ref_supplier)) {
406  $this->ref_supplier = trim($this->ref_supplier);
407  }
408  if (empty($this->type)) {
409  $this->type = self::TYPE_STANDARD;
410  }
411  if (empty($this->date)) {
412  $this->date = $now;
413  }
414 
415  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
416  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
417  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
418  } else {
419  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
420  }
421  if (empty($this->fk_multicurrency)) {
422  $this->multicurrency_code = $conf->currency;
423  $this->fk_multicurrency = 0;
424  $this->multicurrency_tx = 1;
425  }
426 
427  $this->db->begin();
428 
429  // Create invoice from a template recurring invoice
430  if ($this->fac_rec > 0) {
431  $this->fk_fac_rec_source = $this->fac_rec;
432 
433  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
434  $_facrec = new FactureFournisseurRec($this->db);
435  $result = $_facrec->fetch($this->fac_rec);
436  $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
437 
438  // Define some dates
439  if (!empty($_facrec->frequency)) {
440  $originaldatewhen = $_facrec->date_when;
441  $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
442  $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
443  $this->socid = $_facrec->socid;
444  }
445 
446  $this->entity = $_facrec->entity; // Invoice created in same entity than template
447 
448  // Fields coming from GUI (priority on template). TODO Value of template should be used as default value on GUI so we can use here always value from GUI
449  $this->fk_project = GETPOST('projectid', 'int') > 0 ? ((int) GETPOST('projectid', 'int')) : $_facrec->fk_projet;
450  $this->fk_projet = $this->fk_project;
451  $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
452  $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
453  $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
454  $this->cond_reglement_id = GETPOST('cond_reglement_id', 'int') > 0 ? ((int) GETPOST('cond_reglement_id', 'int')) : $_facrec->cond_reglement_id;
455  $this->mode_reglement_id = GETPOST('mode_reglement_id', 'int') > 0 ? ((int) GETPOST('mode_reglement_id', 'int')) : $_facrec->mode_reglement_id;
456  $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
457 
458  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
459  $this->total_ht = $_facrec->total_ht;
460  $this->total_ttc = $_facrec->total_ttc;
461 
462  // Fields always coming from template
463  $this->fk_incoterms = $_facrec->fk_incoterms;
464  $this->location_incoterms = $_facrec->location_incoterms;
465 
466  // Clean parameters
467  if (! $this->type) {
468  $this->type = self::TYPE_STANDARD;
469  }
470  if (!empty(GETPOST('ref_supplier'))) {
471  $this->ref_supplier = trim($this->ref_supplier);
472  } else {
473  $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
474  }
475  $this->note_public = trim($this->note_public);
476  $this->note_private = trim($this->note_private);
477  $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->titre));
478 
479  $this->array_options = $_facrec->array_options;
480 
481  if (! $this->mode_reglement_id) {
482  $this->mode_reglement_id = 0;
483  }
484  $this->brouillon = 1;
485  $this->status = self::STATUS_DRAFT;
486  $this->statut = self::STATUS_DRAFT;
487 
488  $this->linked_objects = $_facrec->linkedObjectsIds;
489  // We do not add link to template invoice or next invoice will be linked to all generated invoices
490  //$this->linked_objects['facturerec'][0] = $this->fac_rec;
491 
492  $forceduedate = $this->calculate_date_lim_reglement();
493 
494  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
495  if ($_facrec->frequency > 0) {
496  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
497  if (empty($_facrec->date_when)) {
498  $_facrec->date_when = $now;
499  }
500  $next_date = $_facrec->getNextDate(); // Calculate next date
501  $result = $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date', '', $user, '');
502  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
503  $result = $_facrec->setNextDate($next_date, 1);
504  }
505 
506  // Define lang of customer
507  $outputlangs = $langs;
508  $newlang = '';
509 
510  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
511  $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
512  }
513  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) {
514  $newlang = $this->default_lang; // for thirdparty
515  }
516  if (!empty($newlang)) {
517  $outputlangs = new Translate("", $conf);
518  $outputlangs->setDefaultLang($newlang);
519  }
520 
521  // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
522  $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
523  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
524  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
525  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
526  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
527  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
528  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
529  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
530  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
531  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y');
532  // Only for template invoice
533  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour');
534  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($nextdatewhen, 'dayhour');
535  $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($previousdaynextdatewhen, 'dayhour');
536  $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
537  $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
538 
539  complete_substitutions_array($substitutionarray, $outputlangs);
540 
541  $this->note_public = make_substitutions($this->note_public, $substitutionarray);
542  $this->note_private = make_substitutions($this->note_private, $substitutionarray);
543  }
544 
545  // Define due date if not already defined
546  if (!empty($forceduedate)) {
547  $this->date_echeance = $forceduedate;
548  }
549 
550  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
551  $sql .= "ref";
552  $sql .= ", ref_supplier";
553  $sql .= ", ref_ext";
554  $sql .= ", entity";
555  $sql .= ", type";
556  $sql .= ", libelle";
557  $sql .= ", fk_soc";
558  $sql .= ", datec";
559  $sql .= ", datef";
560  $sql .= ", vat_reverse_charge";
561  $sql .= ", fk_projet";
562  $sql .= ", fk_cond_reglement";
563  $sql .= ", fk_mode_reglement";
564  $sql .= ", fk_account";
565  $sql .= ", note_private";
566  $sql .= ", note_public";
567  $sql .= ", fk_user_author";
568  $sql .= ", date_lim_reglement";
569  $sql .= ", fk_incoterms, location_incoterms";
570  $sql .= ", fk_multicurrency";
571  $sql .= ", multicurrency_code";
572  $sql .= ", multicurrency_tx";
573  $sql .= ", fk_facture_source";
574  $sql .= ", fk_fac_rec_source";
575  $sql .= ")";
576  $sql .= " VALUES (";
577  $sql .= "'(PROV)'";
578  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
579  $sql .= ", '".$this->db->escape($this->ref_ext)."'";
580  $sql .= ", ".((int) $conf->entity);
581  $sql .= ", '".$this->db->escape($this->type)."'";
582  $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
583  $sql .= ", ".((int) $this->socid);
584  $sql .= ", '".$this->db->idate($now)."'";
585  $sql .= ", '".$this->db->idate($this->date)."'";
586  $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0);
587  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
588  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
589  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
590  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
591  $sql .= ", '".$this->db->escape($this->note_private)."'";
592  $sql .= ", '".$this->db->escape($this->note_public)."'";
593  $sql .= ", ".((int) $user->id).",";
594  $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
595  $sql .= ", ".(int) $this->fk_incoterms;
596  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
597  $sql .= ", ".(int) $this->fk_multicurrency;
598  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
599  $sql .= ", ".(double) $this->multicurrency_tx;
600  $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
601  $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
602  $sql .= ")";
603 
604  dol_syslog(get_class($this)."::create", LOG_DEBUG);
605  $resql = $this->db->query($sql);
606  if ($resql) {
607  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
608 
609  // Update ref with new one
610  $this->ref = '(PROV'.$this->id.')';
611  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
612 
613  dol_syslog(get_class($this)."::create", LOG_DEBUG);
614  $resql = $this->db->query($sql);
615  if (!$resql) {
616  $error++;
617  }
618 
619  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
620  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
621  }
622 
623  // Add object linked
624  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
625  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
626  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, ...))
627  foreach ($tmp_origin_id as $origin_id) {
628  $ret = $this->add_object_linked($origin, $origin_id);
629  if (!$ret) {
630  dol_print_error($this->db);
631  $error++;
632  }
633  }
634  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
635  {
636  $origin_id = $tmp_origin_id;
637  $ret = $this->add_object_linked($origin, $origin_id);
638  if (!$ret) {
639  dol_print_error($this->db);
640  $error++;
641  }
642  }
643  }
644  }
645 
646  if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
647  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
648  foreach ($this->lines as $i => $val) {
649  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
650  $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').')';
651 
652  $resql_insert = $this->db->query($sql);
653  if ($resql_insert) {
654  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
655 
656  $res = $this->updateline(
657  $idligne,
658  $this->lines[$i]->description,
659  $this->lines[$i]->subprice,
660  $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
661  $this->lines[$i]->localtax1_tx,
662  $this->lines[$i]->localtax2_tx,
663  $this->lines[$i]->qty,
664  $this->lines[$i]->fk_product,
665  'HT',
666  (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
667  $this->lines[$i]->product_type,
668  $this->lines[$i]->remise_percent,
669  false,
670  $this->lines[$i]->date_start,
671  $this->lines[$i]->date_end,
672  $this->lines[$i]->array_options,
673  $this->lines[$i]->fk_unit,
674  $this->lines[$i]->multicurrency_subprice,
675  $this->lines[$i]->ref_supplier
676  );
677  } else {
678  $this->error = $this->db->lasterror();
679  $this->db->rollback();
680  return -5;
681  }
682  }
683  } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
684  dol_syslog("There is ".count($this->lines)." lines that are array lines");
685  foreach ($this->lines as $i => $val) {
686  $line = $this->lines[$i];
687 
688  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
689  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
690  if (!is_object($line)) {
691  $line = (object) $line;
692  }
693 
694  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
695  $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').')';
696 
697  $resql_insert = $this->db->query($sql);
698  if ($resql_insert) {
699  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
700 
701  $this->updateline(
702  $idligne,
703  $line->description,
704  $line->pu_ht,
705  $line->tva_tx,
706  $line->localtax1_tx,
707  $line->localtax2_tx,
708  $line->qty,
709  $line->fk_product,
710  'HT',
711  (!empty($line->info_bits) ? $line->info_bits : ''),
712  $line->product_type,
713  $line->remise_percent,
714  0,
715  $line->date_start,
716  $line->date_end,
717  $line->array_options,
718  $line->fk_unit,
719  $line->multicurrency_subprice,
720  $line->ref_supplier
721  );
722  } else {
723  $this->error = $this->db->lasterror();
724  $this->db->rollback();
725  return -5;
726  }
727  }
728  }
729 
730  /*
731  * Insert lines of template invoices
732  */
733  if (! $error && $this->fac_rec > 0) {
734  foreach ($_facrec->lines as $i => $val) {
735  if ($_facrec->lines[$i]->fk_product) {
736  $prod = new Product($this->db);
737  $res = $prod->fetch($_facrec->lines[$i]->fk_product);
738  }
739 
740  // For line from template invoice, we use data from template invoice
741  /*
742  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
743  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
744  if (empty($tva_tx)) $tva_npr=0;
745  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
746  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
747  */
748  $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
749  $tva_npr = $_facrec->lines[$i]->info_bits;
750  if (empty($tva_tx)) {
751  $tva_npr = 0;
752  }
753  $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
754  $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
755 
756  $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
757  $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
758 
759  // If buyprice not defined from template invoice, we try to guess the best value
760  if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
761  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
762  $producttmp = new ProductFournisseur($this->db);
763  $producttmp->fetch($_facrec->lines[$i]->fk_product);
764 
765  // If margin module defined on costprice, we try the costprice
766  // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
767  // else we get the best supplier price
768  if ($conf->global->MARGIN_TYPE == 'costprice' && !empty($producttmp->cost_price)) {
769  $buyprice = $producttmp->cost_price;
770  } elseif (isModEnabled('stock') && ($conf->global->MARGIN_TYPE == 'costprice' || $conf->global->MARGIN_TYPE == 'pmp') && !empty($producttmp->pmp)) {
771  $buyprice = $producttmp->pmp;
772  } else {
773  if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
774  if ($producttmp->product_fourn_price_id > 0) {
775  $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
776  }
777  }
778  }
779  }
780 
781  $result_insert = $this->addline(
782  $_facrec->lines[$i]->description,
783  $_facrec->lines[$i]->pu_ht,
784  $tva_tx,
785  $localtax1_tx,
786  $localtax2_tx,
787  $_facrec->lines[$i]->qty,
788  $_facrec->lines[$i]->fk_product,
789  $_facrec->lines[$i]->remise_percent,
790  ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
791  ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
792  0,
793  $_facrec->lines[$i]->info_bits,
794  'HT',
795  0,
796  $_facrec->lines[$i]->rang,
797  false,
798  $_facrec->lines[$i]->array_options,
799  $_facrec->lines[$i]->fk_unit,
800  0,
801  0,
802  $_facrec->lines[$i]->ref_supplier,
803  $_facrec->lines[$i]->special_code,
804  0,
805  0
806  );
807  if ($result_insert < 0) {
808  $error++;
809  $this->error = $this->db->error();
810  break;
811  }
812  }
813  }
814 
815 
816  // Update total price
817  $result = $this->update_price(1);
818  if ($result > 0) {
819  // Actions on extra fields
820  if (!$error) {
821  $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
822  if ($result < 0) {
823  $error++;
824  }
825  }
826 
827  if (!$error) {
828  // Call trigger
829  $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
830  if ($result < 0) {
831  $error++;
832  }
833  // End call triggers
834  }
835 
836  if (!$error) {
837  $this->db->commit();
838  return $this->id;
839  } else {
840  $this->db->rollback();
841  return -4;
842  }
843  } else {
844  $this->error = $langs->trans('FailedToUpdatePrice');
845  $this->db->rollback();
846  return -3;
847  }
848  } else {
849  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
850  $this->error = $langs->trans('ErrorRefAlreadyExists');
851  $this->db->rollback();
852  return -1;
853  } else {
854  $this->error = $this->db->lasterror();
855  $this->db->rollback();
856  return -2;
857  }
858  }
859  }
860 
869  public function fetch($id = '', $ref = '', $ref_ext = '')
870  {
871  if (empty($id) && empty($ref) && empty($ref_ext)) {
872  return -1;
873  }
874 
875  $sql = "SELECT";
876  $sql .= " t.rowid,";
877  $sql .= " t.ref,";
878  $sql .= " t.ref_supplier,";
879  $sql .= " t.ref_ext,";
880  $sql .= " t.entity,";
881  $sql .= " t.type,";
882  $sql .= " t.fk_soc,";
883  $sql .= " t.datec,";
884  $sql .= " t.datef,";
885  $sql .= " t.tms,";
886  $sql .= " t.libelle as label,";
887  $sql .= " t.paye,";
888  $sql .= " t.close_code,";
889  $sql .= " t.close_note,";
890  $sql .= " t.tva,";
891  $sql .= " t.localtax1,";
892  $sql .= " t.localtax2,";
893  $sql .= " t.total_ht,";
894  $sql .= " t.total_tva,";
895  $sql .= " t.total_ttc,";
896  $sql .= " t.fk_statut as status,";
897  $sql .= " t.fk_user_author,";
898  $sql .= " t.fk_user_valid,";
899  $sql .= " t.fk_facture_source,";
900  $sql .= " t.vat_reverse_charge,";
901  $sql .= " t.fk_fac_rec_source,";
902  $sql .= " t.fk_projet as fk_project,";
903  $sql .= " t.fk_cond_reglement,";
904  $sql .= " t.fk_account,";
905  $sql .= " t.fk_mode_reglement,";
906  $sql .= " t.date_lim_reglement,";
907  $sql .= " t.note_private,";
908  $sql .= " t.note_public,";
909  $sql .= " t.model_pdf,";
910  $sql .= " t.import_key,";
911  $sql .= " t.extraparams,";
912  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
913  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
914  $sql .= ' s.nom as socnom, s.rowid as socid,';
915  $sql .= ' t.fk_incoterms, t.location_incoterms,';
916  $sql .= " i.libelle as label_incoterms,";
917  $sql .= ' t.fk_transport_mode,';
918  $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
919  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
920  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
921  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
922  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
923  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
924  if ($id) {
925  $sql .= " WHERE t.rowid = ".((int) $id);
926  } else {
927  $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
928  if ($ref) {
929  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
930  }
931  if ($ref_ext) {
932  $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
933  }
934  }
935 
936  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
937  $resql = $this->db->query($sql);
938  if ($resql) {
939  if ($this->db->num_rows($resql)) {
940  $obj = $this->db->fetch_object($resql);
941 
942  $this->id = $obj->rowid;
943  $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
944 
945  $this->ref_supplier = $obj->ref_supplier;
946  $this->ref_ext = $obj->ref_ext;
947  $this->entity = $obj->entity;
948  $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
949  $this->socid = $obj->fk_soc;
950  $this->datec = $this->db->jdate($obj->datec);
951  $this->date = $this->db->jdate($obj->datef);
952  $this->datep = $this->db->jdate($obj->datef);
953  $this->tms = $this->db->jdate($obj->tms);
954  $this->libelle = $obj->label; // deprecated
955  $this->label = $obj->label;
956  $this->paye = $obj->paye;
957  $this->paid = $obj->paye;
958  $this->close_code = $obj->close_code;
959  $this->close_note = $obj->close_note;
960  $this->total_localtax1 = $obj->localtax1;
961  $this->total_localtax2 = $obj->localtax2;
962  $this->total_ht = $obj->total_ht;
963  $this->total_tva = $obj->total_tva;
964  $this->total_ttc = $obj->total_ttc;
965  $this->status = $obj->status;
966  $this->statut = $obj->status; // For backward compatibility
967  $this->fk_statut = $obj->status; // For backward compatibility
968  $this->fk_user_author = $obj->fk_user_author;
969  $this->author = $obj->fk_user_author;
970  $this->fk_user_valid = $obj->fk_user_valid;
971  $this->fk_facture_source = $obj->fk_facture_source;
972  $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? '0' : '1';
973  $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
974  $this->fk_project = $obj->fk_project;
975  $this->cond_reglement_id = $obj->fk_cond_reglement;
976  $this->cond_reglement_code = $obj->cond_reglement_code;
977  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
978  $this->cond_reglement_label = $obj->cond_reglement_label;
979  $this->cond_reglement_doc = $obj->cond_reglement_doc;
980  $this->fk_account = $obj->fk_account;
981  $this->mode_reglement_id = $obj->fk_mode_reglement;
982  $this->mode_reglement_code = $obj->mode_reglement_code;
983  $this->mode_reglement = $obj->mode_reglement_label;
984  $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
985  $this->note = $obj->note_private; // deprecated
986  $this->note_private = $obj->note_private;
987  $this->note_public = $obj->note_public;
988  $this->model_pdf = $obj->model_pdf;
989  $this->modelpdf = $obj->model_pdf; // deprecated
990  $this->import_key = $obj->import_key;
991 
992  //Incoterms
993  $this->fk_incoterms = $obj->fk_incoterms;
994  $this->location_incoterms = $obj->location_incoterms;
995  $this->label_incoterms = $obj->label_incoterms;
996  $this->transport_mode_id = $obj->fk_transport_mode;
997 
998  // Multicurrency
999  $this->fk_multicurrency = $obj->fk_multicurrency;
1000  $this->multicurrency_code = $obj->multicurrency_code;
1001  $this->multicurrency_tx = $obj->multicurrency_tx;
1002  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1003  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1004  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1005 
1006  $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1007 
1008  $this->socid = $obj->socid;
1009  $this->socnom = $obj->socnom;
1010 
1011  // Retrieve all extrafield
1012  // fetch optionals attributes and labels
1013  $this->fetch_optionals();
1014 
1015  if ($this->statut == self::STATUS_DRAFT) {
1016  $this->brouillon = 1;
1017  }
1018 
1019  $result = $this->fetch_lines();
1020  if ($result < 0) {
1021  $this->error = $this->db->lasterror();
1022  return -3;
1023  }
1024  } else {
1025  $this->error = 'Bill with id '.$id.' not found';
1026  dol_syslog(get_class($this).'::fetch '.$this->error);
1027  return 0;
1028  }
1029 
1030  $this->db->free($resql);
1031  return 1;
1032  } else {
1033  $this->error = "Error ".$this->db->lasterror();
1034  return -1;
1035  }
1036  }
1037 
1038 
1039  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1045  public function fetch_lines()
1046  {
1047  // phpcs:enable
1048  $this->lines = array();
1049 
1050  $sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
1051  $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';
1052  $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';
1053  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.description as product_desc';
1054  $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';
1055  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1056  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1057  $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1058  $sql .= ' ORDER BY f.rang, f.rowid';
1059 
1060  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1061 
1062  $resql_rows = $this->db->query($sql);
1063  if ($resql_rows) {
1064  $num_rows = $this->db->num_rows($resql_rows);
1065  if ($num_rows) {
1066  $i = 0;
1067  while ($i < $num_rows) {
1068  $obj = $this->db->fetch_object($resql_rows);
1069 
1070  $line = new SupplierInvoiceLine($this->db);
1071 
1072  $line->id = $obj->rowid;
1073  $line->rowid = $obj->rowid;
1074  $line->description = $obj->description;
1075  $line->date_start = $obj->date_start;
1076  $line->date_end = $obj->date_end;
1077 
1078  $line->product_ref = $obj->product_ref;
1079  $line->ref = $obj->product_ref;
1080  $line->ref_supplier = $obj->ref_supplier;
1081  $line->libelle = $obj->label;
1082  $line->label = $obj->label;
1083  $line->product_desc = $obj->product_desc;
1084  $line->subprice = $obj->pu_ht;
1085  $line->pu_ht = $obj->pu_ht;
1086  $line->pu_ttc = $obj->pu_ttc;
1087 
1088  $line->vat_src_code = $obj->vat_src_code;
1089  $line->tva_tx = $obj->tva_tx;
1090  $line->localtax1_tx = $obj->localtax1_tx;
1091  $line->localtax2_tx = $obj->localtax2_tx;
1092  $line->localtax1_type = $obj->localtax1_type;
1093  $line->localtax2_type = $obj->localtax2_type;
1094  $line->qty = $obj->qty;
1095  $line->remise_percent = $obj->remise_percent;
1096  $line->fk_remise_except = $obj->fk_remise_except;
1097  //$line->tva = $obj->total_tva; // deprecated
1098  $line->total_ht = $obj->total_ht;
1099  $line->total_ttc = $obj->total_ttc;
1100  $line->total_tva = $obj->total_tva;
1101  $line->total_localtax1 = $obj->total_localtax1;
1102  $line->total_localtax2 = $obj->total_localtax2;
1103  $line->fk_facture_fourn = $obj->fk_facture_fourn;
1104  $line->fk_product = $obj->fk_product;
1105  $line->product_type = $obj->product_type;
1106  $line->product_label = $obj->label;
1107  $line->info_bits = $obj->info_bits;
1108  $line->fk_parent_line = $obj->fk_parent_line;
1109  $line->special_code = $obj->special_code;
1110  $line->rang = $obj->rang;
1111  $line->fk_unit = $obj->fk_unit;
1112 
1113  // Accountancy
1114  $line->code_ventilation = $obj->fk_code_ventilation;
1115  $line->fk_accounting_account = $obj->fk_code_ventilation;
1116 
1117  // Multicurrency
1118  $line->fk_multicurrency = $obj->fk_multicurrency;
1119  $line->multicurrency_code = $obj->multicurrency_code;
1120  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1121  $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1122  $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1123  $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1124 
1125  // Extra fields
1126  $line->fetch_optionals();
1127 
1128  $this->lines[$i] = $line;
1129 
1130  $i++;
1131  }
1132  }
1133  $this->db->free($resql_rows);
1134  return 1;
1135  } else {
1136  $this->error = $this->db->error();
1137  dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1138  return -3;
1139  }
1140  }
1141 
1142 
1150  public function update($user = null, $notrigger = 0)
1151  {
1152  global $conf, $langs;
1153  $error = 0;
1154 
1155  // Clean parameters
1156  if (empty($this->type)) {
1157  $this->type = self::TYPE_STANDARD;
1158  }
1159  if (isset($this->ref)) {
1160  $this->ref = trim($this->ref);
1161  }
1162  if (isset($this->ref_supplier)) {
1163  $this->ref_supplier = trim($this->ref_supplier);
1164  }
1165  if (isset($this->ref_ext)) {
1166  $this->ref_ext = trim($this->ref_ext);
1167  }
1168  if (isset($this->entity)) {
1169  $this->entity = trim($this->entity);
1170  }
1171  if (isset($this->type)) {
1172  $this->type = trim($this->type);
1173  }
1174  if (isset($this->socid)) {
1175  $this->socid = trim($this->socid);
1176  }
1177  if (isset($this->label)) {
1178  $this->label = trim($this->label);
1179  }
1180  if (isset($this->libelle)) {
1181  $this->libelle = trim($this->libelle); // deprecated
1182  }
1183  if (isset($this->paye)) {
1184  $this->paye = trim($this->paye);
1185  }
1186  if (isset($this->close_code)) {
1187  $this->close_code = trim($this->close_code);
1188  }
1189  if (isset($this->close_note)) {
1190  $this->close_note = trim($this->close_note);
1191  }
1192  if (isset($this->localtax1)) {
1193  $this->localtax1 = trim($this->localtax1);
1194  }
1195  if (isset($this->localtax2)) {
1196  $this->localtax2 = trim($this->localtax2);
1197  }
1198  if (empty($this->total_ht)) {
1199  $this->total_ht = 0;
1200  }
1201  if (empty($this->total_tva)) {
1202  $this->total_tva = 0;
1203  }
1204  // if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
1205  // if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
1206  if (isset($this->total_ttc)) {
1207  $this->total_ttc = trim($this->total_ttc);
1208  }
1209  if (isset($this->statut)) {
1210  $this->statut = (int) $this->statut;
1211  }
1212  if (isset($this->status)) {
1213  $this->status = (int) $this->status;
1214  }
1215  if (isset($this->author)) {
1216  $this->author = trim($this->author);
1217  }
1218  if (isset($this->fk_user_valid)) {
1219  $this->fk_user_valid = trim($this->fk_user_valid);
1220  }
1221  if (isset($this->fk_facture_source)) {
1222  $this->fk_facture_source = trim($this->fk_facture_source);
1223  }
1224  if (isset($this->fk_project)) {
1225  if (empty($this->fk_project)) $this->fk_project = null;
1226  else $this->fk_project = intval($this->fk_project);
1227  }
1228  if (isset($this->cond_reglement_id)) {
1229  $this->cond_reglement_id = trim($this->cond_reglement_id);
1230  }
1231  if (isset($this->note_private)) {
1232  $this->note = trim($this->note_private);
1233  }
1234  if (isset($this->note_public)) {
1235  $this->note_public = trim($this->note_public);
1236  }
1237  if (isset($this->model_pdf)) {
1238  $this->model_pdf = trim($this->model_pdf);
1239  }
1240  if (isset($this->import_key)) {
1241  $this->import_key = trim($this->import_key);
1242  }
1243 
1244 
1245  // Check parameters
1246  // Put here code to add control on parameters values
1247 
1248  // Update request
1249  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1250  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1251  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1252  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1253  $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1254  $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1255  $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1256  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1257  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1258  if (dol_strlen($this->tms) != 0) {
1259  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1260  }
1261  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1262  $sql .= " paye=".(isset($this->paye) ? ((int) $this->paye) : "0").",";
1263  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1264  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1265  $sql .= " localtax1=".(isset($this->localtax1) ? ((float) $this->localtax1) : "null").",";
1266  $sql .= " localtax2=".(isset($this->localtax2) ? ((float) $this->localtax2) : "null").",";
1267  $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1268  $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1269  $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1270  $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1271  $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1272  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1273  $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1274  $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1275  $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1276  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1277  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1278  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1279  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1280  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1281  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1282  $sql .= " WHERE rowid=".((int) $this->id);
1283 
1284  $this->db->begin();
1285 
1286  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1287  $resql = $this->db->query($sql);
1288 
1289  if (!$resql) {
1290  $error++;
1291 
1292  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1293  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1294  } else {
1295  $this->errors[] = "Error ".$this->db->lasterror();
1296  }
1297  }
1298 
1299  if (!$error) {
1300  $result = $this->insertExtraFields();
1301  if ($result < 0) {
1302  $error++;
1303  }
1304  }
1305 
1306  if (!$error) {
1307  if (!$notrigger) {
1308  // Call trigger
1309  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1310  if ($result < 0) {
1311  $error++;
1312  }
1313  // End call triggers
1314  }
1315  }
1316 
1317  // Commit or rollback
1318  if ($error) {
1319  foreach ($this->errors as $errmsg) {
1320  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1321  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1322  }
1323  $this->db->rollback();
1324  return -1 * $error;
1325  } else {
1326  $this->db->commit();
1327  return 1;
1328  }
1329  }
1330 
1331  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1338  public function insert_discount($idremise)
1339  {
1340  // phpcs:enable
1341  global $conf, $langs;
1342 
1343  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1344  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1345 
1346  $this->db->begin();
1347 
1348  $remise = new DiscountAbsolute($this->db);
1349  $result = $remise->fetch($idremise);
1350 
1351  if ($result > 0) {
1352  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1353  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1354  $this->db->rollback();
1355  return -5;
1356  }
1357 
1358  $facligne = new SupplierInvoiceLine($this->db);
1359  $facligne->fk_facture_fourn = $this->id;
1360  $facligne->fk_remise_except = $remise->id;
1361  $facligne->desc = $remise->description; // Description ligne
1362  $facligne->vat_src_code = $remise->vat_src_code;
1363  $facligne->tva_tx = $remise->tva_tx;
1364  $facligne->subprice = -$remise->amount_ht;
1365  $facligne->fk_product = 0; // Id produit predefini
1366  $facligne->product_type = 0;
1367  $facligne->qty = 1;
1368  $facligne->remise_percent = 0;
1369  $facligne->rang = -1;
1370  $facligne->info_bits = 2;
1371 
1372  if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
1373  $facligne->rang = 1;
1374  $linecount = count($this->lines);
1375  for ($ii = 1; $ii <= $linecount; $ii++) {
1376  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii+1);
1377  }
1378  }
1379 
1380  // Get buy/cost price of invoice that is source of discount
1381  if ($remise->fk_invoice_supplier_source > 0) {
1382  $srcinvoice = new FactureFournisseur($this->db);
1383  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1384  $totalcostpriceofinvoice = 0;
1385  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1386  $formmargin = new FormMargin($this->db);
1387  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1388  $facligne->pa_ht = $arraytmp['pa_total'];
1389  }
1390 
1391  $facligne->total_ht = -$remise->amount_ht;
1392  $facligne->total_tva = -$remise->amount_tva;
1393  $facligne->total_ttc = -$remise->amount_ttc;
1394 
1395  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1396  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1397  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1398  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1399 
1400  $lineid = $facligne->insert();
1401  if ($lineid > 0) {
1402  $result = $this->update_price(1);
1403  if ($result > 0) {
1404  // Create link between discount and invoice line
1405  $result = $remise->link_to_invoice($lineid, 0);
1406  if ($result < 0) {
1407  $this->error = $remise->error;
1408  $this->db->rollback();
1409  return -4;
1410  }
1411 
1412  $this->db->commit();
1413  return 1;
1414  } else {
1415  $this->error = $facligne->error;
1416  $this->db->rollback();
1417  return -1;
1418  }
1419  } else {
1420  $this->error = $facligne->error;
1421  $this->db->rollback();
1422  return -2;
1423  }
1424  } else {
1425  $this->db->rollback();
1426  return -3;
1427  }
1428  }
1429 
1430 
1438  public function delete(User $user, $notrigger = 0)
1439  {
1440  global $langs, $conf;
1441 
1442  $rowid = $this->id;
1443 
1444  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1445 
1446  // TODO Test if there is at least on payment. If yes, refuse to delete.
1447 
1448  $error = 0;
1449  $this->db->begin();
1450 
1451  if (!$error && !$notrigger) {
1452  // Call trigger
1453  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1454  if ($result < 0) {
1455  $this->db->rollback();
1456  return -1;
1457  }
1458  // Fin appel triggers
1459  }
1460 
1461  if (!$error) {
1462  // If invoice was converted into a discount not yet consumed, we remove discount
1463  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1464  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1465  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1466  $resql = $this->db->query($sql);
1467 
1468  // If invoice has consumned discounts
1469  $this->fetch_lines();
1470  $list_rowid_det = array();
1471  foreach ($this->lines as $key => $invoiceline) {
1472  $list_rowid_det[] = $invoiceline->rowid;
1473  }
1474 
1475  // Consumned discounts are freed
1476  if (count($list_rowid_det)) {
1477  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1478  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1479  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(join(',', $list_rowid_det)).')';
1480 
1481  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1482  if (!$this->db->query($sql)) {
1483  $error++;
1484  }
1485  }
1486  }
1487 
1488  if (!$error) {
1489  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1490  $ef = $main."_extrafields";
1491  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1492  $resqlef = $this->db->query($sqlef);
1493  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1494  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1495  $resql = $this->db->query($sql);
1496  if ($resqlef && $resql) {
1497  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1498  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1499  $resql2 = $this->db->query($sql);
1500  if (!$resql2) {
1501  $error++;
1502  }
1503  } else {
1504  $error++;
1505  }
1506  }
1507 
1508  if (!$error) {
1509  // Delete linked object
1510  $res = $this->deleteObjectLinked();
1511  if ($res < 0) {
1512  $error++;
1513  }
1514  }
1515 
1516  if (!$error) {
1517  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1518  $this->deleteEcmFiles();
1519 
1520  // We remove directory
1521  if ($conf->fournisseur->facture->dir_output) {
1522  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1523 
1524  $ref = dol_sanitizeFileName($this->ref);
1525  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1526  $file = $dir."/".$ref.".pdf";
1527  if (file_exists($file)) {
1528  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1529  $this->error = 'ErrorFailToDeleteFile';
1530  $error++;
1531  }
1532  }
1533  if (file_exists($dir)) {
1534  $res = @dol_delete_dir_recursive($dir);
1535 
1536  if (!$res) {
1537  $this->error = 'ErrorFailToDeleteDir';
1538  $error++;
1539  }
1540  }
1541  }
1542  }
1543 
1544  // Remove extrafields
1545  if (!$error) {
1546  $result = $this->deleteExtraFields();
1547  if ($result < 0) {
1548  $error++;
1549  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1550  }
1551  }
1552 
1553  if (!$error) {
1554  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1555  $this->db->commit();
1556  return 1;
1557  } else {
1558  $this->error = $this->db->lasterror();
1559  $this->db->rollback();
1560  return -$error;
1561  }
1562  }
1563 
1564 
1565  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1576  public function set_paid($user, $close_code = '', $close_note = '')
1577  {
1578  // phpcs:enable
1579  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1580  return $this->setPaid($user, $close_code, $close_note);
1581  }
1582 
1591  public function setPaid($user, $close_code = '', $close_note = '')
1592  {
1593  $error = 0;
1594 
1595  if ($this->paye != 1) {
1596  $this->db->begin();
1597 
1598  $now = dol_now();
1599 
1600  dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1601 
1602  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1603  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1604  if (!$close_code) {
1605  $sql .= ', paye=1';
1606  }
1607  if ($close_code) {
1608  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1609  }
1610  if ($close_note) {
1611  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1612  }
1613  $sql .= ', fk_user_closing = '.((int) $user->id);
1614  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1615  $sql .= ' WHERE rowid = '.((int) $this->id);
1616 
1617  $resql = $this->db->query($sql);
1618  if ($resql) {
1619  // Call trigger
1620  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1621  if ($result < 0) {
1622  $error++;
1623  }
1624  // End call triggers
1625  } else {
1626  $error++;
1627  $this->error = $this->db->error();
1628  dol_print_error($this->db);
1629  }
1630 
1631  if (!$error) {
1632  $this->db->commit();
1633  return 1;
1634  } else {
1635  $this->db->rollback();
1636  return -1;
1637  }
1638  } else {
1639  return 0;
1640  }
1641  }
1642 
1643  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1654  public function set_unpaid($user)
1655  {
1656  // phpcs:enable
1657  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1658  return $this->setUnpaid($user);
1659  }
1660 
1669  public function setUnpaid($user)
1670  {
1671  $error = 0;
1672 
1673  $this->db->begin();
1674 
1675  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1676  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1677  $sql .= ' date_closing=null,';
1678  $sql .= ' fk_user_closing=null';
1679  $sql .= ' WHERE rowid = '.((int) $this->id);
1680 
1681  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1682  $resql = $this->db->query($sql);
1683  if ($resql) {
1684  // Call trigger
1685  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1686  if ($result < 0) {
1687  $error++;
1688  }
1689  // End call triggers
1690  } else {
1691  $error++;
1692  $this->error = $this->db->error();
1693  dol_print_error($this->db);
1694  }
1695 
1696  if (!$error) {
1697  $this->db->commit();
1698  return 1;
1699  } else {
1700  $this->db->rollback();
1701  return -1;
1702  }
1703  }
1704 
1715  public function setCanceled($user, $close_code = '', $close_note = '')
1716  {
1717  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1718 
1719  $this->db->begin();
1720 
1721  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1722  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1723  if ($close_code) {
1724  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1725  }
1726  if ($close_note) {
1727  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1728  }
1729  $sql .= " WHERE rowid = ".((int) $this->id);
1730 
1731  $resql = $this->db->query($sql);
1732  if ($resql) {
1733  // Bound discounts are deducted from the invoice
1734  // as they have not been used since the invoice is abandoned.
1735  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1736  $sql .= ' SET fk_invoice_supplier = NULL';
1737  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1738 
1739  $resql = $this->db->query($sql);
1740  if ($resql) {
1741  // Call trigger
1742  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1743  if ($result < 0) {
1744  $this->db->rollback();
1745  return -1;
1746  }
1747  // End call triggers
1748 
1749  $this->db->commit();
1750  return 1;
1751  } else {
1752  $this->error = $this->db->error()." sql=".$sql;
1753  $this->db->rollback();
1754  return -1;
1755  }
1756  } else {
1757  $this->error = $this->db->error()." sql=".$sql;
1758  $this->db->rollback();
1759  return -2;
1760  }
1761  }
1762 
1772  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1773  {
1774  global $conf, $langs;
1775 
1776  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1777 
1778  $now = dol_now();
1779 
1780  $error = 0;
1781  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1782 
1783  // Force to have object complete for checks
1784  $this->fetch_thirdparty();
1785  $this->fetch_lines();
1786 
1787  // Check parameters
1788  if ($this->statut > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1789  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1790  return 0;
1791  }
1792  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1793  $langs->load("errors");
1794  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1795  return -1;
1796  }
1797  if (count($this->lines) <= 0) {
1798  $langs->load("errors");
1799  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1800  return -1;
1801  }
1802 
1803  $this->db->begin();
1804 
1805  // Define new ref
1806  if ($force_number) {
1807  $num = $force_number;
1808  } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1809  $num = $this->getNextNumRef($this->thirdparty);
1810  } else {
1811  $num = $this->ref;
1812  }
1813  $this->newref = dol_sanitizeFileName($num);
1814 
1815  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1816  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1817  $sql .= " WHERE rowid = ".((int) $this->id);
1818 
1819  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1820  $resql = $this->db->query($sql);
1821  if ($resql) {
1822  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1823  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1824  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1825  $langs->load("agenda");
1826 
1827  $cpt = count($this->lines);
1828  for ($i = 0; $i < $cpt; $i++) {
1829  if ($this->lines[$i]->fk_product > 0) {
1830  $mouvP = new MouvementStock($this->db);
1831  $mouvP->origin = &$this;
1832  $mouvP->setOrigin($this->element, $this->id);
1833  // We increase stock for product
1834  $up_ht_disc = $this->lines[$i]->subprice;
1835  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1836  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1837  }
1839  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1840  } else {
1841  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1842  }
1843  if ($result < 0) {
1844  $error++;
1845  }
1846  }
1847  }
1848  }
1849 
1850  // Triggers call
1851  if (!$error && empty($notrigger)) {
1852  // Call trigger
1853  $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1854  if ($result < 0) {
1855  $error++;
1856  }
1857  // End call triggers
1858  }
1859 
1860  if (!$error) {
1861  $this->oldref = $this->ref;
1862 
1863  // Rename directory if dir was a temporary ref
1864  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1865  // Now we rename also files into index
1866  $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)."'";
1867  $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;
1868  $resql = $this->db->query($sql);
1869  if (!$resql) {
1870  $error++; $this->error = $this->db->lasterror();
1871  }
1872  $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)."'";
1873  $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1874  $resql = $this->db->query($sql);
1875  if (!$resql) {
1876  $error++; $this->error = $this->db->lasterror();
1877  }
1878 
1879  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1880  $oldref = dol_sanitizeFileName($this->ref);
1881  $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1882  $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1883  if (!$error && file_exists($dirsource)) {
1884  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1885 
1886  if (@rename($dirsource, $dirdest)) {
1887  dol_syslog("Rename ok");
1888  // Rename docs starting with $oldref with $this->newref
1889  $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, '/'));
1890  foreach ($listoffiles as $fileentry) {
1891  $dirsource = $fileentry['name'];
1892  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1893  $dirsource = $fileentry['path'].'/'.$dirsource;
1894  $dirdest = $fileentry['path'].'/'.$dirdest;
1895  @rename($dirsource, $dirdest);
1896  }
1897  }
1898  }
1899  }
1900  }
1901 
1902  // Set new ref and define current statut
1903  if (!$error) {
1904  $this->ref = $this->newref;
1905  $this->statut = self::STATUS_VALIDATED;
1906  //$this->date_validation=$now; this is stored into log table
1907  }
1908 
1909  if (!$error) {
1910  $this->db->commit();
1911  return 1;
1912  } else {
1913  $this->db->rollback();
1914  return -1;
1915  }
1916  } else {
1917  $this->error = $this->db->error();
1918  $this->db->rollback();
1919  return -1;
1920  }
1921  }
1922 
1930  public function setDraft($user, $idwarehouse = -1)
1931  {
1932  // phpcs:enable
1933  global $conf, $langs;
1934 
1935  $error = 0;
1936 
1937  if ($this->statut == self::STATUS_DRAFT) {
1938  dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1939  return 0;
1940  }
1941 
1942  dol_syslog(__METHOD__, LOG_DEBUG);
1943 
1944  $this->db->begin();
1945 
1946  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1947  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1948  $sql .= " WHERE rowid = ".((int) $this->id);
1949 
1950  $result = $this->db->query($sql);
1951  if ($result) {
1952  if (!$error) {
1953  $this->oldcopy = clone $this;
1954  }
1955 
1956  // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
1957  if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1958  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1959  $langs->load("agenda");
1960 
1961  $cpt = count($this->lines);
1962  for ($i = 0; $i < $cpt; $i++) {
1963  if ($this->lines[$i]->fk_product > 0) {
1964  $mouvP = new MouvementStock($this->db);
1965  $mouvP->origin = &$this;
1966  $mouvP->setOrigin($this->element, $this->id);
1967  // We increase stock for product
1969  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1970  } else {
1971  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1972  }
1973  }
1974  }
1975  }
1976  // Triggers call
1977  if (!$error && empty($notrigger)) {
1978  // Call trigger
1979  $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
1980  if ($result < 0) {
1981  $error++;
1982  }
1983  // End call triggers
1984  }
1985  if ($error == 0) {
1986  $this->db->commit();
1987  return 1;
1988  } else {
1989  $this->db->rollback();
1990  return -1;
1991  }
1992  } else {
1993  $this->error = $this->db->error();
1994  $this->db->rollback();
1995  return -1;
1996  }
1997  }
1998 
1999 
2033  public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = '', $date_end = '', $ventil = 0, $info_bits = '', $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = false, $array_options = 0, $fk_unit = null, $origin_id = 0, $pu_devise = 0, $ref_supplier = '', $special_code = '', $fk_parent_line = 0, $fk_remise_except = 0)
2034  {
2035  global $langs, $mysoc, $conf;
2036 
2037  dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$ventil,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG);
2038  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2039 
2040  if ($this->statut == self::STATUS_DRAFT) {
2041  // Clean parameters
2042  if (empty($remise_percent)) {
2043  $remise_percent = 0;
2044  }
2045  if (empty($qty)) {
2046  $qty = 0;
2047  }
2048  if (empty($info_bits)) {
2049  $info_bits = 0;
2050  }
2051  if (empty($rang)) {
2052  $rang = 0;
2053  }
2054  if (empty($ventil)) {
2055  $ventil = 0;
2056  }
2057  if (empty($txtva)) {
2058  $txtva = 0;
2059  }
2060  if (empty($txlocaltax1)) {
2061  $txlocaltax1 = 0;
2062  }
2063  if (empty($txlocaltax2)) {
2064  $txlocaltax2 = 0;
2065  }
2066 
2067  $remise_percent = price2num($remise_percent);
2068  $qty = price2num($qty);
2069  $pu = price2num($pu);
2070  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2071  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2072  }
2073  $txlocaltax1 = price2num($txlocaltax1);
2074  $txlocaltax2 = price2num($txlocaltax2);
2075 
2076  if ($date_start && $date_end && $date_start > $date_end) {
2077  $langs->load("errors");
2078  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2079  return -1;
2080  }
2081 
2082  $this->db->begin();
2083 
2084  if ($fk_product > 0) {
2085  if (!empty($conf->global->SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY)) {
2086  // Check quantity is enough
2087  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2088  $prod = new ProductFournisseur($this->db);
2089  if ($prod->fetch($fk_product) > 0) {
2090  $product_type = $prod->type;
2091  $label = $prod->label;
2092  $fk_prod_fourn_price = 0;
2093 
2094  // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2095  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2096  $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
2097  if ($result > 0) {
2098  if (empty($pu)) {
2099  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2100  }
2101  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2102  // is remise percent not keyed but present for the product we add it
2103  if ($remise_percent == 0 && $prod->remise_percent != 0) {
2104  $remise_percent = $prod->remise_percent;
2105  }
2106  }
2107  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2108  $langs->load("errors");
2109  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2110  $this->db->rollback();
2111  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2112  //$pu = $prod->fourn_pu; // We do not overwrite unit price
2113  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2114  return -1;
2115  }
2116  if ($result == -1) {
2117  $langs->load("errors");
2118  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2119  $this->db->rollback();
2120  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2121  return -1;
2122  }
2123  if ($result < -1) {
2124  $this->error = $prod->error;
2125  $this->db->rollback();
2126  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2127  return -1;
2128  }
2129  } else {
2130  $this->error = $prod->error;
2131  $this->db->rollback();
2132  return -1;
2133  }
2134  }
2135  } else {
2136  $product_type = $type;
2137  }
2138 
2139  if (isModEnabled("multicurrency") && $pu_devise > 0) {
2140  $pu = 0;
2141  }
2142 
2143  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2144 
2145  // Clean vat code
2146  $reg = array();
2147  $vat_src_code = '';
2148  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2149  $vat_src_code = $reg[1];
2150  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2151  }
2152 
2153  // Calcul du total TTC et de la TVA pour la ligne a partir de
2154  // qty, pu, remise_percent et txtva
2155  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2156  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2157 
2158  $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);
2159  $total_ht = $tabprice[0];
2160  $total_tva = $tabprice[1];
2161  $total_ttc = $tabprice[2];
2162  $total_localtax1 = $tabprice[9];
2163  $total_localtax2 = $tabprice[10];
2164  $pu_ht = $tabprice[3];
2165 
2166  // MultiCurrency
2167  $multicurrency_total_ht = $tabprice[16];
2168  $multicurrency_total_tva = $tabprice[17];
2169  $multicurrency_total_ttc = $tabprice[18];
2170  $pu_ht_devise = $tabprice[19];
2171 
2172  // Check parameters
2173  if ($type < 0) {
2174  return -1;
2175  }
2176 
2177  if ($rang < 0) {
2178  $rangmax = $this->line_max();
2179  $rang = $rangmax + 1;
2180  }
2181 
2182  // Insert line
2183  $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2184 
2185  $supplierinvoiceline->context = $this->context;
2186 
2187  $supplierinvoiceline->fk_facture_fourn = $this->id;
2188  //$supplierinvoiceline->label=$label; // deprecated
2189  $supplierinvoiceline->desc = $desc;
2190  $supplierinvoiceline->ref_supplier = $ref_supplier;
2191 
2192  $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2193  $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2194 
2195  $supplierinvoiceline->vat_src_code = $vat_src_code;
2196  $supplierinvoiceline->tva_tx = $txtva;
2197  $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2198  $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2199  $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2200  $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2201 
2202  $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
2203  $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
2204  $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
2205  $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
2206  $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
2207 
2208  $supplierinvoiceline->fk_product = $fk_product;
2209  $supplierinvoiceline->product_type = $type;
2210  $supplierinvoiceline->remise_percent = $remise_percent;
2211  $supplierinvoiceline->date_start = $date_start;
2212  $supplierinvoiceline->date_end = $date_end;
2213  $supplierinvoiceline->fk_code_ventilation = $ventil;
2214  $supplierinvoiceline->rang = $rang;
2215  $supplierinvoiceline->info_bits = $info_bits;
2216  $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2217 
2218 
2219  $supplierinvoiceline->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
2220  $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2221  $supplierinvoiceline->origin = $this->origin;
2222  $supplierinvoiceline->origin_id = $origin_id;
2223  $supplierinvoiceline->fk_unit = $fk_unit;
2224 
2225  // Multicurrency
2226  $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2227  $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2228  $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
2229 
2230  $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
2231  $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
2232  $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
2233 
2234  if (is_array($array_options) && count($array_options) > 0) {
2235  $supplierinvoiceline->array_options = $array_options;
2236  }
2237 
2238  $result = $supplierinvoiceline->insert($notrigger);
2239  if ($result > 0) {
2240  // Reorder if child line
2241  if (!empty($fk_parent_line)) {
2242  $this->line_order(true, 'DESC');
2243  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2244  $linecount = count($this->lines);
2245  for ($ii = $rang; $ii <= $linecount; $ii++) {
2246  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2247  }
2248  }
2249 
2250  // Mise a jour informations denormalisees au niveau de la facture meme
2251  $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.
2252  if ($result > 0) {
2253  $this->db->commit();
2254  return $supplierinvoiceline->id;
2255  } else {
2256  $this->error = $this->db->error();
2257  $this->db->rollback();
2258  return -1;
2259  }
2260  } else {
2261  $this->error = $supplierinvoiceline->error;
2262  $this->errors = $supplierinvoiceline->errors;
2263  $this->db->rollback();
2264  return -2;
2265  }
2266  } else {
2267  return 0;
2268  }
2269  }
2270 
2296  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 = false, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_devise = 0, $ref_supplier = '', $rang = 0)
2297  {
2298  global $mysoc, $langs;
2299 
2300  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);
2301  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2302 
2303  $pu = price2num($pu);
2304  $qty = price2num($qty);
2305  $remise_percent = price2num($remise_percent);
2306  $pu_devise = price2num($pu_devise);
2307 
2308  // Check parameters
2309  //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2310  if ($type < 0) {
2311  return -1;
2312  }
2313 
2314  if ($date_start && $date_end && $date_start > $date_end) {
2315  $langs->load("errors");
2316  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2317  return -1;
2318  }
2319 
2320  // Clean parameters
2321  if (empty($vatrate)) {
2322  $vatrate = 0;
2323  }
2324  if (empty($txlocaltax1)) {
2325  $txlocaltax1 = 0;
2326  }
2327  if (empty($txlocaltax2)) {
2328  $txlocaltax2 = 0;
2329  }
2330 
2331  $txlocaltax1 = price2num($txlocaltax1);
2332  $txlocaltax2 = price2num($txlocaltax2);
2333 
2334  // Calcul du total TTC et de la TVA pour la ligne a partir de
2335  // qty, pu, remise_percent et txtva
2336  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2337  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2338 
2339  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2340 
2341  $reg = array();
2342 
2343  // Clean vat code
2344  $vat_src_code = '';
2345  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2346  $vat_src_code = $reg[1];
2347  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2348  }
2349 
2350  $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);
2351  $total_ht = $tabprice[0];
2352  $total_tva = $tabprice[1];
2353  $total_ttc = $tabprice[2];
2354  $pu_ht = $tabprice[3];
2355  $pu_tva = $tabprice[4];
2356  $pu_ttc = $tabprice[5];
2357  $total_localtax1 = $tabprice[9];
2358  $total_localtax2 = $tabprice[10];
2359 
2360  // MultiCurrency
2361  $multicurrency_total_ht = $tabprice[16];
2362  $multicurrency_total_tva = $tabprice[17];
2363  $multicurrency_total_ttc = $tabprice[18];
2364  $pu_ht_devise = $tabprice[19];
2365 
2366  if (empty($info_bits)) {
2367  $info_bits = 0;
2368  }
2369 
2370  //Fetch current line from the database and then clone the object and set it in $oldline property
2371  $line = new SupplierInvoiceLine($this->db);
2372  $line->fetch($id);
2373  $line->fetch_optionals();
2374 
2375  $staticline = clone $line;
2376 
2377  if ($idproduct) {
2378  $product = new Product($this->db);
2379  $result = $product->fetch($idproduct);
2380  $product_type = $product->type;
2381  } else {
2382  $idproduct = $staticline->fk_product;
2383  $product_type = $type;
2384  }
2385 
2386  $line->oldline = $staticline;
2387  $line->context = $this->context;
2388 
2389  $line->description = $desc;
2390 
2391  $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2392  $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2393  $line->pu_ht = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2394  $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2395 
2396  $line->remise_percent = $remise_percent;
2397  $line->ref_supplier = $ref_supplier;
2398 
2399  $line->date_start = $date_start;
2400  $line->date_end = $date_end;
2401 
2402  $line->vat_src_code = $vat_src_code;
2403  $line->tva_tx = $vatrate;
2404  $line->localtax1_tx = $txlocaltax1;
2405  $line->localtax2_tx = $txlocaltax2;
2406  $line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2407  $line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2408 
2409  $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht);
2410  $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
2411  $line->total_localtax1 = $total_localtax1;
2412  $line->total_localtax2 = $total_localtax2;
2413  $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
2414 
2415  $line->fk_product = $idproduct;
2416  $line->product_type = $product_type;
2417  $line->info_bits = $info_bits;
2418  $line->fk_unit = $fk_unit;
2419  $line->rang = $rang;
2420 
2421  if (is_array($array_options) && count($array_options) > 0) {
2422  // We replace values in this->line->array_options only for entries defined into $array_options
2423  foreach ($array_options as $key => $value) {
2424  $line->array_options[$key] = $array_options[$key];
2425  }
2426  }
2427 
2428  // Multicurrency
2429  $line->multicurrency_subprice = $pu_ht_devise;
2430  $line->multicurrency_total_ht = $multicurrency_total_ht;
2431  $line->multicurrency_total_tva = $multicurrency_total_tva;
2432  $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2433 
2434  $res = $line->update($notrigger);
2435 
2436  if ($res < 1) {
2437  $this->errors[] = $line->error;
2438  } else {
2439  // Update total price into invoice record
2440  $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2441  }
2442 
2443  return $res;
2444  }
2445 
2453  public function deleteline($rowid, $notrigger = 0)
2454  {
2455  if (!$rowid) {
2456  $rowid = $this->id;
2457  }
2458 
2459  $this->db->begin();
2460 
2461  // Free the discount linked to a line of invoice
2462  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2463  $sql .= ' SET fk_invoice_supplier_line = NULL';
2464  $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2465 
2466  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2467  $result = $this->db->query($sql);
2468  if (!$result) {
2469  $this->error = $this->db->error();
2470  $this->db->rollback();
2471  return -2;
2472  }
2473 
2474  $line = new SupplierInvoiceLine($this->db);
2475 
2476  if ($line->fetch($rowid) < 1) {
2477  return -1;
2478  }
2479 
2480  $res = $line->delete($notrigger);
2481 
2482  if ($res < 1) {
2483  $this->errors[] = $line->error;
2484  $this->db->rollback();
2485  return -3;
2486  } else {
2487  $res = $this->update_price(1);
2488 
2489  if ($res > 0) {
2490  $this->db->commit();
2491  return 1;
2492  } else {
2493  $this->db->rollback();
2494  $this->error = $this->db->lasterror();
2495  return -4;
2496  }
2497  }
2498  }
2499 
2500 
2507  public function info($id)
2508  {
2509  $sql = 'SELECT c.rowid, datec, tms as datem, ';
2510  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2511  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2512  $sql .= ' WHERE c.rowid = '.((int) $id);
2513 
2514  $result = $this->db->query($sql);
2515  if ($result) {
2516  if ($this->db->num_rows($result)) {
2517  $obj = $this->db->fetch_object($result);
2518 
2519  $this->id = $obj->rowid;
2520  if ($obj->fk_user_author) {
2521  $cuser = new User($this->db);
2522  $cuser->fetch($obj->fk_user_author);
2523  $this->user_creation = $cuser;
2524  }
2525  if ($obj->fk_user_valid) {
2526  $vuser = new User($this->db);
2527  $vuser->fetch($obj->fk_user_valid);
2528  $this->user_validation = $vuser;
2529  }
2530  if ($obj->fk_user_modif) {
2531  $muser = new User($this->db);
2532  $muser->fetch($obj->fk_user_modif);
2533  $this->user_modification = $muser;
2534  }
2535  $this->date_creation = $this->db->jdate($obj->datec);
2536  $this->date_modification = $this->db->jdate($obj->datem);
2537  //$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)
2538  }
2539  $this->db->free($result);
2540  } else {
2541  dol_print_error($this->db);
2542  }
2543  }
2544 
2545  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2554  public function list_replacable_supplier_invoices($socid = 0)
2555  {
2556  // phpcs:enable
2557  global $conf;
2558 
2559  $return = array();
2560 
2561  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2562  $sql .= " ff.rowid as rowidnext";
2563  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2564  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2565  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2566  $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2567  $sql .= " AND f.entity = ".$conf->entity;
2568  $sql .= " AND f.paye = 0"; // Pas classee payee completement
2569  $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2570  $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
2571  if ($socid > 0) {
2572  $sql .= " AND f.fk_soc = ".((int) $socid);
2573  }
2574  $sql .= " ORDER BY f.ref";
2575 
2576  dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2577  $resql = $this->db->query($sql);
2578  if ($resql) {
2579  while ($obj = $this->db->fetch_object($resql)) {
2580  $return[$obj->rowid] = array(
2581  'id' => $obj->rowid,
2582  'ref' => $obj->ref,
2583  'status' => $obj->fk_statut
2584  );
2585  }
2586  //print_r($return);
2587  return $return;
2588  } else {
2589  $this->error = $this->db->error();
2590  return -1;
2591  }
2592  }
2593 
2594  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2604  public function list_qualified_avoir_supplier_invoices($socid = 0)
2605  {
2606  // phpcs:enable
2607  global $conf;
2608 
2609  $return = array();
2610 
2611  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.paye, pf.fk_paiementfourn";
2612  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2613  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2614  $sql .= " WHERE f.entity = ".$conf->entity;
2615  $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2616  $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2617  $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2618  $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2619  if ($socid > 0) {
2620  $sql .= " AND f.fk_soc = ".((int) $socid);
2621  }
2622  $sql .= " ORDER BY f.ref";
2623 
2624  dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2625  $resql = $this->db->query($sql);
2626  if ($resql) {
2627  while ($obj = $this->db->fetch_object($resql)) {
2628  $qualified = 0;
2629  if ($obj->fk_statut == self::STATUS_VALIDATED) {
2630  $qualified = 1;
2631  }
2632  if ($obj->fk_statut == self::STATUS_CLOSED) {
2633  $qualified = 1;
2634  }
2635  if ($qualified) {
2636  $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2637  $return[$obj->rowid] = array('ref'=>$obj->ref, 'status'=>$obj->fk_statut, 'type'=>$obj->type, 'paye'=>$obj->paye, 'paymentornot'=>$paymentornot);
2638  }
2639  }
2640 
2641  return $return;
2642  } else {
2643  $this->error = $this->db->error();
2644  return -1;
2645  }
2646  }
2647 
2648  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2655  public function load_board($user)
2656  {
2657  // phpcs:enable
2658  global $conf, $langs;
2659 
2660  $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2661  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2662  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
2663  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2664  }
2665  $sql .= ' WHERE ff.paye = 0';
2666  $sql .= ' AND ff.fk_statut > 0';
2667  $sql .= " AND ff.entity = ".$conf->entity;
2668  if ($user->socid) {
2669  $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2670  }
2671 
2672  $resql = $this->db->query($sql);
2673  if ($resql) {
2674  $langs->load("bills");
2675  $now = dol_now();
2676 
2677  $response = new WorkboardResponse();
2678  $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2679  $response->label = $langs->trans("SupplierBillsToPay");
2680  $response->labelShort = $langs->trans("StatusToPay");
2681 
2682  $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2683  $response->img = img_object($langs->trans("Bills"), "bill");
2684 
2685  $facturestatic = new FactureFournisseur($this->db);
2686 
2687  while ($obj = $this->db->fetch_object($resql)) {
2688  $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2689  $facturestatic->statut = $obj->status; // For backward compatibility
2690  $facturestatic->status = $obj->status;
2691 
2692  $response->nbtodo++;
2693  $response->total += $obj->total_ht;
2694 
2695  if ($facturestatic->hasDelay()) {
2696  $response->nbtodolate++;
2697  $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2698  }
2699  }
2700 
2701  $this->db->free($resql);
2702  return $response;
2703  } else {
2704  dol_print_error($this->db);
2705  $this->error = $this->db->error();
2706  return -1;
2707  }
2708  }
2709 
2717  public function getTooltipContentArray($params)
2718  {
2719  global $conf, $langs;
2720 
2721  $langs->load('bills');
2722 
2723  $datas = [];
2724  $moretitle = $params['moretitle'] ?? '';
2725  $picto = $this->picto;
2726  if ($this->type == self::TYPE_REPLACEMENT) {
2727  $picto .= 'r'; // Replacement invoice
2728  }
2729  if ($this->type == self::TYPE_CREDIT_NOTE) {
2730  $picto .= 'a'; // Credit note
2731  }
2732  if ($this->type == self::TYPE_DEPOSIT) {
2733  $picto .= 'd'; // Deposit invoice
2734  }
2735 
2736  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2737  if ($this->type == self::TYPE_REPLACEMENT) {
2738  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2739  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2740  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2741  } elseif ($this->type == self::TYPE_DEPOSIT) {
2742  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2743  }
2744  if (isset($this->status)) {
2745  $alreadypaid = -1;
2746  if (isset($this->alreadypaid)) {
2747  $alreadypaid = $this->alreadypaid;
2748  }
2749 
2750  $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2751  }
2752  if ($moretitle) {
2753  $datas['picto'] .= ' - '.$moretitle;
2754  }
2755  if (!empty($this->ref)) {
2756  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2757  }
2758  if (!empty($this->ref_supplier)) {
2759  $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2760  }
2761  if (!empty($this->label)) {
2762  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2763  }
2764  if (!empty($this->date)) {
2765  $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2766  }
2767  if (!empty($this->date_echeance)) {
2768  $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2769  }
2770  if (!empty($this->total_ht)) {
2771  $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2772  }
2773  if (!empty($this->total_tva)) {
2774  $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2775  }
2776  if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2777  // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2778  $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2779  }
2780  if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2781  $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2782  }
2783  if (!empty($this->revenuestamp)) {
2784  $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2785  }
2786  if (!empty($this->total_ttc)) {
2787  $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2788  }
2789  return $datas;
2790  }
2791 
2805  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2806  {
2807  global $langs, $conf, $user, $hookmanager;
2808 
2809  $result = '';
2810 
2811  if ($option == 'withdraw') {
2812  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2813  } elseif ($option == 'document') {
2814  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2815  } else {
2816  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2817  }
2818 
2819  if ($short) {
2820  return $url;
2821  }
2822 
2823  if ($option !== 'nolink') {
2824  // Add param to save lastsearch_values or not
2825  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2826  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2827  $add_save_lastsearch_values = 1;
2828  }
2829  if ($add_save_lastsearch_values) {
2830  $url .= '&save_lastsearch_values=1';
2831  }
2832  }
2833 
2834  $picto = $this->picto;
2835  if ($this->type == self::TYPE_REPLACEMENT) {
2836  $picto .= 'r'; // Replacement invoice
2837  }
2838  if ($this->type == self::TYPE_CREDIT_NOTE) {
2839  $picto .= 'a'; // Credit note
2840  }
2841  if ($this->type == self::TYPE_DEPOSIT) {
2842  $picto .= 'd'; // Deposit invoice
2843  }
2844  $params = [
2845  'id' => $this->id,
2846  'objecttype' => $this->element,
2847  'option' => $option,
2848  'moretitle' => $moretitle,
2849  ];
2850  $classfortooltip = 'classfortooltip';
2851  $dataparams = '';
2852  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2853  $classfortooltip = 'classforajaxtooltip';
2854  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2855  $label = '';
2856  } else {
2857  $label = implode($this->getTooltipContentArray($params));
2858  }
2859 
2860  $ref = $this->ref;
2861  if (empty($ref)) {
2862  $ref = $this->id;
2863  }
2864 
2865  $linkclose = '';
2866  if (empty($notooltip)) {
2867  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2868  $label = $langs->trans("ShowSupplierInvoice");
2869  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2870  }
2871  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2872  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2873  }
2874 
2875  $linkstart = '<a href="'.$url.'"';
2876  $linkstart .= $linkclose.'>';
2877  $linkend = '</a>';
2878 
2879  $result .= $linkstart;
2880  if ($withpicto) {
2881  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2882  }
2883  if ($withpicto != 2) {
2884  $result .= ($max ?dol_trunc($ref, $max) : $ref);
2885  }
2886  $result .= $linkend;
2887 
2888  if ($addlinktonotes) {
2889  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2890  if ($txttoshow) {
2891  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2892  $result .= ' <span class="note inline-block">';
2893  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2894  $result .= img_picto('', 'note');
2895  $result .= '</a>';
2896  $result .= '</span>';
2897  }
2898  }
2899  global $action;
2900  $hookmanager->initHooks(array($this->element . 'dao'));
2901  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2902  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2903  if ($reshook > 0) {
2904  $result = $hookmanager->resPrint;
2905  } else {
2906  $result .= $hookmanager->resPrint;
2907  }
2908  return $result;
2909  }
2910 
2919  public function getNextNumRef($soc, $mode = 'next')
2920  {
2921  global $db, $langs, $conf;
2922  $langs->load("orders");
2923 
2924  // Clean parameters (if not defined or using deprecated value)
2925  if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) {
2926  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2927  }
2928 
2929  $mybool = false;
2930 
2931  $file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2932  $classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2933 
2934  // Include file with class
2935  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2936 
2937  foreach ($dirmodels as $reldir) {
2938  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2939 
2940  // Load file with numbering class (if found)
2941  $mybool |= @include_once $dir.$file;
2942  }
2943 
2944  if ($mybool === false) {
2945  dol_print_error('', "Failed to include file ".$file);
2946  return '';
2947  }
2948 
2949  $obj = new $classname();
2950  $numref = "";
2951  $numref = $obj->getNumRef($soc, $this, $mode);
2952 
2953  if ($numref != "") {
2954  return $numref;
2955  } else {
2956  $this->error = $obj->error;
2957  return -1;
2958  }
2959  }
2960 
2961 
2970  public function initAsSpecimen($option = '')
2971  {
2972  global $langs, $conf;
2973  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2974 
2975  $now = dol_now();
2976 
2977  // Load array of products prodids
2978  $num_prods = 0;
2979  $prodids = array();
2980 
2981  $sql = "SELECT rowid";
2982  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2983  $sql .= " WHERE entity IN (".getEntity('product').")";
2984  $sql .= $this->db->plimit(100);
2985 
2986  $resql = $this->db->query($sql);
2987  if ($resql) {
2988  $num_prods = $this->db->num_rows($resql);
2989  $i = 0;
2990  while ($i < $num_prods) {
2991  $i++;
2992  $row = $this->db->fetch_row($resql);
2993  $prodids[$i] = $row[0];
2994  }
2995  }
2996 
2997  // Initialise parametres
2998  $this->id = 0;
2999  $this->ref = 'SPECIMEN';
3000  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3001  $this->specimen = 1;
3002  $this->socid = 1;
3003  $this->date = $now;
3004  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3005  $this->cond_reglement_code = 'RECEP';
3006  $this->mode_reglement_code = 'CHQ';
3007 
3008  $this->note_public = 'This is a comment (public)';
3009  $this->note_private = 'This is a comment (private)';
3010 
3011  $this->multicurrency_tx = 1;
3012  $this->multicurrency_code = $conf->currency;
3013 
3014  $xnbp = 0;
3015  if (empty($option) || $option != 'nolines') {
3016  // Lines
3017  $nbp = 5;
3018  while ($xnbp < $nbp) {
3019  $line = new SupplierInvoiceLine($this->db);
3020  $line->desc = $langs->trans("Description")." ".$xnbp;
3021  $line->qty = 1;
3022  $line->subprice = 100;
3023  $line->pu_ht = 100; // the canelle template use pu_ht and not subprice
3024  $line->price = 100;
3025  $line->tva_tx = 19.6;
3026  $line->localtax1_tx = 0;
3027  $line->localtax2_tx = 0;
3028  if ($xnbp == 2) {
3029  $line->total_ht = 50;
3030  $line->total_ttc = 59.8;
3031  $line->total_tva = 9.8;
3032  $line->remise_percent = 50;
3033  } else {
3034  $line->total_ht = 100;
3035  $line->total_ttc = 119.6;
3036  $line->total_tva = 19.6;
3037  $line->remise_percent = 0;
3038  }
3039 
3040  if ($num_prods > 0) {
3041  $prodid = mt_rand(1, $num_prods);
3042  $line->fk_product = $prodids[$prodid];
3043  }
3044  $line->product_type = 0;
3045 
3046  $this->lines[$xnbp] = $line;
3047 
3048  $this->total_ht += $line->total_ht;
3049  $this->total_tva += $line->total_tva;
3050  $this->total_ttc += $line->total_ttc;
3051 
3052  $xnbp++;
3053  }
3054  }
3055 
3056  $this->amount_ht = $xnbp * 100;
3057  $this->total_ht = $xnbp * 100;
3058  $this->total_tva = $xnbp * 19.6;
3059  $this->total_ttc = $xnbp * 119.6;
3060  }
3061 
3062  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3068  public function load_state_board()
3069  {
3070  // phpcs:enable
3071  global $conf, $user;
3072 
3073  $this->nb = array();
3074 
3075  $clause = "WHERE";
3076 
3077  $sql = "SELECT count(f.rowid) as nb";
3078  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3079  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3080  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3081  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3082  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3083  $clause = "AND";
3084  }
3085  $sql .= " ".$clause." f.entity = ".$conf->entity;
3086 
3087  $resql = $this->db->query($sql);
3088  if ($resql) {
3089  while ($obj = $this->db->fetch_object($resql)) {
3090  $this->nb["supplier_invoices"] = $obj->nb;
3091  }
3092  $this->db->free($resql);
3093  return 1;
3094  } else {
3095  dol_print_error($this->db);
3096  $this->error = $this->db->error();
3097  return -1;
3098  }
3099  }
3100 
3109  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3110  {
3111  global $conf, $langs;
3112 
3113  $error = 0;
3114 
3115  $object = new FactureFournisseur($this->db);
3116 
3117  $this->db->begin();
3118 
3119  // Load source object
3120  $object->fetch($fromid);
3121  $object->id = 0;
3122  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3123  $object->status = self::STATUS_DRAFT;
3124 
3125  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3126 
3127  // Clear fields
3128  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3129  $object->author = $user->id;
3130  $object->user_valid = 0;
3131  $object->fk_facture_source = 0;
3132  $object->date_creation = '';
3133  $object->date_validation = '';
3134  $object->date = (empty($this->date) ? dol_now() : $this->date);
3135  $object->ref_client = '';
3136  $object->close_code = '';
3137  $object->close_note = '';
3138  if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3139  $object->note_private = '';
3140  $object->note_public = '';
3141  }
3142 
3143  $object->date_echeance = $object->calculate_date_lim_reglement();
3144 
3145  // Loop on each line of new invoice
3146  foreach ($object->lines as $i => $line) {
3147  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3148  unset($object->lines[$i]);
3149  }
3150  }
3151 
3152  // Create clone
3153  $object->context['createfromclone'] = 'createfromclone';
3154  $result = $object->create($user);
3155 
3156  // Other options
3157  if ($result < 0) {
3158  $this->error = $object->error;
3159  $this->errors = $object->errors;
3160  $error++;
3161  }
3162 
3163  if (!$error) {
3164  }
3165 
3166  unset($object->context['createfromclone']);
3167 
3168  // End
3169  if (!$error) {
3170  $this->db->commit();
3171  return $object->id;
3172  } else {
3173  $this->db->rollback();
3174  return -1;
3175  }
3176  }
3177 
3189  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3190  {
3191  global $conf, $user, $langs;
3192 
3193  $langs->load("suppliers");
3194  $outputlangs->load("products");
3195 
3196  // Set the model on the model name to use
3197  if (empty($modele)) {
3198  if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF)) {
3199  $modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
3200  } else {
3201  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3202  }
3203  }
3204 
3205  if (empty($modele)) {
3206  return 0;
3207  } else {
3208  $modelpath = "core/modules/supplier_invoice/doc/";
3209 
3210  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3211  }
3212  }
3213 
3218  public function getRights()
3219  {
3220  global $user;
3221 
3222  return $user->hasRight("fournisseur", "facture");
3223  }
3224 
3233  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3234  {
3235  $tables = array(
3236  'facture_fourn'
3237  );
3238 
3239  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3240  }
3241 
3250  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3251  {
3252  $tables = array(
3253  'facture_fourn_det'
3254  );
3255 
3256  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3257  }
3258 
3264  public function hasDelay()
3265  {
3266  global $conf;
3267 
3268  $now = dol_now();
3269 
3270  if (!$this->date_echeance) {
3271  return false;
3272  }
3273 
3274  $status = isset($this->status) ? $this->status : $this->statut;
3275 
3276  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3277  }
3278 
3284  public function isCreditNoteUsed()
3285  {
3286  $isUsed = false;
3287 
3288  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3289  $resql = $this->db->query($sql);
3290  if (!empty($resql)) {
3291  $obj = $this->db->fetch_object($resql);
3292  if (!empty($obj->fk_invoice_supplier)) {
3293  $isUsed = true;
3294  }
3295  }
3296 
3297  return $isUsed;
3298  }
3306  public function getKanbanView($option = '', $arraydata = null)
3307  {
3308  global $langs;
3309 
3310  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3311 
3312  $return = '<div class="box-flex-item box-flex-grow-zero">';
3313  $return .= '<div class="info-box info-box-sm">';
3314  $return .= '<span class="info-box-icon bg-infobox-action">';
3315  $return .= img_picto('', $this->picto);
3316  $return .= '</span>';
3317  $return .= '<div class="info-box-content">';
3318  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3319  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3320  if (!empty($arraydata['thirdparty'])) {
3321  $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3322  }
3323  if (property_exists($this, 'date')) {
3324  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3325  }
3326  if (property_exists($this, 'total_ht')) {
3327  $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3328  $return .= ' '.$langs->trans("HT");
3329  $return .= '</span>';
3330  }
3331  if (method_exists($this, 'getLibStatut')) {
3332  $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3333  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3334  }
3335  $return .= '</div>';
3336  $return .= '</div>';
3337  $return .= '</div>';
3338  return $return;
3339  }
3340 
3347  public function setVATReverseCharge($vatreversecharge)
3348  {
3349  if (!$this->table_element) {
3350  dol_syslog(get_class($this)."::setVATReverseCharge was called on objet with property table_element not defined", LOG_ERR);
3351  return -1;
3352  }
3353 
3354  dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3355 
3356  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3357  $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3358  $sql .= " WHERE rowid=".((int) $this->id);
3359 
3360  if ($this->db->query($sql)) {
3361  $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3362  return 1;
3363  } else {
3364  dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3365  $this->error = $this->db->error();
3366  return 0;
3367  }
3368  }
3369 }
3370 
3371 
3372 
3377 {
3381  public $element = 'facture_fourn_det';
3382 
3386  public $table_element = 'facture_fourn_det';
3387 
3388  public $oldline;
3389 
3394  public $ref;
3395 
3400  public $product_ref;
3401 
3407  public $ref_supplier;
3408 
3413  public $product_desc;
3414 
3421  public $pu_ht;
3422 
3427  public $subprice;
3428 
3433  public $pu_ttc;
3434 
3435 
3440  public $fk_facture_fourn;
3441 
3447  public $label;
3448 
3453  public $description;
3454 
3455  public $date_start;
3456  public $date_end;
3457 
3458  public $skip_update_total; // Skip update price total for special lines
3459 
3463  public $situation_percent;
3464 
3468  public $fk_prev_id;
3469 
3474  public $vat_src_code;
3475 
3480  public $tva_tx;
3481 
3486  public $localtax1_tx;
3487 
3492  public $localtax2_tx;
3493 
3498  public $qty;
3499 
3504  public $remise_percent;
3505 
3510  public $pa_ht;
3511 
3516  public $total_ht;
3517 
3522  public $total_ttc;
3523 
3528  public $total_tva;
3529 
3534  public $total_localtax1;
3535 
3540  public $total_localtax2;
3541 
3545  public $fk_product;
3546 
3551  public $product_type;
3552 
3557  public $product_label;
3558 
3565  public $info_bits;
3566 
3571  public $fk_remise_except;
3572 
3576  public $fk_parent_line;
3577 
3578  public $special_code;
3579 
3583  public $rang;
3584 
3589  public $localtax1_type;
3590 
3595  public $localtax2_type;
3596 
3597  // Multicurrency
3601  public $fk_multicurrency;
3602 
3603  public $multicurrency_code;
3604  public $multicurrency_subprice;
3605  public $multicurrency_total_ht;
3606  public $multicurrency_total_tva;
3607  public $multicurrency_total_ttc;
3608 
3609 
3615  public function __construct($db)
3616  {
3617  $this->db = $db;
3618  }
3619 
3626  public function fetch($rowid)
3627  {
3628  $sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.tva_tx';
3629  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3630  $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';
3631  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3632  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3633  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3634  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3635  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3636  $sql .= ' ORDER BY f.rang, f.rowid';
3637 
3638  $query = $this->db->query($sql);
3639 
3640  if (!$query) {
3641  $this->errors[] = $this->db->error();
3642  return -1;
3643  }
3644 
3645  if (!$this->db->num_rows($query)) {
3646  return 0;
3647  }
3648 
3649  $obj = $this->db->fetch_object($query);
3650 
3651  $this->id = $obj->rowid;
3652  $this->rowid = $obj->rowid;
3653  $this->fk_facture_fourn = $obj->fk_facture_fourn;
3654  $this->description = $obj->description;
3655  $this->date_start = $obj->date_start;
3656  $this->date_end = $obj->date_end;
3657  $this->product_ref = $obj->product_ref;
3658  $this->ref_supplier = $obj->ref_supplier;
3659  $this->product_desc = $obj->product_desc;
3660 
3661  $this->subprice = $obj->pu_ht;
3662  $this->pu_ht = $obj->pu_ht;
3663  $this->pu_ttc = $obj->pu_ttc;
3664  $this->tva_tx = $obj->tva_tx;
3665  $this->localtax1_tx = $obj->localtax1_tx;
3666  $this->localtax2_tx = $obj->localtax2_tx;
3667  $this->localtax1_type = $obj->localtax1_type;
3668  $this->localtax2_type = $obj->localtax2_type;
3669 
3670  $this->qty = $obj->qty;
3671  $this->remise_percent = $obj->remise_percent;
3672  $this->fk_remise_except = $obj->fk_remise_except;
3673  //$this->tva = $obj->total_tva; // deprecated
3674  $this->total_ht = $obj->total_ht;
3675  $this->total_tva = $obj->total_tva;
3676  $this->total_localtax1 = $obj->total_localtax1;
3677  $this->total_localtax2 = $obj->total_localtax2;
3678  $this->total_ttc = $obj->total_ttc;
3679  $this->fk_product = $obj->fk_product;
3680  $this->product_type = $obj->product_type;
3681  $this->product_label = $obj->product_label;
3682  $this->info_bits = $obj->info_bits;
3683  $this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3684  $this->fk_parent_line = $obj->fk_parent_line;
3685  $this->special_code = $obj->special_code;
3686  $this->rang = $obj->rang;
3687  $this->fk_unit = $obj->fk_unit;
3688 
3689  $this->multicurrency_subprice = $obj->multicurrency_subprice;
3690  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3691  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3692  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3693 
3694  $this->fetch_optionals();
3695 
3696  return 1;
3697  }
3698 
3705  public function delete($notrigger = 0)
3706  {
3707  global $user, $conf;
3708 
3709  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
3710 
3711  $error = 0;
3712 
3713  $this->db->begin();
3714 
3715  if (!$notrigger) {
3716  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3717  $error++;
3718  }
3719  }
3720 
3721  $this->deleteObjectLinked();
3722 
3723  // Remove extrafields
3724  if (!$error) {
3725  $result = $this->deleteExtraFields();
3726  if ($result < 0) {
3727  $error++;
3728  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3729  }
3730  }
3731 
3732  if (!$error) {
3733  // Supprime ligne
3734  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3735  $sql .= " WHERE rowid = ".((int) $this->id);
3736  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3737  $resql = $this->db->query($sql);
3738  if (!$resql) {
3739  $error++;
3740  $this->error = $this->db->lasterror();
3741  }
3742  }
3743 
3744  if (!$error) {
3745  $this->db->commit();
3746  return 1;
3747  } else {
3748  $this->db->rollback();
3749  return -1;
3750  }
3751  }
3752 
3759  public function update($notrigger = 0)
3760  {
3761  global $conf;
3762 
3763  $pu = price2num($this->pu_ht);
3764  $qty = price2num($this->qty);
3765 
3766  // Check parameters
3767  if (empty($this->qty)) {
3768  $this->qty = 0;
3769  }
3770 
3771  if ($this->product_type < 0) {
3772  return -1;
3773  }
3774 
3775  // Clean parameters
3776  if (empty($this->remise_percent)) {
3777  $this->remise_percent = 0;
3778  }
3779  if (empty($this->tva_tx)) {
3780  $this->tva_tx = 0;
3781  }
3782  if (empty($this->localtax1_tx)) {
3783  $this->localtax1_tx = 0;
3784  }
3785  if (empty($this->localtax2_tx)) {
3786  $this->localtax2_tx = 0;
3787  }
3788 
3789  if (empty($this->pa_ht)) {
3790  $this->pa_ht = 0;
3791  }
3792  if (empty($this->multicurrency_subprice)) {
3793  $this->multicurrency_subprice = 0;
3794  }
3795  if (empty($this->multicurrency_total_ht)) {
3796  $this->multicurrency_total_ht = 0;
3797  }
3798  if (empty($this->multicurrency_total_tva)) {
3799  $this->multicurrency_total_tva = 0;
3800  }
3801  if (empty($this->multicurrency_total_ttc)) {
3802  $this->multicurrency_total_ttc = 0;
3803  }
3804 
3805  $fk_product = (int) $this->fk_product;
3806  $fk_unit = (int) $this->fk_unit;
3807 
3808  $this->db->begin();
3809 
3810  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3811  $sql .= " description = '".$this->db->escape($this->description)."'";
3812  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3813  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3814  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3815  $sql .= ", pu_ht = ".price2num($this->pu_ht);
3816  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3817  $sql .= ", qty = ".price2num($this->qty);
3818  $sql .= ", remise_percent = ".price2num($this->remise_percent);
3819  if ($this->fk_remise_except > 0) $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
3820  else $sql .= ", fk_remise_except=null";
3821  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3822  $sql .= ", tva_tx = ".price2num($this->tva_tx);
3823  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3824  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3825  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3826  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3827  $sql .= ", total_ht = ".price2num($this->total_ht);
3828  $sql .= ", tva= ".price2num($this->total_tva);
3829  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3830  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3831  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3832  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
3833  $sql .= ", product_type = ".((int) $this->product_type);
3834  $sql .= ", info_bits = ".((int) $this->info_bits);
3835  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
3836 
3837  if (!empty($this->rang)) {
3838  $sql .= ", rang=".((int) $this->rang);
3839  }
3840 
3841  // Multicurrency
3842  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3843  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3844  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3845  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3846 
3847  $sql .= " WHERE rowid = ".((int) $this->id);
3848 
3849  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3850  $resql = $this->db->query($sql);
3851 
3852  if (!$resql) {
3853  $this->db->rollback();
3854  $this->error = $this->db->lasterror();
3855  return -1;
3856  }
3857 
3858  $this->rowid = $this->id;
3859  $error = 0;
3860 
3861  if (!$error) {
3862  $result = $this->insertExtraFields();
3863  if ($result < 0) {
3864  $error++;
3865  }
3866  }
3867 
3868  if (!$error && !$notrigger) {
3869  global $langs, $user;
3870 
3871  // Call trigger
3872  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
3873  $this->db->rollback();
3874  return -1;
3875  }
3876  // End call triggers
3877  }
3878 
3879  if ($error) {
3880  $this->db->rollback();
3881  return -1;
3882  }
3883 
3884  $this->db->commit();
3885  return 1;
3886  }
3887 
3894  public function insert($notrigger = 0)
3895  {
3896  global $user, $conf, $langs;
3897 
3898  $error = 0;
3899 
3900  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3901 
3902  // Clean parameters
3903  $this->desc = trim($this->desc);
3904  if (empty($this->tva_tx)) {
3905  $this->tva_tx = 0;
3906  }
3907  if (empty($this->localtax1_tx)) {
3908  $this->localtax1_tx = 0;
3909  }
3910  if (empty($this->localtax2_tx)) {
3911  $this->localtax2_tx = 0;
3912  }
3913  if (empty($this->localtax1_type)) {
3914  $this->localtax1_type = '0';
3915  }
3916  if (empty($this->localtax2_type)) {
3917  $this->localtax2_type = '0';
3918  }
3919  if (empty($this->total_tva)) {
3920  $this->total_tva = 0;
3921  }
3922  if (empty($this->total_localtax1)) {
3923  $this->total_localtax1 = 0;
3924  }
3925  if (empty($this->total_localtax2)) {
3926  $this->total_localtax2 = 0;
3927  }
3928  if (empty($this->rang)) {
3929  $this->rang = 0;
3930  }
3931  if (empty($this->remise_percent)) {
3932  $this->remise_percent = 0;
3933  }
3934  if (empty($this->info_bits)) {
3935  $this->info_bits = 0;
3936  }
3937  if (empty($this->subprice)) {
3938  $this->subprice = 0;
3939  }
3940  if (empty($this->special_code)) {
3941  $this->special_code = 0;
3942  }
3943  if (empty($this->fk_parent_line)) {
3944  $this->fk_parent_line = 0;
3945  }
3946  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
3947  $this->situation_percent = 100;
3948  }
3949 
3950  if (empty($this->pa_ht)) {
3951  $this->pa_ht = 0;
3952  }
3953  if (empty($this->multicurrency_subprice)) {
3954  $this->multicurrency_subprice = 0;
3955  }
3956  if (empty($this->multicurrency_total_ht)) {
3957  $this->multicurrency_total_ht = 0;
3958  }
3959  if (empty($this->multicurrency_total_tva)) {
3960  $this->multicurrency_total_tva = 0;
3961  }
3962  if (empty($this->multicurrency_total_ttc)) {
3963  $this->multicurrency_total_ttc = 0;
3964  }
3965 
3966 
3967  // Check parameters
3968  if ($this->product_type < 0) {
3969  $this->error = 'ErrorProductTypeMustBe0orMore';
3970  return -1;
3971  }
3972  if (!empty($this->fk_product) && $this->fk_product > 0) {
3973  // Check product exists
3974  $result = Product::isExistingObject('product', $this->fk_product);
3975  if ($result <= 0) {
3976  $this->error = 'ErrorProductIdDoesNotExists';
3977  return -1;
3978  }
3979  }
3980 
3981  $this->db->begin();
3982 
3983  // Insertion dans base de la ligne
3984  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3985  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3986  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3987  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
3988  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3989  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3990  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3991  $sql .= ')';
3992  $sql .= " VALUES (".$this->fk_facture_fourn.",";
3993  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3994  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3995  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3996  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
3997  $sql .= " ".price2num($this->qty).",";
3998 
3999  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4000  $sql .= " ".price2num($this->tva_tx).",";
4001  $sql .= " ".price2num($this->localtax1_tx).",";
4002  $sql .= " ".price2num($this->localtax2_tx).",";
4003  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4004  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4005  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4006  $sql .= " ".((int) $this->product_type).",";
4007  $sql .= " ".price2num($this->remise_percent).",";
4008  $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
4009  $sql .= " ".price2num($this->subprice).",";
4010  $sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
4011  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4012  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4013  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
4014  $sql .= ' '.((int) $this->rang).',';
4015  $sql .= ' '.((int) $this->special_code).',';
4016  $sql .= " ".((int) $this->info_bits).",";
4017  $sql .= " ".price2num($this->total_ht).",";
4018  $sql .= " ".price2num($this->total_tva).",";
4019  $sql .= " ".price2num($this->total_ttc).",";
4020  $sql .= " ".price2num($this->total_localtax1).",";
4021  $sql .= " ".price2num($this->total_localtax2);
4022  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4023  $sql .= ", ".(int) $this->fk_multicurrency;
4024  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4025  $sql .= ", ".price2num($this->multicurrency_subprice);
4026  $sql .= ", ".price2num($this->multicurrency_total_ht);
4027  $sql .= ", ".price2num($this->multicurrency_total_tva);
4028  $sql .= ", ".price2num($this->multicurrency_total_ttc);
4029  $sql .= ')';
4030 
4031  $resql = $this->db->query($sql);
4032  if ($resql) {
4033  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4034  $this->rowid = $this->id; // backward compatibility
4035 
4036  if (!$error) {
4037  $result = $this->insertExtraFields();
4038  if ($result < 0) {
4039  $error++;
4040  }
4041  }
4042 
4043  // Si fk_remise_except defini, on lie la remise a la facture
4044  // ce qui la flague comme "consommee".
4045  if ($this->fk_remise_except) {
4046  $discount = new DiscountAbsolute($this->db);
4047  $result = $discount->fetch($this->fk_remise_except);
4048  if ($result >= 0) {
4049  // Check if discount was found
4050  if ($result > 0) {
4051  // Check if discount not already affected to another invoice
4052  if ($discount->fk_facture_line > 0) {
4053  if (empty($noerrorifdiscountalreadylinked)) {
4054  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4055  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4056  $this->db->rollback();
4057  return -3;
4058  }
4059  } else {
4060  $result = $discount->link_to_invoice($this->rowid, 0);
4061  if ($result < 0) {
4062  $this->error = $discount->error;
4063  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4064  $this->db->rollback();
4065  return -3;
4066  }
4067  }
4068  } else {
4069  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4070  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4071  $this->db->rollback();
4072  return -3;
4073  }
4074  } else {
4075  $this->error = $discount->error;
4076  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4077  $this->db->rollback();
4078  return -3;
4079  }
4080  }
4081 
4082  if (!$error && !$notrigger) {
4083  // Call trigger
4084  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4085  if ($result < 0) {
4086  $this->db->rollback();
4087  return -2;
4088  }
4089  // End call triggers
4090  }
4091 
4092  $this->db->commit();
4093  return $this->id;
4094  } else {
4095  $this->error = $this->db->error();
4096  $this->db->rollback();
4097  return -2;
4098  }
4099  }
4100 
4101  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4107  public function update_total()
4108  {
4109  // phpcs:enable
4110  $this->db->begin();
4111 
4112  // Mise a jour ligne en base
4113  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4114  $sql .= " total_ht = ".price2num($this->total_ht);
4115  $sql .= ", tva= ".price2num($this->total_tva);
4116  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4117  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4118  $sql .= ", total_ttc = ".price2num($this->total_ttc);
4119  $sql .= " WHERE rowid = ".((int) $this->rowid);
4120 
4121  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4122 
4123  $resql = $this->db->query($sql);
4124  if ($resql) {
4125  $this->db->commit();
4126  return 1;
4127  } else {
4128  $this->error = $this->db->error();
4129  $this->db->rollback();
4130  return -2;
4131  }
4132  }
4133 }
$object ref
Definition: info.php:78
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.
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.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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...
addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product=0, $remise_percent=0, $date_start='', $date_end='', $ventil=0, $info_bits='', $price_base_type='HT', $type=0, $rang=-1, $notrigger=false, $array_options=0, $fk_unit=null, $origin_id=0, $pu_devise=0, $ref_supplier='', $special_code='', $fk_parent_line=0, $fk_remise_except=0)
Adds an invoice line (associated with no predefined product/service) The parameters are already suppo...
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...
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=false, $date_start='', $date_end='', $array_options=0, $fk_unit=null, $pu_devise=0, $ref_supplier='', $rang=0)
Update a line detail into database.
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)
deleteline($rowid, $notrigger=0)
Delete a detail line from database.
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.
load_state_board()
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
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)
getNextNumRef($soc, $mode='next')
Return next reference of supplier invoice not already used (or last reference) according to numbering...
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.
fetch($id='', $ref='', $ref_ext='')
Load object in memory from database.
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.
setDraft($user, $idwarehouse=-1)
Set draft status.
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.
const STATUS_CLOSED
Classified paid.
setVATReverseCharge($vatreversecharge)
Change the option VAT reverse charge.
Class to manage invoice templates.
Classe 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.
insert($notrigger=0)
Insert line into database.
update($notrigger=0)
Update a supplier invoice line.
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:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->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') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:122
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:1507
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:1356
dol_dir_list($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:62
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_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
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_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.
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:926
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:86
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:120