dolibarr  19.0.0-dev
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->fk_soc = $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  return -3;
1138  }
1139  }
1140 
1141 
1149  public function update($user = null, $notrigger = 0)
1150  {
1151  global $conf, $langs;
1152  $error = 0;
1153 
1154  // Clean parameters
1155  if (empty($this->type)) {
1156  $this->type = self::TYPE_STANDARD;
1157  }
1158  if (isset($this->ref)) {
1159  $this->ref = trim($this->ref);
1160  }
1161  if (isset($this->ref_supplier)) {
1162  $this->ref_supplier = trim($this->ref_supplier);
1163  }
1164  if (isset($this->ref_ext)) {
1165  $this->ref_ext = trim($this->ref_ext);
1166  }
1167  if (isset($this->entity)) {
1168  $this->entity = trim($this->entity);
1169  }
1170  if (isset($this->type)) {
1171  $this->type = trim($this->type);
1172  }
1173  if (isset($this->fk_soc)) {
1174  $this->fk_soc = trim($this->fk_soc);
1175  }
1176  if (isset($this->label)) {
1177  $this->label = trim($this->label);
1178  }
1179  if (isset($this->libelle)) {
1180  $this->libelle = trim($this->libelle); // deprecated
1181  }
1182  if (isset($this->paye)) {
1183  $this->paye = trim($this->paye);
1184  }
1185  if (isset($this->close_code)) {
1186  $this->close_code = trim($this->close_code);
1187  }
1188  if (isset($this->close_note)) {
1189  $this->close_note = trim($this->close_note);
1190  }
1191  if (isset($this->localtax1)) {
1192  $this->localtax1 = trim($this->localtax1);
1193  }
1194  if (isset($this->localtax2)) {
1195  $this->localtax2 = trim($this->localtax2);
1196  }
1197  if (empty($this->total_ht)) {
1198  $this->total_ht = 0;
1199  }
1200  if (empty($this->total_tva)) {
1201  $this->total_tva = 0;
1202  }
1203  // if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
1204  // if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
1205  if (isset($this->total_ttc)) {
1206  $this->total_ttc = trim($this->total_ttc);
1207  }
1208  if (isset($this->statut)) {
1209  $this->statut = (int) $this->statut;
1210  }
1211  if (isset($this->status)) {
1212  $this->status = (int) $this->status;
1213  }
1214  if (isset($this->author)) {
1215  $this->author = trim($this->author);
1216  }
1217  if (isset($this->fk_user_valid)) {
1218  $this->fk_user_valid = trim($this->fk_user_valid);
1219  }
1220  if (isset($this->fk_facture_source)) {
1221  $this->fk_facture_source = trim($this->fk_facture_source);
1222  }
1223  if (isset($this->fk_project)) {
1224  if (empty($this->fk_project)) $this->fk_project = null;
1225  else $this->fk_project = intval($this->fk_project);
1226  }
1227  if (isset($this->cond_reglement_id)) {
1228  $this->cond_reglement_id = trim($this->cond_reglement_id);
1229  }
1230  if (isset($this->note_private)) {
1231  $this->note = trim($this->note_private);
1232  }
1233  if (isset($this->note_public)) {
1234  $this->note_public = trim($this->note_public);
1235  }
1236  if (isset($this->model_pdf)) {
1237  $this->model_pdf = trim($this->model_pdf);
1238  }
1239  if (isset($this->import_key)) {
1240  $this->import_key = trim($this->import_key);
1241  }
1242 
1243 
1244  // Check parameters
1245  // Put here code to add control on parameters values
1246 
1247  // Update request
1248  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1249  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1250  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1251  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1252  $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1253  $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1254  $sql .= " fk_soc=".(isset($this->fk_soc) ? ((int) $this->fk_soc) : "null").",";
1255  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1256  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1257  if (dol_strlen($this->tms) != 0) {
1258  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1259  }
1260  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1261  $sql .= " paye=".(isset($this->paye) ? ((int) $this->paye) : "null").",";
1262  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1263  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1264  $sql .= " localtax1=".(isset($this->localtax1) ? ((float) $this->localtax1) : "null").",";
1265  $sql .= " localtax2=".(isset($this->localtax2) ? ((float) $this->localtax2) : "null").",";
1266  $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1267  $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1268  $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1269  $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1270  $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1271  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1272  $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1273  $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1274  $sql .= " fk_projet=".(isset($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1275  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1276  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1277  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1278  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1279  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1280  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1281  $sql .= " WHERE rowid=".((int) $this->id);
1282 
1283  $this->db->begin();
1284 
1285  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1286  $resql = $this->db->query($sql);
1287 
1288  if (!$resql) {
1289  $error++;
1290 
1291  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1292  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1293  } else {
1294  $this->errors[] = "Error ".$this->db->lasterror();
1295  }
1296  }
1297 
1298  if (!$error) {
1299  $result = $this->insertExtraFields();
1300  if ($result < 0) {
1301  $error++;
1302  }
1303  }
1304 
1305  if (!$error) {
1306  if (!$notrigger) {
1307  // Call trigger
1308  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1309  if ($result < 0) {
1310  $error++;
1311  }
1312  // End call triggers
1313  }
1314  }
1315 
1316  // Commit or rollback
1317  if ($error) {
1318  foreach ($this->errors as $errmsg) {
1319  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1320  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1321  }
1322  $this->db->rollback();
1323  return -1 * $error;
1324  } else {
1325  $this->db->commit();
1326  return 1;
1327  }
1328  }
1329 
1330  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1337  public function insert_discount($idremise)
1338  {
1339  // phpcs:enable
1340  global $conf, $langs;
1341 
1342  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1343  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1344 
1345  $this->db->begin();
1346 
1347  $remise = new DiscountAbsolute($this->db);
1348  $result = $remise->fetch($idremise);
1349 
1350  if ($result > 0) {
1351  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1352  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1353  $this->db->rollback();
1354  return -5;
1355  }
1356 
1357  $facligne = new SupplierInvoiceLine($this->db);
1358  $facligne->fk_facture_fourn = $this->id;
1359  $facligne->fk_remise_except = $remise->id;
1360  $facligne->desc = $remise->description; // Description ligne
1361  $facligne->vat_src_code = $remise->vat_src_code;
1362  $facligne->tva_tx = $remise->tva_tx;
1363  $facligne->subprice = -$remise->amount_ht;
1364  $facligne->fk_product = 0; // Id produit predefini
1365  $facligne->product_type = 0;
1366  $facligne->qty = 1;
1367  $facligne->remise_percent = 0;
1368  $facligne->rang = -1;
1369  $facligne->info_bits = 2;
1370 
1371  if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
1372  $facligne->rang = 1;
1373  $linecount = count($this->lines);
1374  for ($ii = 1; $ii <= $linecount; $ii++) {
1375  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii+1);
1376  }
1377  }
1378 
1379  // Get buy/cost price of invoice that is source of discount
1380  if ($remise->fk_invoice_supplier_source > 0) {
1381  $srcinvoice = new FactureFournisseur($this->db);
1382  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1383  $totalcostpriceofinvoice = 0;
1384  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1385  $formmargin = new FormMargin($this->db);
1386  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1387  $facligne->pa_ht = $arraytmp['pa_total'];
1388  }
1389 
1390  $facligne->total_ht = -$remise->amount_ht;
1391  $facligne->total_tva = -$remise->amount_tva;
1392  $facligne->total_ttc = -$remise->amount_ttc;
1393 
1394  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1395  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1396  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1397  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1398 
1399  $lineid = $facligne->insert();
1400  if ($lineid > 0) {
1401  $result = $this->update_price(1);
1402  if ($result > 0) {
1403  // Create link between discount and invoice line
1404  $result = $remise->link_to_invoice($lineid, 0);
1405  if ($result < 0) {
1406  $this->error = $remise->error;
1407  $this->db->rollback();
1408  return -4;
1409  }
1410 
1411  $this->db->commit();
1412  return 1;
1413  } else {
1414  $this->error = $facligne->error;
1415  $this->db->rollback();
1416  return -1;
1417  }
1418  } else {
1419  $this->error = $facligne->error;
1420  $this->db->rollback();
1421  return -2;
1422  }
1423  } else {
1424  $this->db->rollback();
1425  return -3;
1426  }
1427  }
1428 
1429 
1437  public function delete(User $user, $notrigger = 0)
1438  {
1439  global $langs, $conf;
1440 
1441  $rowid = $this->id;
1442 
1443  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1444 
1445  // TODO Test if there is at least on payment. If yes, refuse to delete.
1446 
1447  $error = 0;
1448  $this->db->begin();
1449 
1450  if (!$error && !$notrigger) {
1451  // Call trigger
1452  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1453  if ($result < 0) {
1454  $this->db->rollback();
1455  return -1;
1456  }
1457  // Fin appel triggers
1458  }
1459 
1460  if (!$error) {
1461  // If invoice was converted into a discount not yet consumed, we remove discount
1462  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1463  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1464  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1465  $resql = $this->db->query($sql);
1466 
1467  // If invoice has consumned discounts
1468  $this->fetch_lines();
1469  $list_rowid_det = array();
1470  foreach ($this->lines as $key => $invoiceline) {
1471  $list_rowid_det[] = $invoiceline->rowid;
1472  }
1473 
1474  // Consumned discounts are freed
1475  if (count($list_rowid_det)) {
1476  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1477  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1478  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(join(',', $list_rowid_det)).')';
1479 
1480  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1481  if (!$this->db->query($sql)) {
1482  $error++;
1483  }
1484  }
1485  }
1486 
1487  if (!$error) {
1488  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1489  $ef = $main."_extrafields";
1490  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1491  $resqlef = $this->db->query($sqlef);
1492  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1493  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1494  $resql = $this->db->query($sql);
1495  if ($resqlef && $resql) {
1496  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1497  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1498  $resql2 = $this->db->query($sql);
1499  if (!$resql2) {
1500  $error++;
1501  }
1502  } else {
1503  $error++;
1504  }
1505  }
1506 
1507  if (!$error) {
1508  // Delete linked object
1509  $res = $this->deleteObjectLinked();
1510  if ($res < 0) {
1511  $error++;
1512  }
1513  }
1514 
1515  if (!$error) {
1516  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1517  $this->deleteEcmFiles();
1518 
1519  // We remove directory
1520  if ($conf->fournisseur->facture->dir_output) {
1521  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1522 
1523  $ref = dol_sanitizeFileName($this->ref);
1524  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1525  $file = $dir."/".$ref.".pdf";
1526  if (file_exists($file)) {
1527  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1528  $this->error = 'ErrorFailToDeleteFile';
1529  $error++;
1530  }
1531  }
1532  if (file_exists($dir)) {
1533  $res = @dol_delete_dir_recursive($dir);
1534 
1535  if (!$res) {
1536  $this->error = 'ErrorFailToDeleteDir';
1537  $error++;
1538  }
1539  }
1540  }
1541  }
1542 
1543  // Remove extrafields
1544  if (!$error) {
1545  $result = $this->deleteExtraFields();
1546  if ($result < 0) {
1547  $error++;
1548  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1549  }
1550  }
1551 
1552  if (!$error) {
1553  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1554  $this->db->commit();
1555  return 1;
1556  } else {
1557  $this->error = $this->db->lasterror();
1558  $this->db->rollback();
1559  return -$error;
1560  }
1561  }
1562 
1563 
1564  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1575  public function set_paid($user, $close_code = '', $close_note = '')
1576  {
1577  // phpcs:enable
1578  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1579  return $this->setPaid($user, $close_code, $close_note);
1580  }
1581 
1590  public function setPaid($user, $close_code = '', $close_note = '')
1591  {
1592  $error = 0;
1593 
1594  if ($this->paye != 1) {
1595  $this->db->begin();
1596 
1597  $now = dol_now();
1598 
1599  dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1600 
1601  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1602  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1603  if (!$close_code) {
1604  $sql .= ', paye=1';
1605  }
1606  if ($close_code) {
1607  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1608  }
1609  if ($close_note) {
1610  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1611  }
1612  $sql .= ', fk_user_closing = '.((int) $user->id);
1613  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1614  $sql .= ' WHERE rowid = '.((int) $this->id);
1615 
1616  $resql = $this->db->query($sql);
1617  if ($resql) {
1618  // Call trigger
1619  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1620  if ($result < 0) {
1621  $error++;
1622  }
1623  // End call triggers
1624  } else {
1625  $error++;
1626  $this->error = $this->db->error();
1627  dol_print_error($this->db);
1628  }
1629 
1630  if (!$error) {
1631  $this->db->commit();
1632  return 1;
1633  } else {
1634  $this->db->rollback();
1635  return -1;
1636  }
1637  } else {
1638  return 0;
1639  }
1640  }
1641 
1642  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1653  public function set_unpaid($user)
1654  {
1655  // phpcs:enable
1656  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1657  return $this->setUnpaid($user);
1658  }
1659 
1668  public function setUnpaid($user)
1669  {
1670  $error = 0;
1671 
1672  $this->db->begin();
1673 
1674  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1675  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1676  $sql .= ' date_closing=null,';
1677  $sql .= ' fk_user_closing=null';
1678  $sql .= ' WHERE rowid = '.((int) $this->id);
1679 
1680  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1681  $resql = $this->db->query($sql);
1682  if ($resql) {
1683  // Call trigger
1684  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1685  if ($result < 0) {
1686  $error++;
1687  }
1688  // End call triggers
1689  } else {
1690  $error++;
1691  $this->error = $this->db->error();
1692  dol_print_error($this->db);
1693  }
1694 
1695  if (!$error) {
1696  $this->db->commit();
1697  return 1;
1698  } else {
1699  $this->db->rollback();
1700  return -1;
1701  }
1702  }
1703 
1714  public function setCanceled($user, $close_code = '', $close_note = '')
1715  {
1716  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1717 
1718  $this->db->begin();
1719 
1720  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1721  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1722  if ($close_code) {
1723  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1724  }
1725  if ($close_note) {
1726  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1727  }
1728  $sql .= " WHERE rowid = ".((int) $this->id);
1729 
1730  $resql = $this->db->query($sql);
1731  if ($resql) {
1732  // Bound discounts are deducted from the invoice
1733  // as they have not been used since the invoice is abandoned.
1734  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1735  $sql .= ' SET fk_invoice_supplier = NULL';
1736  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1737 
1738  $resql = $this->db->query($sql);
1739  if ($resql) {
1740  // Call trigger
1741  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1742  if ($result < 0) {
1743  $this->db->rollback();
1744  return -1;
1745  }
1746  // End call triggers
1747 
1748  $this->db->commit();
1749  return 1;
1750  } else {
1751  $this->error = $this->db->error()." sql=".$sql;
1752  $this->db->rollback();
1753  return -1;
1754  }
1755  } else {
1756  $this->error = $this->db->error()." sql=".$sql;
1757  $this->db->rollback();
1758  return -2;
1759  }
1760  }
1761 
1771  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1772  {
1773  global $conf, $langs;
1774 
1775  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1776 
1777  $now = dol_now();
1778 
1779  $error = 0;
1780  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1781 
1782  // Force to have object complete for checks
1783  $this->fetch_thirdparty();
1784  $this->fetch_lines();
1785 
1786  // Check parameters
1787  if ($this->statut > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1788  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1789  return 0;
1790  }
1791  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1792  $langs->load("errors");
1793  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1794  return -1;
1795  }
1796  if (count($this->lines) <= 0) {
1797  $langs->load("errors");
1798  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1799  return -1;
1800  }
1801 
1802  $this->db->begin();
1803 
1804  // Define new ref
1805  if ($force_number) {
1806  $num = $force_number;
1807  } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1808  $num = $this->getNextNumRef($this->thirdparty);
1809  } else {
1810  $num = $this->ref;
1811  }
1812  $this->newref = dol_sanitizeFileName($num);
1813 
1814  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1815  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1816  $sql .= " WHERE rowid = ".((int) $this->id);
1817 
1818  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1819  $resql = $this->db->query($sql);
1820  if ($resql) {
1821  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1822  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1823  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1824  $langs->load("agenda");
1825 
1826  $cpt = count($this->lines);
1827  for ($i = 0; $i < $cpt; $i++) {
1828  if ($this->lines[$i]->fk_product > 0) {
1829  $mouvP = new MouvementStock($this->db);
1830  $mouvP->origin = &$this;
1831  $mouvP->setOrigin($this->element, $this->id);
1832  // We increase stock for product
1833  $up_ht_disc = $this->lines[$i]->subprice;
1834  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1835  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1836  }
1838  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1839  } else {
1840  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1841  }
1842  if ($result < 0) {
1843  $error++;
1844  }
1845  }
1846  }
1847  }
1848 
1849  // Triggers call
1850  if (!$error && empty($notrigger)) {
1851  // Call trigger
1852  $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1853  if ($result < 0) {
1854  $error++;
1855  }
1856  // End call triggers
1857  }
1858 
1859  if (!$error) {
1860  $this->oldref = $this->ref;
1861 
1862  // Rename directory if dir was a temporary ref
1863  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1864  // Now we rename also files into index
1865  $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)."'";
1866  $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;
1867  $resql = $this->db->query($sql);
1868  if (!$resql) {
1869  $error++; $this->error = $this->db->lasterror();
1870  }
1871 
1872  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1873  $oldref = dol_sanitizeFileName($this->ref);
1874  $newref = dol_sanitizeFileName($num);
1875  $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1876  $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref;
1877  if (!$error && file_exists($dirsource)) {
1878  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1879 
1880  if (@rename($dirsource, $dirdest)) {
1881  dol_syslog("Rename ok");
1882  // Rename docs starting with $oldref with $newref
1883  $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1884  foreach ($listoffiles as $fileentry) {
1885  $dirsource = $fileentry['name'];
1886  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1887  $dirsource = $fileentry['path'].'/'.$dirsource;
1888  $dirdest = $fileentry['path'].'/'.$dirdest;
1889  @rename($dirsource, $dirdest);
1890  }
1891  }
1892  }
1893  }
1894  }
1895 
1896  // Set new ref and define current statut
1897  if (!$error) {
1898  $this->ref = $num;
1899  $this->statut = self::STATUS_VALIDATED;
1900  //$this->date_validation=$now; this is stored into log table
1901  }
1902 
1903  if (!$error) {
1904  $this->db->commit();
1905  return 1;
1906  } else {
1907  $this->db->rollback();
1908  return -1;
1909  }
1910  } else {
1911  $this->error = $this->db->error();
1912  $this->db->rollback();
1913  return -1;
1914  }
1915  }
1916 
1924  public function setDraft($user, $idwarehouse = -1)
1925  {
1926  // phpcs:enable
1927  global $conf, $langs;
1928 
1929  $error = 0;
1930 
1931  if ($this->statut == self::STATUS_DRAFT) {
1932  dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1933  return 0;
1934  }
1935 
1936  dol_syslog(__METHOD__, LOG_DEBUG);
1937 
1938  $this->db->begin();
1939 
1940  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1941  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1942  $sql .= " WHERE rowid = ".((int) $this->id);
1943 
1944  $result = $this->db->query($sql);
1945  if ($result) {
1946  if (!$error) {
1947  $this->oldcopy = clone $this;
1948  }
1949 
1950  // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
1951  if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1952  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1953  $langs->load("agenda");
1954 
1955  $cpt = count($this->lines);
1956  for ($i = 0; $i < $cpt; $i++) {
1957  if ($this->lines[$i]->fk_product > 0) {
1958  $mouvP = new MouvementStock($this->db);
1959  $mouvP->origin = &$this;
1960  $mouvP->setOrigin($this->element, $this->id);
1961  // We increase stock for product
1963  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1964  } else {
1965  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1966  }
1967  }
1968  }
1969  }
1970  // Triggers call
1971  if (!$error && empty($notrigger)) {
1972  // Call trigger
1973  $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
1974  if ($result < 0) {
1975  $error++;
1976  }
1977  // End call triggers
1978  }
1979  if ($error == 0) {
1980  $this->db->commit();
1981  return 1;
1982  } else {
1983  $this->db->rollback();
1984  return -1;
1985  }
1986  } else {
1987  $this->error = $this->db->error();
1988  $this->db->rollback();
1989  return -1;
1990  }
1991  }
1992 
1993 
2027  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)
2028  {
2029  global $langs, $mysoc, $conf;
2030 
2031  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);
2032  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2033 
2034  if ($this->statut == self::STATUS_DRAFT) {
2035  // Clean parameters
2036  if (empty($remise_percent)) {
2037  $remise_percent = 0;
2038  }
2039  if (empty($qty)) {
2040  $qty = 0;
2041  }
2042  if (empty($info_bits)) {
2043  $info_bits = 0;
2044  }
2045  if (empty($rang)) {
2046  $rang = 0;
2047  }
2048  if (empty($ventil)) {
2049  $ventil = 0;
2050  }
2051  if (empty($txtva)) {
2052  $txtva = 0;
2053  }
2054  if (empty($txlocaltax1)) {
2055  $txlocaltax1 = 0;
2056  }
2057  if (empty($txlocaltax2)) {
2058  $txlocaltax2 = 0;
2059  }
2060 
2061  $remise_percent = price2num($remise_percent);
2062  $qty = price2num($qty);
2063  $pu = price2num($pu);
2064  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2065  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2066  }
2067  $txlocaltax1 = price2num($txlocaltax1);
2068  $txlocaltax2 = price2num($txlocaltax2);
2069 
2070  if ($date_start && $date_end && $date_start > $date_end) {
2071  $langs->load("errors");
2072  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2073  return -1;
2074  }
2075 
2076  $this->db->begin();
2077 
2078  if ($fk_product > 0) {
2079  if (!empty($conf->global->SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY)) {
2080  // Check quantity is enough
2081  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2082  $prod = new ProductFournisseur($this->db);
2083  if ($prod->fetch($fk_product) > 0) {
2084  $product_type = $prod->type;
2085  $label = $prod->label;
2086  $fk_prod_fourn_price = 0;
2087 
2088  // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2089  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2090  $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
2091  if ($result > 0) {
2092  if (empty($pu)) {
2093  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2094  }
2095  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2096  // is remise percent not keyed but present for the product we add it
2097  if ($remise_percent == 0 && $prod->remise_percent != 0) {
2098  $remise_percent = $prod->remise_percent;
2099  }
2100  }
2101  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2102  $langs->load("errors");
2103  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2104  $this->db->rollback();
2105  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2106  //$pu = $prod->fourn_pu; // We do not overwrite unit price
2107  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2108  return -1;
2109  }
2110  if ($result == -1) {
2111  $langs->load("errors");
2112  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2113  $this->db->rollback();
2114  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2115  return -1;
2116  }
2117  if ($result < -1) {
2118  $this->error = $prod->error;
2119  $this->db->rollback();
2120  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2121  return -1;
2122  }
2123  } else {
2124  $this->error = $prod->error;
2125  $this->db->rollback();
2126  return -1;
2127  }
2128  }
2129  } else {
2130  $product_type = $type;
2131  }
2132 
2133  if (isModEnabled("multicurrency") && $pu_devise > 0) {
2134  $pu = 0;
2135  }
2136 
2137  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2138 
2139  // Clean vat code
2140  $reg = array();
2141  $vat_src_code = '';
2142  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2143  $vat_src_code = $reg[1];
2144  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2145  }
2146 
2147  // Calcul du total TTC et de la TVA pour la ligne a partir de
2148  // qty, pu, remise_percent et txtva
2149  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2150  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2151 
2152  $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);
2153  $total_ht = $tabprice[0];
2154  $total_tva = $tabprice[1];
2155  $total_ttc = $tabprice[2];
2156  $total_localtax1 = $tabprice[9];
2157  $total_localtax2 = $tabprice[10];
2158  $pu_ht = $tabprice[3];
2159 
2160  // MultiCurrency
2161  $multicurrency_total_ht = $tabprice[16];
2162  $multicurrency_total_tva = $tabprice[17];
2163  $multicurrency_total_ttc = $tabprice[18];
2164  $pu_ht_devise = $tabprice[19];
2165 
2166  // Check parameters
2167  if ($type < 0) {
2168  return -1;
2169  }
2170 
2171  if ($rang < 0) {
2172  $rangmax = $this->line_max();
2173  $rang = $rangmax + 1;
2174  }
2175 
2176  // Insert line
2177  $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2178 
2179  $supplierinvoiceline->context = $this->context;
2180 
2181  $supplierinvoiceline->fk_facture_fourn = $this->id;
2182  //$supplierinvoiceline->label=$label; // deprecated
2183  $supplierinvoiceline->desc = $desc;
2184  $supplierinvoiceline->ref_supplier = $ref_supplier;
2185 
2186  $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2187  $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2188 
2189  $supplierinvoiceline->vat_src_code = $vat_src_code;
2190  $supplierinvoiceline->tva_tx = $txtva;
2191  $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2192  $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2193  $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2194  $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2195 
2196  $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
2197  $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
2198  $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
2199  $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
2200  $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
2201 
2202  $supplierinvoiceline->fk_product = $fk_product;
2203  $supplierinvoiceline->product_type = $type;
2204  $supplierinvoiceline->remise_percent = $remise_percent;
2205  $supplierinvoiceline->date_start = $date_start;
2206  $supplierinvoiceline->date_end = $date_end;
2207  $supplierinvoiceline->fk_code_ventilation = $ventil;
2208  $supplierinvoiceline->rang = $rang;
2209  $supplierinvoiceline->info_bits = $info_bits;
2210  $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2211 
2212  $supplierinvoiceline->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
2213  $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2214  $supplierinvoiceline->origin = $this->origin;
2215  $supplierinvoiceline->origin_id = $origin_id;
2216  $supplierinvoiceline->fk_unit = $fk_unit;
2217 
2218  // Multicurrency
2219  $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2220  $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2221  $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
2222 
2223  $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
2224  $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
2225  $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
2226 
2227  if (is_array($array_options) && count($array_options) > 0) {
2228  $supplierinvoiceline->array_options = $array_options;
2229  }
2230 
2231  $result = $supplierinvoiceline->insert($notrigger);
2232  if ($result > 0) {
2233  // Reorder if child line
2234  if (!empty($fk_parent_line)) {
2235  $this->line_order(true, 'DESC');
2236  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2237  $linecount = count($this->lines);
2238  for ($ii = $rang; $ii <= $linecount; $ii++) {
2239  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2240  }
2241  }
2242 
2243  // Mise a jour informations denormalisees au niveau de la facture meme
2244  $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.
2245  if ($result > 0) {
2246  $this->db->commit();
2247  return $supplierinvoiceline->id;
2248  } else {
2249  $this->error = $this->db->error();
2250  $this->db->rollback();
2251  return -1;
2252  }
2253  } else {
2254  $this->error = $supplierinvoiceline->error;
2255  $this->errors = $supplierinvoiceline->errors;
2256  $this->db->rollback();
2257  return -2;
2258  }
2259  } else {
2260  return 0;
2261  }
2262  }
2263 
2289  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)
2290  {
2291  global $mysoc, $langs;
2292 
2293  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);
2294  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2295 
2296  $pu = price2num($pu);
2297  $qty = price2num($qty);
2298  $remise_percent = price2num($remise_percent);
2299  $pu_devise = price2num($pu_devise);
2300 
2301  // Check parameters
2302  //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2303  if ($type < 0) {
2304  return -1;
2305  }
2306 
2307  if ($date_start && $date_end && $date_start > $date_end) {
2308  $langs->load("errors");
2309  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2310  return -1;
2311  }
2312 
2313  // Clean parameters
2314  if (empty($vatrate)) {
2315  $vatrate = 0;
2316  }
2317  if (empty($txlocaltax1)) {
2318  $txlocaltax1 = 0;
2319  }
2320  if (empty($txlocaltax2)) {
2321  $txlocaltax2 = 0;
2322  }
2323 
2324  $txlocaltax1 = price2num($txlocaltax1);
2325  $txlocaltax2 = price2num($txlocaltax2);
2326 
2327  // Calcul du total TTC et de la TVA pour la ligne a partir de
2328  // qty, pu, remise_percent et txtva
2329  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2330  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2331 
2332  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2333 
2334  $reg = array();
2335 
2336  // Clean vat code
2337  $vat_src_code = '';
2338  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2339  $vat_src_code = $reg[1];
2340  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2341  }
2342 
2343  $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);
2344  $total_ht = $tabprice[0];
2345  $total_tva = $tabprice[1];
2346  $total_ttc = $tabprice[2];
2347  $pu_ht = $tabprice[3];
2348  $pu_tva = $tabprice[4];
2349  $pu_ttc = $tabprice[5];
2350  $total_localtax1 = $tabprice[9];
2351  $total_localtax2 = $tabprice[10];
2352 
2353  // MultiCurrency
2354  $multicurrency_total_ht = $tabprice[16];
2355  $multicurrency_total_tva = $tabprice[17];
2356  $multicurrency_total_ttc = $tabprice[18];
2357  $pu_ht_devise = $tabprice[19];
2358 
2359  if (empty($info_bits)) {
2360  $info_bits = 0;
2361  }
2362 
2363  //Fetch current line from the database and then clone the object and set it in $oldline property
2364  $line = new SupplierInvoiceLine($this->db);
2365  $line->fetch($id);
2366  $line->fetch_optionals();
2367 
2368  $staticline = clone $line;
2369 
2370  if ($idproduct) {
2371  $product = new Product($this->db);
2372  $result = $product->fetch($idproduct);
2373  $product_type = $product->type;
2374  } else {
2375  $idproduct = $staticline->fk_product;
2376  $product_type = $type;
2377  }
2378 
2379  $line->oldline = $staticline;
2380  $line->context = $this->context;
2381 
2382  $line->description = $desc;
2383 
2384  $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2385  $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2386  $line->pu_ht = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2387  $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2388 
2389  $line->remise_percent = $remise_percent;
2390  $line->ref_supplier = $ref_supplier;
2391 
2392  $line->date_start = $date_start;
2393  $line->date_end = $date_end;
2394 
2395  $line->vat_src_code = $vat_src_code;
2396  $line->tva_tx = $vatrate;
2397  $line->localtax1_tx = $txlocaltax1;
2398  $line->localtax2_tx = $txlocaltax2;
2399  $line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2400  $line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2401 
2402  $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht);
2403  $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
2404  $line->total_localtax1 = $total_localtax1;
2405  $line->total_localtax2 = $total_localtax2;
2406  $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
2407 
2408  $line->fk_product = $idproduct;
2409  $line->product_type = $product_type;
2410  $line->info_bits = $info_bits;
2411  $line->fk_unit = $fk_unit;
2412  $line->rang = $rang;
2413 
2414  if (is_array($array_options) && count($array_options) > 0) {
2415  // We replace values in this->line->array_options only for entries defined into $array_options
2416  foreach ($array_options as $key => $value) {
2417  $line->array_options[$key] = $array_options[$key];
2418  }
2419  }
2420 
2421  // Multicurrency
2422  $line->multicurrency_subprice = $pu_ht_devise;
2423  $line->multicurrency_total_ht = $multicurrency_total_ht;
2424  $line->multicurrency_total_tva = $multicurrency_total_tva;
2425  $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2426 
2427  $res = $line->update($notrigger);
2428 
2429  if ($res < 1) {
2430  $this->errors[] = $line->error;
2431  } else {
2432  // Update total price into invoice record
2433  $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2434  }
2435 
2436  return $res;
2437  }
2438 
2446  public function deleteline($rowid, $notrigger = 0)
2447  {
2448  if (!$rowid) {
2449  $rowid = $this->id;
2450  }
2451 
2452  $this->db->begin();
2453 
2454  // Free the discount linked to a line of invoice
2455  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2456  $sql .= ' SET fk_invoice_supplier_line = NULL';
2457  $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2458 
2459  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2460  $result = $this->db->query($sql);
2461  if (!$result) {
2462  $this->error = $this->db->error();
2463  $this->db->rollback();
2464  return -2;
2465  }
2466 
2467  $line = new SupplierInvoiceLine($this->db);
2468 
2469  if ($line->fetch($rowid) < 1) {
2470  return -1;
2471  }
2472 
2473  $res = $line->delete($notrigger);
2474 
2475  if ($res < 1) {
2476  $this->errors[] = $line->error;
2477  $this->db->rollback();
2478  return -3;
2479  } else {
2480  $res = $this->update_price(1);
2481 
2482  if ($res > 0) {
2483  $this->db->commit();
2484  return 1;
2485  } else {
2486  $this->db->rollback();
2487  $this->error = $this->db->lasterror();
2488  return -4;
2489  }
2490  }
2491  }
2492 
2493 
2500  public function info($id)
2501  {
2502  $sql = 'SELECT c.rowid, datec, tms as datem, ';
2503  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2504  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2505  $sql .= ' WHERE c.rowid = '.((int) $id);
2506 
2507  $result = $this->db->query($sql);
2508  if ($result) {
2509  if ($this->db->num_rows($result)) {
2510  $obj = $this->db->fetch_object($result);
2511 
2512  $this->id = $obj->rowid;
2513  if ($obj->fk_user_author) {
2514  $cuser = new User($this->db);
2515  $cuser->fetch($obj->fk_user_author);
2516  $this->user_creation = $cuser;
2517  }
2518  if ($obj->fk_user_valid) {
2519  $vuser = new User($this->db);
2520  $vuser->fetch($obj->fk_user_valid);
2521  $this->user_validation = $vuser;
2522  }
2523  if ($obj->fk_user_modif) {
2524  $muser = new User($this->db);
2525  $muser->fetch($obj->fk_user_modif);
2526  $this->user_modification = $muser;
2527  }
2528  $this->date_creation = $this->db->jdate($obj->datec);
2529  $this->date_modification = $this->db->jdate($obj->datem);
2530  //$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)
2531  }
2532  $this->db->free($result);
2533  } else {
2534  dol_print_error($this->db);
2535  }
2536  }
2537 
2538  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2547  public function list_replacable_supplier_invoices($socid = 0)
2548  {
2549  // phpcs:enable
2550  global $conf;
2551 
2552  $return = array();
2553 
2554  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2555  $sql .= " ff.rowid as rowidnext";
2556  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2557  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2558  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2559  $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2560  $sql .= " AND f.entity = ".$conf->entity;
2561  $sql .= " AND f.paye = 0"; // Pas classee payee completement
2562  $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2563  $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
2564  if ($socid > 0) {
2565  $sql .= " AND f.fk_soc = ".((int) $socid);
2566  }
2567  $sql .= " ORDER BY f.ref";
2568 
2569  dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2570  $resql = $this->db->query($sql);
2571  if ($resql) {
2572  while ($obj = $this->db->fetch_object($resql)) {
2573  $return[$obj->rowid] = array(
2574  'id' => $obj->rowid,
2575  'ref' => $obj->ref,
2576  'status' => $obj->fk_statut
2577  );
2578  }
2579  //print_r($return);
2580  return $return;
2581  } else {
2582  $this->error = $this->db->error();
2583  return -1;
2584  }
2585  }
2586 
2587  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2597  public function list_qualified_avoir_supplier_invoices($socid = 0)
2598  {
2599  // phpcs:enable
2600  global $conf;
2601 
2602  $return = array();
2603 
2604  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.paye, pf.fk_paiementfourn";
2605  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2606  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2607  $sql .= " WHERE f.entity = ".$conf->entity;
2608  $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2609  $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2610  $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2611  $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2612  if ($socid > 0) {
2613  $sql .= " AND f.fk_soc = ".((int) $socid);
2614  }
2615  $sql .= " ORDER BY f.ref";
2616 
2617  dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2618  $resql = $this->db->query($sql);
2619  if ($resql) {
2620  while ($obj = $this->db->fetch_object($resql)) {
2621  $qualified = 0;
2622  if ($obj->fk_statut == self::STATUS_VALIDATED) {
2623  $qualified = 1;
2624  }
2625  if ($obj->fk_statut == self::STATUS_CLOSED) {
2626  $qualified = 1;
2627  }
2628  if ($qualified) {
2629  $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2630  $return[$obj->rowid] = array('ref'=>$obj->ref, 'status'=>$obj->fk_statut, 'type'=>$obj->type, 'paye'=>$obj->paye, 'paymentornot'=>$paymentornot);
2631  }
2632  }
2633 
2634  return $return;
2635  } else {
2636  $this->error = $this->db->error();
2637  return -1;
2638  }
2639  }
2640 
2641  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2648  public function load_board($user)
2649  {
2650  // phpcs:enable
2651  global $conf, $langs;
2652 
2653  $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2654  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2655  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
2656  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2657  }
2658  $sql .= ' WHERE ff.paye = 0';
2659  $sql .= ' AND ff.fk_statut > 0';
2660  $sql .= " AND ff.entity = ".$conf->entity;
2661  if ($user->socid) {
2662  $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2663  }
2664 
2665  $resql = $this->db->query($sql);
2666  if ($resql) {
2667  $langs->load("bills");
2668  $now = dol_now();
2669 
2670  $response = new WorkboardResponse();
2671  $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2672  $response->label = $langs->trans("SupplierBillsToPay");
2673  $response->labelShort = $langs->trans("StatusToPay");
2674 
2675  $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2676  $response->img = img_object($langs->trans("Bills"), "bill");
2677 
2678  $facturestatic = new FactureFournisseur($this->db);
2679 
2680  while ($obj = $this->db->fetch_object($resql)) {
2681  $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2682  $facturestatic->statut = $obj->status; // For backward compatibility
2683  $facturestatic->status = $obj->status;
2684 
2685  $response->nbtodo++;
2686  $response->total += $obj->total_ht;
2687 
2688  if ($facturestatic->hasDelay()) {
2689  $response->nbtodolate++;
2690  $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2691  }
2692  }
2693 
2694  $this->db->free($resql);
2695  return $response;
2696  } else {
2697  dol_print_error($this->db);
2698  $this->error = $this->db->error();
2699  return -1;
2700  }
2701  }
2702 
2710  public function getTooltipContentArray($params)
2711  {
2712  global $conf, $langs;
2713 
2714  $langs->load('bills');
2715 
2716  $datas = [];
2717  $moretitle = $params['moretitle'] ?? '';
2718  $picto = $this->picto;
2719  if ($this->type == self::TYPE_REPLACEMENT) {
2720  $picto .= 'r'; // Replacement invoice
2721  }
2722  if ($this->type == self::TYPE_CREDIT_NOTE) {
2723  $picto .= 'a'; // Credit note
2724  }
2725  if ($this->type == self::TYPE_DEPOSIT) {
2726  $picto .= 'd'; // Deposit invoice
2727  }
2728 
2729  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2730  if ($this->type == self::TYPE_REPLACEMENT) {
2731  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2732  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2733  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2734  } elseif ($this->type == self::TYPE_DEPOSIT) {
2735  $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2736  }
2737  if (isset($this->status)) {
2738  $alreadypaid = -1;
2739  if (isset($this->alreadypaid)) {
2740  $alreadypaid = $this->alreadypaid;
2741  }
2742 
2743  $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2744  }
2745  if ($moretitle) {
2746  $datas['picto'] .= ' - '.$moretitle;
2747  }
2748  if (!empty($this->ref)) {
2749  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2750  }
2751  if (!empty($this->ref_supplier)) {
2752  $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2753  }
2754  if (!empty($this->label)) {
2755  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2756  }
2757  if (!empty($this->date)) {
2758  $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2759  }
2760  if (!empty($this->date_echeance)) {
2761  $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2762  }
2763  if (!empty($this->total_ht)) {
2764  $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2765  }
2766  if (!empty($this->total_tva)) {
2767  $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2768  }
2769  if (!empty($this->total_ttc)) {
2770  $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2771  }
2772  return $datas;
2773  }
2774 
2788  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2789  {
2790  global $langs, $conf, $user, $hookmanager;
2791 
2792  $result = '';
2793 
2794  if ($option == 'withdraw') {
2795  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2796  } elseif ($option == 'document') {
2797  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2798  } else {
2799  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2800  }
2801 
2802  if ($short) {
2803  return $url;
2804  }
2805 
2806  if ($option !== 'nolink') {
2807  // Add param to save lastsearch_values or not
2808  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2809  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2810  $add_save_lastsearch_values = 1;
2811  }
2812  if ($add_save_lastsearch_values) {
2813  $url .= '&save_lastsearch_values=1';
2814  }
2815  }
2816 
2817  $picto = $this->picto;
2818  if ($this->type == self::TYPE_REPLACEMENT) {
2819  $picto .= 'r'; // Replacement invoice
2820  }
2821  if ($this->type == self::TYPE_CREDIT_NOTE) {
2822  $picto .= 'a'; // Credit note
2823  }
2824  if ($this->type == self::TYPE_DEPOSIT) {
2825  $picto .= 'd'; // Deposit invoice
2826  }
2827  $params = [
2828  'id' => $this->id,
2829  'objecttype' => $this->element,
2830  'option' => $option,
2831  'moretitle' => $moretitle,
2832  ];
2833  $classfortooltip = 'classfortooltip';
2834  $dataparams = '';
2835  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2836  $classfortooltip = 'classforajaxtooltip';
2837  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2838  $label = '';
2839  } else {
2840  $label = implode($this->getTooltipContentArray($params));
2841  }
2842 
2843  $ref = $this->ref;
2844  if (empty($ref)) {
2845  $ref = $this->id;
2846  }
2847 
2848  $linkclose = '';
2849  if (empty($notooltip)) {
2850  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2851  $label = $langs->trans("ShowSupplierInvoice");
2852  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2853  }
2854  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2855  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2856  }
2857 
2858  $linkstart = '<a href="'.$url.'"';
2859  $linkstart .= $linkclose.'>';
2860  $linkend = '</a>';
2861 
2862  $result .= $linkstart;
2863  if ($withpicto) {
2864  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1);
2865  }
2866  if ($withpicto != 2) {
2867  $result .= ($max ?dol_trunc($ref, $max) : $ref);
2868  }
2869  $result .= $linkend;
2870 
2871  if ($addlinktonotes) {
2872  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2873  if ($txttoshow) {
2874  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2875  $result .= ' <span class="note inline-block">';
2876  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2877  $result .= img_picto('', 'note');
2878  $result .= '</a>';
2879  $result .= '</span>';
2880  }
2881  }
2882  global $action;
2883  $hookmanager->initHooks(array($this->element . 'dao'));
2884  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2885  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2886  if ($reshook > 0) {
2887  $result = $hookmanager->resPrint;
2888  } else {
2889  $result .= $hookmanager->resPrint;
2890  }
2891  return $result;
2892  }
2893 
2902  public function getNextNumRef($soc, $mode = 'next')
2903  {
2904  global $db, $langs, $conf;
2905  $langs->load("orders");
2906 
2907  // Clean parameters (if not defined or using deprecated value)
2908  if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) {
2909  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2910  }
2911 
2912  $mybool = false;
2913 
2914  $file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2915  $classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2916 
2917  // Include file with class
2918  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2919 
2920  foreach ($dirmodels as $reldir) {
2921  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2922 
2923  // Load file with numbering class (if found)
2924  $mybool |= @include_once $dir.$file;
2925  }
2926 
2927  if ($mybool === false) {
2928  dol_print_error('', "Failed to include file ".$file);
2929  return '';
2930  }
2931 
2932  $obj = new $classname();
2933  $numref = "";
2934  $numref = $obj->getNumRef($soc, $this, $mode);
2935 
2936  if ($numref != "") {
2937  return $numref;
2938  } else {
2939  $this->error = $obj->error;
2940  return -1;
2941  }
2942  }
2943 
2944 
2953  public function initAsSpecimen($option = '')
2954  {
2955  global $langs, $conf;
2956  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2957 
2958  $now = dol_now();
2959 
2960  // Load array of products prodids
2961  $num_prods = 0;
2962  $prodids = array();
2963 
2964  $sql = "SELECT rowid";
2965  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2966  $sql .= " WHERE entity IN (".getEntity('product').")";
2967  $sql .= $this->db->plimit(100);
2968 
2969  $resql = $this->db->query($sql);
2970  if ($resql) {
2971  $num_prods = $this->db->num_rows($resql);
2972  $i = 0;
2973  while ($i < $num_prods) {
2974  $i++;
2975  $row = $this->db->fetch_row($resql);
2976  $prodids[$i] = $row[0];
2977  }
2978  }
2979 
2980  // Initialise parametres
2981  $this->id = 0;
2982  $this->ref = 'SPECIMEN';
2983  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
2984  $this->specimen = 1;
2985  $this->socid = 1;
2986  $this->date = $now;
2987  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2988  $this->cond_reglement_code = 'RECEP';
2989  $this->mode_reglement_code = 'CHQ';
2990 
2991  $this->note_public = 'This is a comment (public)';
2992  $this->note_private = 'This is a comment (private)';
2993 
2994  $this->multicurrency_tx = 1;
2995  $this->multicurrency_code = $conf->currency;
2996 
2997  $xnbp = 0;
2998  if (empty($option) || $option != 'nolines') {
2999  // Lines
3000  $nbp = 5;
3001  while ($xnbp < $nbp) {
3002  $line = new SupplierInvoiceLine($this->db);
3003  $line->desc = $langs->trans("Description")." ".$xnbp;
3004  $line->qty = 1;
3005  $line->subprice = 100;
3006  $line->pu_ht = 100; // the canelle template use pu_ht and not subprice
3007  $line->price = 100;
3008  $line->tva_tx = 19.6;
3009  $line->localtax1_tx = 0;
3010  $line->localtax2_tx = 0;
3011  if ($xnbp == 2) {
3012  $line->total_ht = 50;
3013  $line->total_ttc = 59.8;
3014  $line->total_tva = 9.8;
3015  $line->remise_percent = 50;
3016  } else {
3017  $line->total_ht = 100;
3018  $line->total_ttc = 119.6;
3019  $line->total_tva = 19.6;
3020  $line->remise_percent = 0;
3021  }
3022 
3023  if ($num_prods > 0) {
3024  $prodid = mt_rand(1, $num_prods);
3025  $line->fk_product = $prodids[$prodid];
3026  }
3027  $line->product_type = 0;
3028 
3029  $this->lines[$xnbp] = $line;
3030 
3031  $this->total_ht += $line->total_ht;
3032  $this->total_tva += $line->total_tva;
3033  $this->total_ttc += $line->total_ttc;
3034 
3035  $xnbp++;
3036  }
3037  }
3038 
3039  $this->amount_ht = $xnbp * 100;
3040  $this->total_ht = $xnbp * 100;
3041  $this->total_tva = $xnbp * 19.6;
3042  $this->total_ttc = $xnbp * 119.6;
3043  }
3044 
3045  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3051  public function load_state_board()
3052  {
3053  // phpcs:enable
3054  global $conf, $user;
3055 
3056  $this->nb = array();
3057 
3058  $clause = "WHERE";
3059 
3060  $sql = "SELECT count(f.rowid) as nb";
3061  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3062  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3063  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3064  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3065  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3066  $clause = "AND";
3067  }
3068  $sql .= " ".$clause." f.entity = ".$conf->entity;
3069 
3070  $resql = $this->db->query($sql);
3071  if ($resql) {
3072  while ($obj = $this->db->fetch_object($resql)) {
3073  $this->nb["supplier_invoices"] = $obj->nb;
3074  }
3075  $this->db->free($resql);
3076  return 1;
3077  } else {
3078  dol_print_error($this->db);
3079  $this->error = $this->db->error();
3080  return -1;
3081  }
3082  }
3083 
3092  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3093  {
3094  global $conf, $langs;
3095 
3096  $error = 0;
3097 
3098  $object = new FactureFournisseur($this->db);
3099 
3100  $this->db->begin();
3101 
3102  // Load source object
3103  $object->fetch($fromid);
3104  $object->id = 0;
3105  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3106  $object->status = self::STATUS_DRAFT;
3107 
3108  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3109 
3110  // Clear fields
3111  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3112  $object->author = $user->id;
3113  $object->user_valid = 0;
3114  $object->fk_facture_source = 0;
3115  $object->date_creation = '';
3116  $object->date_validation = '';
3117  $object->date = (empty($this->date) ? dol_now() : $this->date);
3118  $object->ref_client = '';
3119  $object->close_code = '';
3120  $object->close_note = '';
3121  if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3122  $object->note_private = '';
3123  $object->note_public = '';
3124  }
3125 
3126  $object->date_echeance = $object->calculate_date_lim_reglement();
3127 
3128  // Loop on each line of new invoice
3129  foreach ($object->lines as $i => $line) {
3130  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3131  unset($object->lines[$i]);
3132  }
3133  }
3134 
3135  // Create clone
3136  $object->context['createfromclone'] = 'createfromclone';
3137  $result = $object->create($user);
3138 
3139  // Other options
3140  if ($result < 0) {
3141  $this->error = $object->error;
3142  $this->errors = $object->errors;
3143  $error++;
3144  }
3145 
3146  if (!$error) {
3147  }
3148 
3149  unset($object->context['createfromclone']);
3150 
3151  // End
3152  if (!$error) {
3153  $this->db->commit();
3154  return $object->id;
3155  } else {
3156  $this->db->rollback();
3157  return -1;
3158  }
3159  }
3160 
3172  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3173  {
3174  global $conf, $user, $langs;
3175 
3176  $langs->load("suppliers");
3177  $outputlangs->load("products");
3178 
3179  // Set the model on the model name to use
3180  if (empty($modele)) {
3181  if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF)) {
3182  $modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
3183  } else {
3184  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3185  }
3186  }
3187 
3188  if (empty($modele)) {
3189  return 0;
3190  } else {
3191  $modelpath = "core/modules/supplier_invoice/doc/";
3192 
3193  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3194  }
3195  }
3196 
3201  public function getRights()
3202  {
3203  global $user;
3204 
3205  return $user->hasRight("fournisseur", "facture");
3206  }
3207 
3216  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3217  {
3218  $tables = array(
3219  'facture_fourn'
3220  );
3221 
3222  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3223  }
3224 
3233  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3234  {
3235  $tables = array(
3236  'facture_fourn_det'
3237  );
3238 
3239  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3240  }
3241 
3247  public function hasDelay()
3248  {
3249  global $conf;
3250 
3251  $now = dol_now();
3252 
3253  if (!$this->date_echeance) {
3254  return false;
3255  }
3256 
3257  $status = isset($this->status) ? $this->status : $this->statut;
3258 
3259  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3260  }
3261 
3267  public function isCreditNoteUsed()
3268  {
3269  $isUsed = false;
3270 
3271  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3272  $resql = $this->db->query($sql);
3273  if (!empty($resql)) {
3274  $obj = $this->db->fetch_object($resql);
3275  if (!empty($obj->fk_invoice_supplier)) {
3276  $isUsed = true;
3277  }
3278  }
3279 
3280  return $isUsed;
3281  }
3289  public function getKanbanView($option = '', $arraydata = null)
3290  {
3291  global $langs;
3292 
3293  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3294 
3295  $return = '<div class="box-flex-item box-flex-grow-zero">';
3296  $return .= '<div class="info-box info-box-sm">';
3297  $return .= '<span class="info-box-icon bg-infobox-action">';
3298  $return .= img_picto('', $this->picto);
3299  $return .= '</span>';
3300  $return .= '<div class="info-box-content">';
3301  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3302  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3303  if (!empty($arraydata['thirdparty'])) {
3304  $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3305  }
3306  if (property_exists($this, 'date')) {
3307  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3308  }
3309  if (property_exists($this, 'total_ht')) {
3310  $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3311  $return .= ' '.$langs->trans("HT");
3312  $return .= '</span>';
3313  }
3314  if (method_exists($this, 'getLibStatut')) {
3315  $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3316  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3317  }
3318  $return .= '</div>';
3319  $return .= '</div>';
3320  $return .= '</div>';
3321  return $return;
3322  }
3323 
3330  public function setVATReverseCharge($vatreversecharge)
3331  {
3332  if (!$this->table_element) {
3333  dol_syslog(get_class($this)."::setVATReverseCharge was called on objet with property table_element not defined", LOG_ERR);
3334  return -1;
3335  }
3336 
3337  dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3338 
3339  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3340  $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3341  $sql .= " WHERE rowid=".((int) $this->id);
3342 
3343  if ($this->db->query($sql)) {
3344  $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3345  return 1;
3346  } else {
3347  dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3348  $this->error = $this->db->error();
3349  return 0;
3350  }
3351  }
3352 }
3353 
3354 
3355 
3360 {
3364  public $element = 'facture_fourn_det';
3365 
3369  public $table_element = 'facture_fourn_det';
3370 
3371  public $oldline;
3372 
3377  public $ref;
3378 
3383  public $product_ref;
3384 
3390  public $ref_supplier;
3391 
3396  public $product_desc;
3397 
3404  public $pu_ht;
3405 
3410  public $subprice;
3411 
3416  public $pu_ttc;
3417 
3418 
3423  public $fk_facture_fourn;
3424 
3430  public $label;
3431 
3436  public $description;
3437 
3438  public $date_start;
3439  public $date_end;
3440 
3441  public $skip_update_total; // Skip update price total for special lines
3442 
3446  public $situation_percent;
3447 
3451  public $fk_prev_id;
3452 
3457  public $vat_src_code;
3458 
3463  public $tva_tx;
3464 
3469  public $localtax1_tx;
3470 
3475  public $localtax2_tx;
3476 
3481  public $qty;
3482 
3487  public $remise_percent;
3488 
3493  public $pa_ht;
3494 
3499  public $total_ht;
3500 
3505  public $total_ttc;
3506 
3511  public $total_tva;
3512 
3517  public $total_localtax1;
3518 
3523  public $total_localtax2;
3524 
3528  public $fk_product;
3529 
3534  public $product_type;
3535 
3540  public $product_label;
3541 
3548  public $info_bits;
3549 
3554  public $fk_remise_except;
3555 
3559  public $fk_parent_line;
3560 
3561  public $special_code;
3562 
3566  public $rang;
3567 
3572  public $localtax1_type;
3573 
3578  public $localtax2_type;
3579 
3580  // Multicurrency
3584  public $fk_multicurrency;
3585 
3586  public $multicurrency_code;
3587  public $multicurrency_subprice;
3588  public $multicurrency_total_ht;
3589  public $multicurrency_total_tva;
3590  public $multicurrency_total_ttc;
3591 
3592 
3598  public function __construct($db)
3599  {
3600  $this->db = $db;
3601  }
3602 
3609  public function fetch($rowid)
3610  {
3611  $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';
3612  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3613  $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';
3614  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3615  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3616  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3617  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3618  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3619  $sql .= ' ORDER BY f.rang, f.rowid';
3620 
3621  $query = $this->db->query($sql);
3622 
3623  if (!$query) {
3624  $this->errors[] = $this->db->error();
3625  return -1;
3626  }
3627 
3628  if (!$this->db->num_rows($query)) {
3629  return 0;
3630  }
3631 
3632  $obj = $this->db->fetch_object($query);
3633 
3634  $this->id = $obj->rowid;
3635  $this->rowid = $obj->rowid;
3636  $this->fk_facture_fourn = $obj->fk_facture_fourn;
3637  $this->description = $obj->description;
3638  $this->date_start = $obj->date_start;
3639  $this->date_end = $obj->date_end;
3640  $this->product_ref = $obj->product_ref;
3641  $this->ref_supplier = $obj->ref_supplier;
3642  $this->product_desc = $obj->product_desc;
3643 
3644  $this->subprice = $obj->pu_ht;
3645  $this->pu_ht = $obj->pu_ht;
3646  $this->pu_ttc = $obj->pu_ttc;
3647  $this->tva_tx = $obj->tva_tx;
3648  $this->localtax1_tx = $obj->localtax1_tx;
3649  $this->localtax2_tx = $obj->localtax2_tx;
3650  $this->localtax1_type = $obj->localtax1_type;
3651  $this->localtax2_type = $obj->localtax2_type;
3652 
3653  $this->qty = $obj->qty;
3654  $this->remise_percent = $obj->remise_percent;
3655  $this->fk_remise_except = $obj->fk_remise_except;
3656  //$this->tva = $obj->total_tva; // deprecated
3657  $this->total_ht = $obj->total_ht;
3658  $this->total_tva = $obj->total_tva;
3659  $this->total_localtax1 = $obj->total_localtax1;
3660  $this->total_localtax2 = $obj->total_localtax2;
3661  $this->total_ttc = $obj->total_ttc;
3662  $this->fk_product = $obj->fk_product;
3663  $this->product_type = $obj->product_type;
3664  $this->product_label = $obj->product_label;
3665  $this->info_bits = $obj->info_bits;
3666  $this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3667  $this->fk_parent_line = $obj->fk_parent_line;
3668  $this->special_code = $obj->special_code;
3669  $this->rang = $obj->rang;
3670  $this->fk_unit = $obj->fk_unit;
3671 
3672  $this->multicurrency_subprice = $obj->multicurrency_subprice;
3673  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3674  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3675  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3676 
3677  $this->fetch_optionals();
3678 
3679  return 1;
3680  }
3681 
3688  public function delete($notrigger = 0)
3689  {
3690  global $user, $conf;
3691 
3692  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
3693 
3694  $error = 0;
3695 
3696  $this->db->begin();
3697 
3698  if (!$notrigger) {
3699  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3700  $error++;
3701  }
3702  }
3703 
3704  $this->deleteObjectLinked();
3705 
3706  // Remove extrafields
3707  if (!$error) {
3708  $result = $this->deleteExtraFields();
3709  if ($result < 0) {
3710  $error++;
3711  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3712  }
3713  }
3714 
3715  if (!$error) {
3716  // Supprime ligne
3717  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3718  $sql .= " WHERE rowid = ".((int) $this->id);
3719  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3720  $resql = $this->db->query($sql);
3721  if (!$resql) {
3722  $error++;
3723  $this->error = $this->db->lasterror();
3724  }
3725  }
3726 
3727  if (!$error) {
3728  $this->db->commit();
3729  return 1;
3730  } else {
3731  $this->db->rollback();
3732  return -1;
3733  }
3734  }
3735 
3742  public function update($notrigger = 0)
3743  {
3744  global $conf;
3745 
3746  $pu = price2num($this->pu_ht);
3747  $qty = price2num($this->qty);
3748 
3749  // Check parameters
3750  if (empty($this->qty)) {
3751  $this->qty = 0;
3752  }
3753 
3754  if ($this->product_type < 0) {
3755  return -1;
3756  }
3757 
3758  // Clean parameters
3759  if (empty($this->remise_percent)) {
3760  $this->remise_percent = 0;
3761  }
3762  if (empty($this->tva_tx)) {
3763  $this->tva_tx = 0;
3764  }
3765  if (empty($this->localtax1_tx)) {
3766  $this->localtax1_tx = 0;
3767  }
3768  if (empty($this->localtax2_tx)) {
3769  $this->localtax2_tx = 0;
3770  }
3771 
3772  if (empty($this->pa_ht)) {
3773  $this->pa_ht = 0;
3774  }
3775  if (empty($this->multicurrency_subprice)) {
3776  $this->multicurrency_subprice = 0;
3777  }
3778  if (empty($this->multicurrency_total_ht)) {
3779  $this->multicurrency_total_ht = 0;
3780  }
3781  if (empty($this->multicurrency_total_tva)) {
3782  $this->multicurrency_total_tva = 0;
3783  }
3784  if (empty($this->multicurrency_total_ttc)) {
3785  $this->multicurrency_total_ttc = 0;
3786  }
3787 
3788  $fk_product = (int) $this->fk_product;
3789  $fk_unit = (int) $this->fk_unit;
3790 
3791  $this->db->begin();
3792 
3793  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3794  $sql .= " description = '".$this->db->escape($this->description)."'";
3795  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3796  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3797  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3798  $sql .= ", pu_ht = ".price2num($this->pu_ht);
3799  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3800  $sql .= ", qty = ".price2num($this->qty);
3801  $sql .= ", remise_percent = ".price2num($this->remise_percent);
3802  if ($this->fk_remise_except > 0) $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
3803  else $sql .= ", fk_remise_except=null";
3804  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3805  $sql .= ", tva_tx = ".price2num($this->tva_tx);
3806  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3807  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3808  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3809  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3810  $sql .= ", total_ht = ".price2num($this->total_ht);
3811  $sql .= ", tva= ".price2num($this->total_tva);
3812  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3813  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3814  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3815  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
3816  $sql .= ", product_type = ".((int) $this->product_type);
3817  $sql .= ", info_bits = ".((int) $this->info_bits);
3818  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
3819 
3820  if (!empty($this->rang)) {
3821  $sql .= ", rang=".((int) $this->rang);
3822  }
3823 
3824  // Multicurrency
3825  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3826  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3827  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3828  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3829 
3830  $sql .= " WHERE rowid = ".((int) $this->id);
3831 
3832  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3833  $resql = $this->db->query($sql);
3834 
3835  if (!$resql) {
3836  $this->db->rollback();
3837  $this->error = $this->db->lasterror();
3838  return -1;
3839  }
3840 
3841  $this->rowid = $this->id;
3842  $error = 0;
3843 
3844  if (!$error) {
3845  $result = $this->insertExtraFields();
3846  if ($result < 0) {
3847  $error++;
3848  }
3849  }
3850 
3851  if (!$error && !$notrigger) {
3852  global $langs, $user;
3853 
3854  // Call trigger
3855  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
3856  $this->db->rollback();
3857  return -1;
3858  }
3859  // End call triggers
3860  }
3861 
3862  if ($error) {
3863  $this->db->rollback();
3864  return -1;
3865  }
3866 
3867  $this->db->commit();
3868  return 1;
3869  }
3870 
3877  public function insert($notrigger = 0)
3878  {
3879  global $user, $conf, $langs;
3880 
3881  $error = 0;
3882 
3883  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3884 
3885  // Clean parameters
3886  $this->desc = trim($this->desc);
3887  if (empty($this->tva_tx)) {
3888  $this->tva_tx = 0;
3889  }
3890  if (empty($this->localtax1_tx)) {
3891  $this->localtax1_tx = 0;
3892  }
3893  if (empty($this->localtax2_tx)) {
3894  $this->localtax2_tx = 0;
3895  }
3896  if (empty($this->localtax1_type)) {
3897  $this->localtax1_type = '0';
3898  }
3899  if (empty($this->localtax2_type)) {
3900  $this->localtax2_type = '0';
3901  }
3902  if (empty($this->total_tva)) {
3903  $this->total_tva = 0;
3904  }
3905  if (empty($this->total_localtax1)) {
3906  $this->total_localtax1 = 0;
3907  }
3908  if (empty($this->total_localtax2)) {
3909  $this->total_localtax2 = 0;
3910  }
3911  if (empty($this->rang)) {
3912  $this->rang = 0;
3913  }
3914  if (empty($this->remise_percent)) {
3915  $this->remise_percent = 0;
3916  }
3917  if (empty($this->info_bits)) {
3918  $this->info_bits = 0;
3919  }
3920  if (empty($this->subprice)) {
3921  $this->subprice = 0;
3922  }
3923  if (empty($this->special_code)) {
3924  $this->special_code = 0;
3925  }
3926  if (empty($this->fk_parent_line)) {
3927  $this->fk_parent_line = 0;
3928  }
3929  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
3930  $this->situation_percent = 100;
3931  }
3932 
3933  if (empty($this->pa_ht)) {
3934  $this->pa_ht = 0;
3935  }
3936  if (empty($this->multicurrency_subprice)) {
3937  $this->multicurrency_subprice = 0;
3938  }
3939  if (empty($this->multicurrency_total_ht)) {
3940  $this->multicurrency_total_ht = 0;
3941  }
3942  if (empty($this->multicurrency_total_tva)) {
3943  $this->multicurrency_total_tva = 0;
3944  }
3945  if (empty($this->multicurrency_total_ttc)) {
3946  $this->multicurrency_total_ttc = 0;
3947  }
3948 
3949 
3950  // Check parameters
3951  if ($this->product_type < 0) {
3952  $this->error = 'ErrorProductTypeMustBe0orMore';
3953  return -1;
3954  }
3955  if (!empty($this->fk_product) && $this->fk_product > 0) {
3956  // Check product exists
3957  $result = Product::isExistingObject('product', $this->fk_product);
3958  if ($result <= 0) {
3959  $this->error = 'ErrorProductIdDoesNotExists';
3960  return -1;
3961  }
3962  }
3963 
3964  $this->db->begin();
3965 
3966  // Insertion dans base de la ligne
3967  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3968  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3969  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3970  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
3971  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3972  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3973  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3974  $sql .= ')';
3975  $sql .= " VALUES (".$this->fk_facture_fourn.",";
3976  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3977  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3978  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3979  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
3980  $sql .= " ".price2num($this->qty).",";
3981 
3982  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3983  $sql .= " ".price2num($this->tva_tx).",";
3984  $sql .= " ".price2num($this->localtax1_tx).",";
3985  $sql .= " ".price2num($this->localtax2_tx).",";
3986  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3987  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3988  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
3989  $sql .= " ".((int) $this->product_type).",";
3990  $sql .= " ".price2num($this->remise_percent).",";
3991  $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
3992  $sql .= " ".price2num($this->subprice).",";
3993  $sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
3994  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3995  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3996  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
3997  $sql .= ' '.((int) $this->rang).',';
3998  $sql .= ' '.((int) $this->special_code).',';
3999  $sql .= " ".((int) $this->info_bits).",";
4000  $sql .= " ".price2num($this->total_ht).",";
4001  $sql .= " ".price2num($this->total_tva).",";
4002  $sql .= " ".price2num($this->total_ttc).",";
4003  $sql .= " ".price2num($this->total_localtax1).",";
4004  $sql .= " ".price2num($this->total_localtax2);
4005  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4006  $sql .= ", ".(int) $this->fk_multicurrency;
4007  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4008  $sql .= ", ".price2num($this->multicurrency_subprice);
4009  $sql .= ", ".price2num($this->multicurrency_total_ht);
4010  $sql .= ", ".price2num($this->multicurrency_total_tva);
4011  $sql .= ", ".price2num($this->multicurrency_total_ttc);
4012  $sql .= ')';
4013 
4014  $resql = $this->db->query($sql);
4015  if ($resql) {
4016  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4017  $this->rowid = $this->id; // backward compatibility
4018 
4019  if (!$error) {
4020  $result = $this->insertExtraFields();
4021  if ($result < 0) {
4022  $error++;
4023  }
4024  }
4025 
4026  // Si fk_remise_except defini, on lie la remise a la facture
4027  // ce qui la flague comme "consommee".
4028  if ($this->fk_remise_except) {
4029  $discount = new DiscountAbsolute($this->db);
4030  $result = $discount->fetch($this->fk_remise_except);
4031  if ($result >= 0) {
4032  // Check if discount was found
4033  if ($result > 0) {
4034  // Check if discount not already affected to another invoice
4035  if ($discount->fk_facture_line > 0) {
4036  if (empty($noerrorifdiscountalreadylinked)) {
4037  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4038  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4039  $this->db->rollback();
4040  return -3;
4041  }
4042  } else {
4043  $result = $discount->link_to_invoice($this->rowid, 0);
4044  if ($result < 0) {
4045  $this->error = $discount->error;
4046  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4047  $this->db->rollback();
4048  return -3;
4049  }
4050  }
4051  } else {
4052  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4053  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4054  $this->db->rollback();
4055  return -3;
4056  }
4057  } else {
4058  $this->error = $discount->error;
4059  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4060  $this->db->rollback();
4061  return -3;
4062  }
4063  }
4064 
4065  if (!$error && !$notrigger) {
4066  // Call trigger
4067  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4068  if ($result < 0) {
4069  $this->db->rollback();
4070  return -2;
4071  }
4072  // End call triggers
4073  }
4074 
4075  $this->db->commit();
4076  return $this->id;
4077  } else {
4078  $this->error = $this->db->error();
4079  $this->db->rollback();
4080  return -2;
4081  }
4082  }
4083 
4084  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4090  public function update_total()
4091  {
4092  // phpcs:enable
4093  $this->db->begin();
4094 
4095  // Mise a jour ligne en base
4096  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4097  $sql .= " total_ht = ".price2num($this->total_ht);
4098  $sql .= ", tva= ".price2num($this->total_tva);
4099  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4100  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4101  $sql .= ", total_ttc = ".price2num($this->total_ttc);
4102  $sql .= " WHERE rowid = ".((int) $this->rowid);
4103 
4104  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4105 
4106  $resql = $this->db->query($sql);
4107  if ($resql) {
4108  $this->db->commit();
4109  return 1;
4110  } else {
4111  $this->error = $this->db->error();
4112  $this->db->rollback();
4113  return -2;
4114  }
4115  }
4116 }
$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:1485
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:1334
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:921
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