dolibarr  x.y.z
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-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
14  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
15  * Copyright (C) 2018-2022 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 
238  public $extraparams = array();
239 
244  public $lines = array();
245 
249  public $fournisseur;
250 
251  // Multicurrency
255  public $fk_multicurrency;
256 
257  public $multicurrency_code;
258  public $multicurrency_tx;
259  public $multicurrency_total_ht;
260  public $multicurrency_total_tva;
261  public $multicurrency_total_ttc;
263 
266  public $fk_facture_source;
267 
268  public $fac_rec;
269 
270 
271  public $fields = array(
272  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
273  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
274  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefSupplier', 'enabled'=>1, 'visible'=>-1, 'position'=>20),
275  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>25, 'index'=>1),
276  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>30),
277  'type' =>array('type'=>'smallint(6)', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
278  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'notnull'=>1, 'position'=>40),
279  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
280  'datef' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
281  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>55),
282  'libelle' =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
283  'paye' =>array('type'=>'smallint(6)', 'label'=>'Paye', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
284  'amount' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
285  'remise' =>array('type'=>'double(24,8)', 'label'=>'Discount', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
286  'close_code' =>array('type'=>'varchar(16)', 'label'=>'CloseCode', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
287  'close_note' =>array('type'=>'varchar(128)', 'label'=>'CloseNote', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
288  'tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
289  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
290  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>100),
291  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
292  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'TotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
293  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
294  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
295  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>130),
296  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
297  'fk_facture_source' =>array('type'=>'integer', 'label'=>'Fk facture source', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
298  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>145),
299  'fk_account' =>array('type'=>'integer', 'label'=>'Account', 'enabled'=>'$conf->banque->enabled', 'visible'=>-1, 'position'=>150),
300  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
301  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
302  'date_lim_reglement' =>array('type'=>'date', 'label'=>'DateLimReglement', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
303  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>170),
304  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>175),
305  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>180),
306  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
307  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
308  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
309  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'MulticurrencyId', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
310  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCode', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
311  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
312  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
313  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
314  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>230),
315  'date_pointoftax' =>array('type'=>'date', 'label'=>'Date pointoftax', 'enabled'=>1, 'visible'=>-1, 'position'=>235),
316  'date_valid' =>array('type'=>'date', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>240),
317  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>245),
318  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
319  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
320  );
321 
322 
326  const TYPE_STANDARD = 0;
327 
331  const TYPE_REPLACEMENT = 1;
332 
336  const TYPE_CREDIT_NOTE = 2;
337 
341  const TYPE_DEPOSIT = 3;
342 
346  const STATUS_DRAFT = 0;
347 
351  const STATUS_VALIDATED = 1;
352 
360  const STATUS_CLOSED = 2;
361 
369  const STATUS_ABANDONED = 3;
370 
371  const CLOSECODE_DISCOUNTVAT = 'discount_vat';
372  const CLOSECODE_BADCREDIT = 'badsupplier';
373  const CLOSECODE_ABANDONED = 'abandon';
374  const CLOSECODE_REPLACED = 'replaced';
375 
381  public function __construct($db)
382  {
383  $this->db = $db;
384  }
385 
392  public function create($user)
393  {
394  global $langs, $conf, $hookmanager;
395 
396  $error = 0;
397  $now = dol_now();
398 
399  // Clean parameters
400  if (isset($this->ref_supplier)) {
401  $this->ref_supplier = trim($this->ref_supplier);
402  }
403  if (empty($this->type)) {
404  $this->type = self::TYPE_STANDARD;
405  }
406  if (empty($this->date)) {
407  $this->date = $now;
408  }
409 
410  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
411  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
412  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
413  } else {
414  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
415  }
416  if (empty($this->fk_multicurrency)) {
417  $this->multicurrency_code = $conf->currency;
418  $this->fk_multicurrency = 0;
419  $this->multicurrency_tx = 1;
420  }
421 
422  $this->db->begin();
423 
424  // Create invoice from a template recurring invoice
425  if ($this->fac_rec > 0) {
426  $this->fk_fac_rec_source = $this->fac_rec;
427 
428  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
429  $_facrec = new FactureFournisseurRec($this->db);
430  $result = $_facrec->fetch($this->fac_rec);
431  $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
432 
433  // Define some dates
434  if (!empty($_facrec->frequency)) {
435  $originaldatewhen = $_facrec->date_when;
436  $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
437  $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
438  $this->socid = $_facrec->socid;
439  }
440 
441  $this->entity = $_facrec->entity; // Invoice created in same entity than template
442 
443  // 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
444  $this->fk_project = GETPOST('projectid', 'int') > 0 ? ((int) GETPOST('projectid', 'int')) : $_facrec->fk_projet;
445  $this->fk_projet = $this->fk_project;
446  $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
447  $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
448  $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
449  $this->cond_reglement_id = GETPOST('cond_reglement_id', 'int') > 0 ? ((int) GETPOST('cond_reglement_id', 'int')) : $_facrec->cond_reglement_id;
450  $this->mode_reglement_id = GETPOST('mode_reglement_id', 'int') > 0 ? ((int) GETPOST('mode_reglement_id', 'int')) : $_facrec->mode_reglement_id;
451  $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
452 
453  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
454  $this->total_ht = $_facrec->total_ht;
455  $this->total_ttc = $_facrec->total_ttc;
456 
457  // Fields always coming from template
458  $this->fk_incoterms = $_facrec->fk_incoterms;
459  $this->location_incoterms = $_facrec->location_incoterms;
460 
461  // Clean parameters
462  if (! $this->type) {
463  $this->type = self::TYPE_STANDARD;
464  }
465  if (!empty(GETPOST('ref_supplier'))) {
466  $this->ref_supplier = trim($this->ref_supplier);
467  } else {
468  $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
469  }
470  $this->note_public = trim($this->note_public);
471  $this->note_private = trim($this->note_private);
472  $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->titre));
473 
474  $this->array_options = $_facrec->array_options;
475 
476  if (! $this->mode_reglement_id) {
477  $this->mode_reglement_id = 0;
478  }
479  $this->brouillon = 1;
480  $this->status = self::STATUS_DRAFT;
481  $this->statut = self::STATUS_DRAFT;
482 
483  $this->linked_objects = $_facrec->linkedObjectsIds;
484  // We do not add link to template invoice or next invoice will be linked to all generated invoices
485  //$this->linked_objects['facturerec'][0] = $this->fac_rec;
486 
487  $forceduedate = $this->calculate_date_lim_reglement();
488 
489  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
490  if ($_facrec->frequency > 0) {
491  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
492  if (empty($_facrec->date_when)) {
493  $_facrec->date_when = $now;
494  }
495  $next_date = $_facrec->getNextDate(); // Calculate next date
496  $result = $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date', '', $user, '');
497  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
498  $result = $_facrec->setNextDate($next_date, 1);
499  }
500 
501  // Define lang of customer
502  $outputlangs = $langs;
503  $newlang = '';
504 
505  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
506  $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
507  }
508  if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) {
509  $newlang = $this->default_lang; // for thirdparty
510  }
511  if (!empty($newlang)) {
512  $outputlangs = new Translate("", $conf);
513  $outputlangs->setDefaultLang($newlang);
514  }
515 
516  // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
517  $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
518  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
519  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
520  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
521  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
522  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
523  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
524  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
525  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
526  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y');
527  // Only for template invoice
528  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour');
529  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($nextdatewhen, 'dayhour');
530  $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($previousdaynextdatewhen, 'dayhour');
531  $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
532  $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
533 
534  complete_substitutions_array($substitutionarray, $outputlangs);
535 
536  $this->note_public = make_substitutions($this->note_public, $substitutionarray);
537  $this->note_private = make_substitutions($this->note_private, $substitutionarray);
538  }
539 
540  // Define due date if not already defined
541  if (!empty($forceduedate)) {
542  $this->date_echeance = $forceduedate;
543  }
544 
545  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
546  $sql .= "ref";
547  $sql .= ", ref_supplier";
548  $sql .= ", ref_ext";
549  $sql .= ", entity";
550  $sql .= ", type";
551  $sql .= ", libelle";
552  $sql .= ", fk_soc";
553  $sql .= ", datec";
554  $sql .= ", datef";
555  $sql .= ", fk_projet";
556  $sql .= ", fk_cond_reglement";
557  $sql .= ", fk_mode_reglement";
558  $sql .= ", fk_account";
559  $sql .= ", note_private";
560  $sql .= ", note_public";
561  $sql .= ", fk_user_author";
562  $sql .= ", date_lim_reglement";
563  $sql .= ", fk_incoterms, location_incoterms";
564  $sql .= ", fk_multicurrency";
565  $sql .= ", multicurrency_code";
566  $sql .= ", multicurrency_tx";
567  $sql .= ", fk_facture_source";
568  $sql .= ", fk_fac_rec_source";
569  $sql .= ")";
570  $sql .= " VALUES (";
571  $sql .= "'(PROV)'";
572  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
573  $sql .= ", '".$this->db->escape($this->ref_ext)."'";
574  $sql .= ", ".((int) $conf->entity);
575  $sql .= ", '".$this->db->escape($this->type)."'";
576  $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
577  $sql .= ", ".((int) $this->socid);
578  $sql .= ", '".$this->db->idate($now)."'";
579  $sql .= ", '".$this->db->idate($this->date)."'";
580  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
581  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
582  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
583  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
584  $sql .= ", '".$this->db->escape($this->note_private)."'";
585  $sql .= ", '".$this->db->escape($this->note_public)."'";
586  $sql .= ", ".((int) $user->id).",";
587  $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
588  $sql .= ", ".(int) $this->fk_incoterms;
589  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
590  $sql .= ", ".(int) $this->fk_multicurrency;
591  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
592  $sql .= ", ".(double) $this->multicurrency_tx;
593  $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
594  $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
595  $sql .= ")";
596 
597  dol_syslog(get_class($this)."::create", LOG_DEBUG);
598  $resql = $this->db->query($sql);
599  if ($resql) {
600  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
601 
602  // Update ref with new one
603  $this->ref = '(PROV'.$this->id.')';
604  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
605 
606  dol_syslog(get_class($this)."::create", LOG_DEBUG);
607  $resql = $this->db->query($sql);
608  if (!$resql) {
609  $error++;
610  }
611 
612  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
613  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
614  }
615 
616  // Add object linked
617  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
618  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
619  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, ...))
620  foreach ($tmp_origin_id as $origin_id) {
621  $ret = $this->add_object_linked($origin, $origin_id);
622  if (!$ret) {
623  dol_print_error($this->db);
624  $error++;
625  }
626  }
627  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
628  {
629  $origin_id = $tmp_origin_id;
630  $ret = $this->add_object_linked($origin, $origin_id);
631  if (!$ret) {
632  dol_print_error($this->db);
633  $error++;
634  }
635  }
636  }
637  }
638 
639  if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
640  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
641  foreach ($this->lines as $i => $val) {
642  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
643  $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').')';
644 
645  $resql_insert = $this->db->query($sql);
646  if ($resql_insert) {
647  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
648 
649  $res = $this->updateline(
650  $idligne,
651  $this->lines[$i]->description,
652  $this->lines[$i]->pu_ht,
653  $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
654  $this->lines[$i]->localtax1_tx,
655  $this->lines[$i]->localtax2_tx,
656  $this->lines[$i]->qty,
657  $this->lines[$i]->fk_product,
658  'HT',
659  (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
660  $this->lines[$i]->product_type,
661  $this->lines[$i]->remise_percent,
662  false,
663  $this->lines[$i]->date_start,
664  $this->lines[$i]->date_end,
665  $this->lines[$i]->array_options,
666  $this->lines[$i]->fk_unit,
667  $this->lines[$i]->multicurrency_subprice,
668  $this->lines[$i]->ref_supplier
669  );
670  } else {
671  $this->error = $this->db->lasterror();
672  $this->db->rollback();
673  return -5;
674  }
675  }
676  } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
677  dol_syslog("There is ".count($this->lines)." lines that are array lines");
678  foreach ($this->lines as $i => $val) {
679  $line = $this->lines[$i];
680 
681  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
682  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
683  if (!is_object($line)) {
684  $line = (object) $line;
685  }
686 
687  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
688  $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').')';
689 
690  $resql_insert = $this->db->query($sql);
691  if ($resql_insert) {
692  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
693 
694  $this->updateline(
695  $idligne,
696  $line->description,
697  $line->pu_ht,
698  $line->tva_tx,
699  $line->localtax1_tx,
700  $line->localtax2_tx,
701  $line->qty,
702  $line->fk_product,
703  'HT',
704  (!empty($line->info_bits) ? $line->info_bits : ''),
705  $line->product_type,
706  $line->remise_percent,
707  0,
708  $line->date_start,
709  $line->date_end,
710  $line->array_options,
711  $line->fk_unit,
712  $line->multicurrency_subprice,
713  $line->ref_supplier
714  );
715  } else {
716  $this->error = $this->db->lasterror();
717  $this->db->rollback();
718  return -5;
719  }
720  }
721  }
722 
723  /*
724  * Insert lines of template invoices
725  */
726  if (! $error && $this->fac_rec > 0) {
727  foreach ($_facrec->lines as $i => $val) {
728  if ($_facrec->lines[$i]->fk_product) {
729  $prod = new Product($this->db);
730  $res = $prod->fetch($_facrec->lines[$i]->fk_product);
731  }
732 
733  // For line from template invoice, we use data from template invoice
734  /*
735  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
736  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
737  if (empty($tva_tx)) $tva_npr=0;
738  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
739  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
740  */
741  $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
742  $tva_npr = $_facrec->lines[$i]->info_bits;
743  if (empty($tva_tx)) {
744  $tva_npr = 0;
745  }
746  $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
747  $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
748 
749  $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
750  $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
751 
752  // If buyprice not defined from template invoice, we try to guess the best value
753  if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
754  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
755  $producttmp = new ProductFournisseur($this->db);
756  $producttmp->fetch($_facrec->lines[$i]->fk_product);
757 
758  // If margin module defined on costprice, we try the costprice
759  // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
760  // else we get the best supplier price
761  if ($conf->global->MARGIN_TYPE == 'costprice' && !empty($producttmp->cost_price)) {
762  $buyprice = $producttmp->cost_price;
763  } elseif (isModEnabled('stock') && ($conf->global->MARGIN_TYPE == 'costprice' || $conf->global->MARGIN_TYPE == 'pmp') && !empty($producttmp->pmp)) {
764  $buyprice = $producttmp->pmp;
765  } else {
766  if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
767  if ($producttmp->product_fourn_price_id > 0) {
768  $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
769  }
770  }
771  }
772  }
773 
774  $result_insert = $this->addline(
775  $_facrec->lines[$i]->description,
776  $_facrec->lines[$i]->pu_ht,
777  $tva_tx,
778  $localtax1_tx,
779  $localtax2_tx,
780  $_facrec->lines[$i]->qty,
781  $_facrec->lines[$i]->fk_product,
782  $_facrec->lines[$i]->remise_percent,
783  ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
784  ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
785  0,
786  $_facrec->lines[$i]->info_bits,
787  'HT',
788  0,
789  $_facrec->lines[$i]->rang,
790  false,
791  $_facrec->lines[$i]->array_options,
792  $_facrec->lines[$i]->fk_unit,
793  0,
794  0,
795  $_facrec->lines[$i]->ref_supplier,
796  $_facrec->lines[$i]->special_code,
797  0,
798  0
799  );
800  if ($result_insert < 0) {
801  $error++;
802  $this->error = $this->db->error();
803  break;
804  }
805  }
806  }
807 
808 
809  // Update total price
810  $result = $this->update_price(1);
811  if ($result > 0) {
812  // Actions on extra fields
813  if (!$error) {
814  $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
815  if ($result < 0) {
816  $error++;
817  }
818  }
819 
820  if (!$error) {
821  // Call trigger
822  $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
823  if ($result < 0) {
824  $error++;
825  }
826  // End call triggers
827  }
828 
829  if (!$error) {
830  $this->db->commit();
831  return $this->id;
832  } else {
833  $this->db->rollback();
834  return -4;
835  }
836  } else {
837  $this->error = $langs->trans('FailedToUpdatePrice');
838  $this->db->rollback();
839  return -3;
840  }
841  } else {
842  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
843  $this->error = $langs->trans('ErrorRefAlreadyExists');
844  $this->db->rollback();
845  return -1;
846  } else {
847  $this->error = $this->db->lasterror();
848  $this->db->rollback();
849  return -2;
850  }
851  }
852  }
853 
862  public function fetch($id = '', $ref = '', $ref_ext = '')
863  {
864  if (empty($id) && empty($ref) && empty($ref_ext)) {
865  return -1;
866  }
867 
868  $sql = "SELECT";
869  $sql .= " t.rowid,";
870  $sql .= " t.ref,";
871  $sql .= " t.ref_supplier,";
872  $sql .= " t.ref_ext,";
873  $sql .= " t.entity,";
874  $sql .= " t.type,";
875  $sql .= " t.fk_soc,";
876  $sql .= " t.datec,";
877  $sql .= " t.datef,";
878  $sql .= " t.tms,";
879  $sql .= " t.libelle as label,";
880  $sql .= " t.paye,";
881  $sql .= " t.close_code,";
882  $sql .= " t.close_note,";
883  $sql .= " t.tva,";
884  $sql .= " t.localtax1,";
885  $sql .= " t.localtax2,";
886  $sql .= " t.total_ht,";
887  $sql .= " t.total_tva,";
888  $sql .= " t.total_ttc,";
889  $sql .= " t.fk_statut as status,";
890  $sql .= " t.fk_user_author,";
891  $sql .= " t.fk_user_valid,";
892  $sql .= " t.fk_facture_source,";
893  $sql .= " t.fk_fac_rec_source,";
894  $sql .= " t.fk_projet as fk_project,";
895  $sql .= " t.fk_cond_reglement,";
896  $sql .= " t.fk_account,";
897  $sql .= " t.fk_mode_reglement,";
898  $sql .= " t.date_lim_reglement,";
899  $sql .= " t.note_private,";
900  $sql .= " t.note_public,";
901  $sql .= " t.model_pdf,";
902  $sql .= " t.import_key,";
903  $sql .= " t.extraparams,";
904  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
905  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
906  $sql .= ' s.nom as socnom, s.rowid as socid,';
907  $sql .= ' t.fk_incoterms, t.location_incoterms,';
908  $sql .= " i.libelle as label_incoterms,";
909  $sql .= ' t.fk_transport_mode,';
910  $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
911  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
912  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
913  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
914  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
915  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
916  if ($id) {
917  $sql .= " WHERE t.rowid = ".((int) $id);
918  } else {
919  $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
920  if ($ref) {
921  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
922  }
923  if ($ref_ext) {
924  $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
925  }
926  }
927 
928  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
929  $resql = $this->db->query($sql);
930  if ($resql) {
931  if ($this->db->num_rows($resql)) {
932  $obj = $this->db->fetch_object($resql);
933 
934  $this->id = $obj->rowid;
935  $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
936 
937  $this->ref_supplier = $obj->ref_supplier;
938  $this->ref_ext = $obj->ref_ext;
939  $this->entity = $obj->entity;
940  $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
941  $this->fk_soc = $obj->fk_soc;
942  $this->datec = $this->db->jdate($obj->datec);
943  $this->date = $this->db->jdate($obj->datef);
944  $this->datep = $this->db->jdate($obj->datef);
945  $this->tms = $this->db->jdate($obj->tms);
946  $this->libelle = $obj->label; // deprecated
947  $this->label = $obj->label;
948  $this->paye = $obj->paye;
949  $this->paid = $obj->paye;
950  $this->close_code = $obj->close_code;
951  $this->close_note = $obj->close_note;
952  $this->total_localtax1 = $obj->localtax1;
953  $this->total_localtax2 = $obj->localtax2;
954  $this->total_ht = $obj->total_ht;
955  $this->total_tva = $obj->total_tva;
956  $this->total_ttc = $obj->total_ttc;
957  $this->status = $obj->status;
958  $this->statut = $obj->status; // For backward compatibility
959  $this->fk_statut = $obj->status; // For backward compatibility
960  $this->fk_user_author = $obj->fk_user_author;
961  $this->author = $obj->fk_user_author;
962  $this->fk_user_valid = $obj->fk_user_valid;
963  $this->fk_facture_source = $obj->fk_facture_source;
964  $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
965  $this->fk_project = $obj->fk_project;
966  $this->cond_reglement_id = $obj->fk_cond_reglement;
967  $this->cond_reglement_code = $obj->cond_reglement_code;
968  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
969  $this->cond_reglement_label = $obj->cond_reglement_label;
970  $this->cond_reglement_doc = $obj->cond_reglement_doc;
971  $this->fk_account = $obj->fk_account;
972  $this->mode_reglement_id = $obj->fk_mode_reglement;
973  $this->mode_reglement_code = $obj->mode_reglement_code;
974  $this->mode_reglement = $obj->mode_reglement_label;
975  $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
976  $this->note = $obj->note_private; // deprecated
977  $this->note_private = $obj->note_private;
978  $this->note_public = $obj->note_public;
979  $this->model_pdf = $obj->model_pdf;
980  $this->modelpdf = $obj->model_pdf; // deprecated
981  $this->import_key = $obj->import_key;
982 
983  //Incoterms
984  $this->fk_incoterms = $obj->fk_incoterms;
985  $this->location_incoterms = $obj->location_incoterms;
986  $this->label_incoterms = $obj->label_incoterms;
987  $this->transport_mode_id = $obj->fk_transport_mode;
988 
989  // Multicurrency
990  $this->fk_multicurrency = $obj->fk_multicurrency;
991  $this->multicurrency_code = $obj->multicurrency_code;
992  $this->multicurrency_tx = $obj->multicurrency_tx;
993  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
994  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
995  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
996 
997  $this->extraparams = (array) json_decode($obj->extraparams, true);
998 
999  $this->socid = $obj->socid;
1000  $this->socnom = $obj->socnom;
1001 
1002  // Retrieve all extrafield
1003  // fetch optionals attributes and labels
1004  $this->fetch_optionals();
1005 
1006  if ($this->statut == self::STATUS_DRAFT) {
1007  $this->brouillon = 1;
1008  }
1009 
1010  $result = $this->fetch_lines();
1011  if ($result < 0) {
1012  $this->error = $this->db->lasterror();
1013  return -3;
1014  }
1015  } else {
1016  $this->error = 'Bill with id '.$id.' not found';
1017  dol_syslog(get_class($this).'::fetch '.$this->error);
1018  return 0;
1019  }
1020 
1021  $this->db->free($resql);
1022  return 1;
1023  } else {
1024  $this->error = "Error ".$this->db->lasterror();
1025  return -1;
1026  }
1027  }
1028 
1029 
1030  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1036  public function fetch_lines()
1037  {
1038  // phpcs:enable
1039  $this->lines = array();
1040 
1041  $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';
1042  $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';
1043  $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';
1044  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.description as product_desc';
1045  $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';
1046  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1047  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1048  $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1049  $sql .= ' ORDER BY f.rang, f.rowid';
1050 
1051  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1052 
1053  $resql_rows = $this->db->query($sql);
1054  if ($resql_rows) {
1055  $num_rows = $this->db->num_rows($resql_rows);
1056  if ($num_rows) {
1057  $i = 0;
1058  while ($i < $num_rows) {
1059  $obj = $this->db->fetch_object($resql_rows);
1060 
1061  $line = new SupplierInvoiceLine($this->db);
1062 
1063  $line->id = $obj->rowid;
1064  $line->rowid = $obj->rowid;
1065  $line->description = $obj->description;
1066  $line->date_start = $obj->date_start;
1067  $line->date_end = $obj->date_end;
1068 
1069  $line->product_ref = $obj->product_ref;
1070  $line->ref = $obj->product_ref;
1071  $line->ref_supplier = $obj->ref_supplier;
1072  $line->libelle = $obj->label;
1073  $line->label = $obj->label;
1074  $line->product_desc = $obj->product_desc;
1075  $line->subprice = $obj->pu_ht;
1076  $line->pu_ht = $obj->pu_ht;
1077  $line->pu_ttc = $obj->pu_ttc;
1078 
1079  $line->vat_src_code = $obj->vat_src_code;
1080  $line->tva_tx = $obj->tva_tx;
1081  $line->localtax1_tx = $obj->localtax1_tx;
1082  $line->localtax2_tx = $obj->localtax2_tx;
1083  $line->localtax1_type = $obj->localtax1_type;
1084  $line->localtax2_type = $obj->localtax2_type;
1085  $line->qty = $obj->qty;
1086  $line->remise_percent = $obj->remise_percent;
1087  $line->fk_remise_except = $obj->fk_remise_except;
1088  //$line->tva = $obj->total_tva; // deprecated
1089  $line->total_ht = $obj->total_ht;
1090  $line->total_ttc = $obj->total_ttc;
1091  $line->total_tva = $obj->total_tva;
1092  $line->total_localtax1 = $obj->total_localtax1;
1093  $line->total_localtax2 = $obj->total_localtax2;
1094  $line->fk_facture_fourn = $obj->fk_facture_fourn;
1095  $line->fk_product = $obj->fk_product;
1096  $line->product_type = $obj->product_type;
1097  $line->product_label = $obj->label;
1098  $line->info_bits = $obj->info_bits;
1099  $line->fk_parent_line = $obj->fk_parent_line;
1100  $line->special_code = $obj->special_code;
1101  $line->rang = $obj->rang;
1102  $line->fk_unit = $obj->fk_unit;
1103 
1104  // Accountancy
1105  $line->code_ventilation = $obj->fk_code_ventilation;
1106  $line->fk_accounting_account = $obj->fk_code_ventilation;
1107 
1108  // Multicurrency
1109  $line->fk_multicurrency = $obj->fk_multicurrency;
1110  $line->multicurrency_code = $obj->multicurrency_code;
1111  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1112  $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1113  $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1114  $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1115 
1116  // Extra fields
1117  $line->fetch_optionals();
1118 
1119  $this->lines[$i] = $line;
1120 
1121  $i++;
1122  }
1123  }
1124  $this->db->free($resql_rows);
1125  return 1;
1126  } else {
1127  $this->error = $this->db->error();
1128  return -3;
1129  }
1130  }
1131 
1132 
1140  public function update($user = null, $notrigger = 0)
1141  {
1142  global $conf, $langs;
1143  $error = 0;
1144 
1145  // Clean parameters
1146  if (empty($this->type)) {
1147  $this->type = self::TYPE_STANDARD;
1148  }
1149  if (isset($this->ref)) {
1150  $this->ref = trim($this->ref);
1151  }
1152  if (isset($this->ref_supplier)) {
1153  $this->ref_supplier = trim($this->ref_supplier);
1154  }
1155  if (isset($this->ref_ext)) {
1156  $this->ref_ext = trim($this->ref_ext);
1157  }
1158  if (isset($this->entity)) {
1159  $this->entity = trim($this->entity);
1160  }
1161  if (isset($this->type)) {
1162  $this->type = trim($this->type);
1163  }
1164  if (isset($this->fk_soc)) {
1165  $this->fk_soc = trim($this->fk_soc);
1166  }
1167  if (isset($this->label)) {
1168  $this->label = trim($this->label);
1169  }
1170  if (isset($this->libelle)) {
1171  $this->libelle = trim($this->libelle); // deprecated
1172  }
1173  if (isset($this->paye)) {
1174  $this->paye = trim($this->paye);
1175  }
1176  if (isset($this->close_code)) {
1177  $this->close_code = trim($this->close_code);
1178  }
1179  if (isset($this->close_note)) {
1180  $this->close_note = trim($this->close_note);
1181  }
1182  if (isset($this->localtax1)) {
1183  $this->localtax1 = trim($this->localtax1);
1184  }
1185  if (isset($this->localtax2)) {
1186  $this->localtax2 = trim($this->localtax2);
1187  }
1188  if (empty($this->total_ht)) {
1189  $this->total_ht = 0;
1190  }
1191  if (empty($this->total_tva)) {
1192  $this->total_tva = 0;
1193  }
1194  // if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
1195  // if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
1196  if (isset($this->total_ttc)) {
1197  $this->total_ttc = trim($this->total_ttc);
1198  }
1199  if (isset($this->statut)) {
1200  $this->statut = (int) $this->statut;
1201  }
1202  if (isset($this->status)) {
1203  $this->status = (int) $this->status;
1204  }
1205  if (isset($this->author)) {
1206  $this->author = trim($this->author);
1207  }
1208  if (isset($this->fk_user_valid)) {
1209  $this->fk_user_valid = trim($this->fk_user_valid);
1210  }
1211  if (isset($this->fk_facture_source)) {
1212  $this->fk_facture_source = trim($this->fk_facture_source);
1213  }
1214  if (isset($this->fk_project)) {
1215  if (empty($this->fk_project)) $this->fk_project = null;
1216  else $this->fk_project = intval($this->fk_project);
1217  }
1218  if (isset($this->cond_reglement_id)) {
1219  $this->cond_reglement_id = trim($this->cond_reglement_id);
1220  }
1221  if (isset($this->note_private)) {
1222  $this->note = trim($this->note_private);
1223  }
1224  if (isset($this->note_public)) {
1225  $this->note_public = trim($this->note_public);
1226  }
1227  if (isset($this->model_pdf)) {
1228  $this->model_pdf = trim($this->model_pdf);
1229  }
1230  if (isset($this->import_key)) {
1231  $this->import_key = trim($this->import_key);
1232  }
1233 
1234 
1235  // Check parameters
1236  // Put here code to add control on parameters values
1237 
1238  // Update request
1239  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1240  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1241  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1242  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1243  $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1244  $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1245  $sql .= " fk_soc=".(isset($this->fk_soc) ? ((int) $this->fk_soc) : "null").",";
1246  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1247  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1248  if (dol_strlen($this->tms) != 0) {
1249  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1250  }
1251  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1252  $sql .= " paye=".(isset($this->paye) ? ((int) $this->paye) : "null").",";
1253  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1254  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1255  $sql .= " localtax1=".(isset($this->localtax1) ? ((float) $this->localtax1) : "null").",";
1256  $sql .= " localtax2=".(isset($this->localtax2) ? ((float) $this->localtax2) : "null").",";
1257  $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1258  $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1259  $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1260  $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1261  $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1262  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1263  $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1264  $sql .= " fk_projet=".(isset($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1265  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1266  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1267  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1268  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1269  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1270  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1271  $sql .= " WHERE rowid=".((int) $this->id);
1272 
1273  $this->db->begin();
1274 
1275  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1276  $resql = $this->db->query($sql);
1277 
1278  if (!$resql) {
1279  $error++;
1280 
1281  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1282  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1283  } else {
1284  $this->errors[] = "Error ".$this->db->lasterror();
1285  }
1286  }
1287 
1288  if (!$error) {
1289  $result = $this->insertExtraFields();
1290  if ($result < 0) {
1291  $error++;
1292  }
1293  }
1294 
1295  if (!$error) {
1296  if (!$notrigger) {
1297  // Call trigger
1298  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1299  if ($result < 0) {
1300  $error++;
1301  }
1302  // End call triggers
1303  }
1304  }
1305 
1306  // Commit or rollback
1307  if ($error) {
1308  foreach ($this->errors as $errmsg) {
1309  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1310  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1311  }
1312  $this->db->rollback();
1313  return -1 * $error;
1314  } else {
1315  $this->db->commit();
1316  return 1;
1317  }
1318  }
1319 
1320  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1327  public function insert_discount($idremise)
1328  {
1329  // phpcs:enable
1330  global $conf, $langs;
1331 
1332  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1333  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1334 
1335  $this->db->begin();
1336 
1337  $remise = new DiscountAbsolute($this->db);
1338  $result = $remise->fetch($idremise);
1339 
1340  if ($result > 0) {
1341  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1342  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1343  $this->db->rollback();
1344  return -5;
1345  }
1346 
1347  $facligne = new SupplierInvoiceLine($this->db);
1348  $facligne->fk_facture_fourn = $this->id;
1349  $facligne->fk_remise_except = $remise->id;
1350  $facligne->desc = $remise->description; // Description ligne
1351  $facligne->vat_src_code = $remise->vat_src_code;
1352  $facligne->tva_tx = $remise->tva_tx;
1353  $facligne->subprice = -$remise->amount_ht;
1354  $facligne->fk_product = 0; // Id produit predefini
1355  $facligne->product_type = 0;
1356  $facligne->qty = 1;
1357  $facligne->remise_percent = 0;
1358  $facligne->rang = -1;
1359  $facligne->info_bits = 2;
1360 
1361  if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
1362  $facligne->rang = 1;
1363  $linecount = count($this->lines);
1364  for ($ii = 1; $ii <= $linecount; $ii++) {
1365  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii+1);
1366  }
1367  }
1368 
1369  // Get buy/cost price of invoice that is source of discount
1370  if ($remise->fk_invoice_supplier_source > 0) {
1371  $srcinvoice = new FactureFournisseur($this->db);
1372  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1373  $totalcostpriceofinvoice = 0;
1374  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1375  $formmargin = new FormMargin($this->db);
1376  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1377  $facligne->pa_ht = $arraytmp['pa_total'];
1378  }
1379 
1380  $facligne->total_ht = -$remise->amount_ht;
1381  $facligne->total_tva = -$remise->amount_tva;
1382  $facligne->total_ttc = -$remise->amount_ttc;
1383 
1384  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1385  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1386  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1387  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1388 
1389  $lineid = $facligne->insert();
1390  if ($lineid > 0) {
1391  $result = $this->update_price(1);
1392  if ($result > 0) {
1393  // Create link between discount and invoice line
1394  $result = $remise->link_to_invoice($lineid, 0);
1395  if ($result < 0) {
1396  $this->error = $remise->error;
1397  $this->db->rollback();
1398  return -4;
1399  }
1400 
1401  $this->db->commit();
1402  return 1;
1403  } else {
1404  $this->error = $facligne->error;
1405  $this->db->rollback();
1406  return -1;
1407  }
1408  } else {
1409  $this->error = $facligne->error;
1410  $this->db->rollback();
1411  return -2;
1412  }
1413  } else {
1414  $this->db->rollback();
1415  return -3;
1416  }
1417  }
1418 
1419 
1427  public function delete(User $user, $notrigger = 0)
1428  {
1429  global $langs, $conf;
1430 
1431  $rowid = $this->id;
1432 
1433  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1434 
1435  // TODO Test if there is at least on payment. If yes, refuse to delete.
1436 
1437  $error = 0;
1438  $this->db->begin();
1439 
1440  if (!$error && !$notrigger) {
1441  // Call trigger
1442  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1443  if ($result < 0) {
1444  $this->db->rollback();
1445  return -1;
1446  }
1447  // Fin appel triggers
1448  }
1449 
1450  if (!$error) {
1451  // If invoice was converted into a discount not yet consumed, we remove discount
1452  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1453  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1454  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1455  $resql = $this->db->query($sql);
1456 
1457  // If invoice has consumned discounts
1458  $this->fetch_lines();
1459  $list_rowid_det = array();
1460  foreach ($this->lines as $key => $invoiceline) {
1461  $list_rowid_det[] = $invoiceline->rowid;
1462  }
1463 
1464  // Consumned discounts are freed
1465  if (count($list_rowid_det)) {
1466  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1467  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1468  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(join(',', $list_rowid_det)).')';
1469 
1470  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1471  if (!$this->db->query($sql)) {
1472  $error++;
1473  }
1474  }
1475  }
1476 
1477  if (!$error) {
1478  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1479  $ef = $main."_extrafields";
1480  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1481  $resqlef = $this->db->query($sqlef);
1482  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1483  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1484  $resql = $this->db->query($sql);
1485  if ($resqlef && $resql) {
1486  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1487  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1488  $resql2 = $this->db->query($sql);
1489  if (!$resql2) {
1490  $error++;
1491  }
1492  } else {
1493  $error++;
1494  }
1495  }
1496 
1497  if (!$error) {
1498  // Delete linked object
1499  $res = $this->deleteObjectLinked();
1500  if ($res < 0) {
1501  $error++;
1502  }
1503  }
1504 
1505  if (!$error) {
1506  // Delete linked object
1507  $res = $this->deleteObjectLinked();
1508  if ($res < 0) {
1509  $error++;
1510  }
1511  }
1512 
1513  if (!$error) {
1514  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1515  $this->deleteEcmFiles();
1516 
1517  // We remove directory
1518  if ($conf->fournisseur->facture->dir_output) {
1519  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1520 
1521  $ref = dol_sanitizeFileName($this->ref);
1522  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1523  $file = $dir."/".$ref.".pdf";
1524  if (file_exists($file)) {
1525  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1526  $this->error = 'ErrorFailToDeleteFile';
1527  $error++;
1528  }
1529  }
1530  if (file_exists($dir)) {
1531  $res = @dol_delete_dir_recursive($dir);
1532 
1533  if (!$res) {
1534  $this->error = 'ErrorFailToDeleteDir';
1535  $error++;
1536  }
1537  }
1538  }
1539  }
1540 
1541  // Remove extrafields
1542  if (!$error) {
1543  $result = $this->deleteExtraFields();
1544  if ($result < 0) {
1545  $error++;
1546  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1547  }
1548  }
1549 
1550  if (!$error) {
1551  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1552  $this->db->commit();
1553  return 1;
1554  } else {
1555  $this->error = $this->db->lasterror();
1556  $this->db->rollback();
1557  return -$error;
1558  }
1559  }
1560 
1561 
1562  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1573  public function set_paid($user, $close_code = '', $close_note = '')
1574  {
1575  // phpcs:enable
1576  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1577  return $this->setPaid($user, $close_code, $close_note);
1578  }
1579 
1588  public function setPaid($user, $close_code = '', $close_note = '')
1589  {
1590  $error = 0;
1591 
1592  if ($this->paye != 1) {
1593  $this->db->begin();
1594 
1595  $now = dol_now();
1596 
1597  dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1598 
1599  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1600  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1601  if (!$close_code) {
1602  $sql .= ', paye=1';
1603  }
1604  if ($close_code) {
1605  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1606  }
1607  if ($close_note) {
1608  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1609  }
1610  $sql .= ', fk_user_closing = '.((int) $user->id);
1611  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1612  $sql .= ' WHERE rowid = '.((int) $this->id);
1613 
1614  $resql = $this->db->query($sql);
1615  if ($resql) {
1616  // Call trigger
1617  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1618  if ($result < 0) {
1619  $error++;
1620  }
1621  // End call triggers
1622  } else {
1623  $error++;
1624  $this->error = $this->db->error();
1625  dol_print_error($this->db);
1626  }
1627 
1628  if (!$error) {
1629  $this->db->commit();
1630  return 1;
1631  } else {
1632  $this->db->rollback();
1633  return -1;
1634  }
1635  } else {
1636  return 0;
1637  }
1638  }
1639 
1640  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1651  public function set_unpaid($user)
1652  {
1653  // phpcs:enable
1654  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1655  return $this->setUnpaid($user);
1656  }
1657 
1666  public function setUnpaid($user)
1667  {
1668  $error = 0;
1669 
1670  $this->db->begin();
1671 
1672  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1673  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1674  $sql .= ' date_closing=null,';
1675  $sql .= ' fk_user_closing=null';
1676  $sql .= ' WHERE rowid = '.((int) $this->id);
1677 
1678  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1679  $resql = $this->db->query($sql);
1680  if ($resql) {
1681  // Call trigger
1682  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1683  if ($result < 0) {
1684  $error++;
1685  }
1686  // End call triggers
1687  } else {
1688  $error++;
1689  $this->error = $this->db->error();
1690  dol_print_error($this->db);
1691  }
1692 
1693  if (!$error) {
1694  $this->db->commit();
1695  return 1;
1696  } else {
1697  $this->db->rollback();
1698  return -1;
1699  }
1700  }
1701 
1712  public function setCanceled($user, $close_code = '', $close_note = '')
1713  {
1714  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1715 
1716  $this->db->begin();
1717 
1718  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1719  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1720  if ($close_code) {
1721  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1722  }
1723  if ($close_note) {
1724  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1725  }
1726  $sql .= " WHERE rowid = ".((int) $this->id);
1727 
1728  $resql = $this->db->query($sql);
1729  if ($resql) {
1730  // Bound discounts are deducted from the invoice
1731  // as they have not been used since the invoice is abandoned.
1732  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1733  $sql .= ' SET fk_invoice_supplier = NULL';
1734  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1735 
1736  $resql = $this->db->query($sql);
1737  if ($resql) {
1738  // Call trigger
1739  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1740  if ($result < 0) {
1741  $this->db->rollback();
1742  return -1;
1743  }
1744  // End call triggers
1745 
1746  $this->db->commit();
1747  return 1;
1748  } else {
1749  $this->error = $this->db->error()." sql=".$sql;
1750  $this->db->rollback();
1751  return -1;
1752  }
1753  } else {
1754  $this->error = $this->db->error()." sql=".$sql;
1755  $this->db->rollback();
1756  return -2;
1757  }
1758  }
1759 
1769  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1770  {
1771  global $conf, $langs;
1772 
1773  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1774 
1775  $now = dol_now();
1776 
1777  $error = 0;
1778  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1779 
1780  // Force to have object complete for checks
1781  $this->fetch_thirdparty();
1782  $this->fetch_lines();
1783 
1784  // Check parameters
1785  if ($this->statut > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1786  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1787  return 0;
1788  }
1789  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1790  $langs->load("errors");
1791  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1792  return -1;
1793  }
1794  if (count($this->lines) <= 0) {
1795  $langs->load("errors");
1796  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1797  return -1;
1798  }
1799 
1800  $this->db->begin();
1801 
1802  // Define new ref
1803  if ($force_number) {
1804  $num = $force_number;
1805  } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1806  $num = $this->getNextNumRef($this->thirdparty);
1807  } else {
1808  $num = $this->ref;
1809  }
1810  $this->newref = dol_sanitizeFileName($num);
1811 
1812  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1813  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1814  $sql .= " WHERE rowid = ".((int) $this->id);
1815 
1816  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1817  $resql = $this->db->query($sql);
1818  if ($resql) {
1819  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1820  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1821  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1822  $langs->load("agenda");
1823 
1824  $cpt = count($this->lines);
1825  for ($i = 0; $i < $cpt; $i++) {
1826  if ($this->lines[$i]->fk_product > 0) {
1827  $this->line = $this->lines[$i];
1828  $mouvP = new MouvementStock($this->db);
1829  $mouvP->origin = &$this;
1830  $mouvP->setOrigin($this->element, $this->id);
1831  // We increase stock for product
1832  $up_ht_disc = $this->lines[$i]->pu_ht;
1833  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1834  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1835  }
1837  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1838  } else {
1839  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1840  }
1841  if ($result < 0) {
1842  $error++;
1843  }
1844  unset($this->line);
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  $this->line = new SupplierInvoiceLine($this->db);
2178 
2179  $this->line->context = $this->context;
2180 
2181  $this->line->fk_facture_fourn = $this->id;
2182  //$this->line->label=$label; // deprecated
2183  $this->line->desc = $desc;
2184  $this->line->ref_supplier = $ref_supplier;
2185 
2186  $this->line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2187  $this->line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2188 
2189  $this->line->vat_src_code = $vat_src_code;
2190  $this->line->tva_tx = $txtva;
2191  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2192  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2193  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2194  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2195 
2196  $this->line->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  $this->line->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  $this->line->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  $this->line->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  $this->line->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  $this->line->fk_product = $fk_product;
2203  $this->line->product_type = $type;
2204  $this->line->remise_percent = $remise_percent;
2205  $this->line->date_start = $date_start;
2206  $this->line->date_end = $date_end;
2207  $this->line->fk_code_ventilation = $ventil;
2208  $this->line->rang = $rang;
2209  $this->line->info_bits = $info_bits;
2210  $this->line->fk_remise_except = $fk_remise_except;
2211 
2212  $this->line->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
2213  $this->line->fk_parent_line = $fk_parent_line;
2214  $this->line->origin = $this->origin;
2215  $this->line->origin_id = $origin_id;
2216  $this->line->fk_unit = $fk_unit;
2217 
2218  // Multicurrency
2219  $this->line->fk_multicurrency = $this->fk_multicurrency;
2220  $this->line->multicurrency_code = $this->multicurrency_code;
2221  $this->line->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  $this->line->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  $this->line->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  $this->line->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  $this->line->array_options = $array_options;
2229  }
2230 
2231  $result = $this->line->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 $this->line->id;
2248  } else {
2249  $this->error = $this->db->error();
2250  $this->db->rollback();
2251  return -1;
2252  }
2253  } else {
2254  $this->error = $this->line->error;
2255  $this->errors = $this->line->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 (empty($user->rights->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 
2703 
2717  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2718  {
2719  global $langs, $conf, $user, $hookmanager;
2720 
2721  $result = '';
2722 
2723  if ($option == 'withdraw') {
2724  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2725  } elseif ($option == 'document') {
2726  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2727  } else {
2728  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2729  }
2730 
2731  if ($short) {
2732  return $url;
2733  }
2734 
2735  if ($option !== 'nolink') {
2736  // Add param to save lastsearch_values or not
2737  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2738  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2739  $add_save_lastsearch_values = 1;
2740  }
2741  if ($add_save_lastsearch_values) {
2742  $url .= '&save_lastsearch_values=1';
2743  }
2744  }
2745 
2746  $picto = $this->picto;
2747  if ($this->type == self::TYPE_REPLACEMENT) {
2748  $picto .= 'r'; // Replacement invoice
2749  }
2750  if ($this->type == self::TYPE_CREDIT_NOTE) {
2751  $picto .= 'a'; // Credit note
2752  }
2753  if ($this->type == self::TYPE_DEPOSIT) {
2754  $picto .= 'd'; // Deposit invoice
2755  }
2756 
2757  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2758  if ($this->type == self::TYPE_REPLACEMENT) {
2759  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2760  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2761  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2762  } elseif ($this->type == self::TYPE_DEPOSIT) {
2763  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2764  }
2765  if (isset($this->status)) {
2766  $alreadypaid = -1;
2767  if (isset($this->alreadypaid)) {
2768  $alreadypaid = $this->alreadypaid;
2769  }
2770 
2771  $label .= ' '.$this->getLibStatut(5, $alreadypaid);
2772  }
2773  if (!empty($this->ref)) {
2774  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2775  }
2776  if (!empty($this->ref_supplier)) {
2777  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2778  }
2779  if (!empty($this->label)) {
2780  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2781  }
2782  if (!empty($this->date)) {
2783  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2784  }
2785  if (!empty($this->total_ht)) {
2786  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2787  }
2788  if (!empty($this->total_tva)) {
2789  $label .= '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2790  }
2791  if (!empty($this->total_ttc)) {
2792  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2793  }
2794  if ($moretitle) {
2795  $label .= ' - '.$moretitle;
2796  }
2797 
2798  $ref = $this->ref;
2799  if (empty($ref)) {
2800  $ref = $this->id;
2801  }
2802 
2803  $linkclose = '';
2804  if (empty($notooltip)) {
2805  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2806  $label = $langs->trans("ShowSupplierInvoice");
2807  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2808  }
2809  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2810  $linkclose .= ' class="classfortooltip"';
2811  }
2812 
2813  $linkstart = '<a href="'.$url.'"';
2814  $linkstart .= $linkclose.'>';
2815  $linkend = '</a>';
2816 
2817  $result .= $linkstart;
2818  if ($withpicto) {
2819  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2820  }
2821  if ($withpicto != 2) {
2822  $result .= ($max ?dol_trunc($ref, $max) : $ref);
2823  }
2824  $result .= $linkend;
2825 
2826  if ($addlinktonotes) {
2827  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2828  if ($txttoshow) {
2829  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2830  $result .= ' <span class="note inline-block">';
2831  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2832  $result .= img_picto('', 'note');
2833  $result .= '</a>';
2834  $result .= '</span>';
2835  }
2836  }
2837  global $action;
2838  $hookmanager->initHooks(array($this->element . 'dao'));
2839  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2840  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2841  if ($reshook > 0) {
2842  $result = $hookmanager->resPrint;
2843  } else {
2844  $result .= $hookmanager->resPrint;
2845  }
2846  return $result;
2847  }
2848 
2857  public function getNextNumRef($soc, $mode = 'next')
2858  {
2859  global $db, $langs, $conf;
2860  $langs->load("orders");
2861 
2862  // Clean parameters (if not defined or using deprecated value)
2863  if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) {
2864  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2865  }
2866 
2867  $mybool = false;
2868 
2869  $file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2870  $classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2871 
2872  // Include file with class
2873  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2874 
2875  foreach ($dirmodels as $reldir) {
2876  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2877 
2878  // Load file with numbering class (if found)
2879  $mybool |= @include_once $dir.$file;
2880  }
2881 
2882  if ($mybool === false) {
2883  dol_print_error('', "Failed to include file ".$file);
2884  return '';
2885  }
2886 
2887  $obj = new $classname();
2888  $numref = "";
2889  $numref = $obj->getNumRef($soc, $this, $mode);
2890 
2891  if ($numref != "") {
2892  return $numref;
2893  } else {
2894  $this->error = $obj->error;
2895  return -1;
2896  }
2897  }
2898 
2899 
2908  public function initAsSpecimen($option = '')
2909  {
2910  global $langs, $conf;
2911  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2912 
2913  $now = dol_now();
2914 
2915  // Load array of products prodids
2916  $num_prods = 0;
2917  $prodids = array();
2918 
2919  $sql = "SELECT rowid";
2920  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2921  $sql .= " WHERE entity IN (".getEntity('product').")";
2922  $sql .= $this->db->plimit(100);
2923 
2924  $resql = $this->db->query($sql);
2925  if ($resql) {
2926  $num_prods = $this->db->num_rows($resql);
2927  $i = 0;
2928  while ($i < $num_prods) {
2929  $i++;
2930  $row = $this->db->fetch_row($resql);
2931  $prodids[$i] = $row[0];
2932  }
2933  }
2934 
2935  // Initialise parametres
2936  $this->id = 0;
2937  $this->ref = 'SPECIMEN';
2938  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
2939  $this->specimen = 1;
2940  $this->socid = 1;
2941  $this->date = $now;
2942  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2943  $this->cond_reglement_code = 'RECEP';
2944  $this->mode_reglement_code = 'CHQ';
2945 
2946  $this->note_public = 'This is a comment (public)';
2947  $this->note_private = 'This is a comment (private)';
2948 
2949  $this->multicurrency_tx = 1;
2950  $this->multicurrency_code = $conf->currency;
2951 
2952  $xnbp = 0;
2953  if (empty($option) || $option != 'nolines') {
2954  // Lines
2955  $nbp = 5;
2956  while ($xnbp < $nbp) {
2957  $line = new SupplierInvoiceLine($this->db);
2958  $line->desc = $langs->trans("Description")." ".$xnbp;
2959  $line->qty = 1;
2960  $line->subprice = 100;
2961  $line->pu_ht = 100; // the canelle template use pu_ht and not subprice
2962  $line->price = 100;
2963  $line->tva_tx = 19.6;
2964  $line->localtax1_tx = 0;
2965  $line->localtax2_tx = 0;
2966  if ($xnbp == 2) {
2967  $line->total_ht = 50;
2968  $line->total_ttc = 59.8;
2969  $line->total_tva = 9.8;
2970  $line->remise_percent = 50;
2971  } else {
2972  $line->total_ht = 100;
2973  $line->total_ttc = 119.6;
2974  $line->total_tva = 19.6;
2975  $line->remise_percent = 0;
2976  }
2977 
2978  if ($num_prods > 0) {
2979  $prodid = mt_rand(1, $num_prods);
2980  $line->fk_product = $prodids[$prodid];
2981  }
2982  $line->product_type = 0;
2983 
2984  $this->lines[$xnbp] = $line;
2985 
2986  $this->total_ht += $line->total_ht;
2987  $this->total_tva += $line->total_tva;
2988  $this->total_ttc += $line->total_ttc;
2989 
2990  $xnbp++;
2991  }
2992  }
2993 
2994  $this->amount_ht = $xnbp * 100;
2995  $this->total_ht = $xnbp * 100;
2996  $this->total_tva = $xnbp * 19.6;
2997  $this->total_ttc = $xnbp * 119.6;
2998  }
2999 
3000  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3006  public function load_state_board()
3007  {
3008  // phpcs:enable
3009  global $conf, $user;
3010 
3011  $this->nb = array();
3012 
3013  $clause = "WHERE";
3014 
3015  $sql = "SELECT count(f.rowid) as nb";
3016  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3017  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3018  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3019  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3020  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3021  $clause = "AND";
3022  }
3023  $sql .= " ".$clause." f.entity = ".$conf->entity;
3024 
3025  $resql = $this->db->query($sql);
3026  if ($resql) {
3027  while ($obj = $this->db->fetch_object($resql)) {
3028  $this->nb["supplier_invoices"] = $obj->nb;
3029  }
3030  $this->db->free($resql);
3031  return 1;
3032  } else {
3033  dol_print_error($this->db);
3034  $this->error = $this->db->error();
3035  return -1;
3036  }
3037  }
3038 
3047  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3048  {
3049  global $conf, $langs;
3050 
3051  $error = 0;
3052 
3053  $object = new FactureFournisseur($this->db);
3054 
3055  $this->db->begin();
3056 
3057  // Load source object
3058  $object->fetch($fromid);
3059  $object->id = 0;
3060  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3061  $object->status = self::STATUS_DRAFT;
3062 
3063  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3064 
3065  // Clear fields
3066  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3067  $object->author = $user->id;
3068  $object->user_valid = 0;
3069  $object->fk_facture_source = 0;
3070  $object->date_creation = '';
3071  $object->date_validation = '';
3072  $object->date = (empty($this->date) ? '' : $this->date);
3073  $object->date_echeance = '';
3074  $object->ref_client = '';
3075  $object->close_code = '';
3076  $object->close_note = '';
3077  if ($conf->global->MAIN_DONT_KEEP_NOTE_ON_CLONING == 1) {
3078  $object->note_private = '';
3079  $object->note_public = '';
3080  }
3081 
3082  // Loop on each line of new invoice
3083  foreach ($object->lines as $i => $line) {
3084  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3085  unset($object->lines[$i]);
3086  }
3087  }
3088 
3089  // Create clone
3090  $object->context['createfromclone'] = 'createfromclone';
3091  $result = $object->create($user);
3092 
3093  // Other options
3094  if ($result < 0) {
3095  $this->error = $object->error;
3096  $this->errors = $object->errors;
3097  $error++;
3098  }
3099 
3100  if (!$error) {
3101  }
3102 
3103  unset($object->context['createfromclone']);
3104 
3105  // End
3106  if (!$error) {
3107  $this->db->commit();
3108  return $object->id;
3109  } else {
3110  $this->db->rollback();
3111  return -1;
3112  }
3113  }
3114 
3126  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3127  {
3128  global $conf, $user, $langs;
3129 
3130  $langs->load("suppliers");
3131  $outputlangs->load("products");
3132 
3133  // Set the model on the model name to use
3134  if (empty($modele)) {
3135  if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF)) {
3136  $modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
3137  } else {
3138  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3139  }
3140  }
3141 
3142  if (empty($modele)) {
3143  return 0;
3144  } else {
3145  $modelpath = "core/modules/supplier_invoice/doc/";
3146 
3147  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3148  }
3149  }
3150 
3155  public function getRights()
3156  {
3157  global $user;
3158 
3159  return $user->rights->fournisseur->facture;
3160  }
3161 
3170  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3171  {
3172  $tables = array(
3173  'facture_fourn'
3174  );
3175 
3176  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3177  }
3178 
3187  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3188  {
3189  $tables = array(
3190  'facture_fourn_det'
3191  );
3192 
3193  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3194  }
3195 
3201  public function hasDelay()
3202  {
3203  global $conf;
3204 
3205  $now = dol_now();
3206 
3207  if (!$this->date_echeance) {
3208  return false;
3209  }
3210 
3211  $status = isset($this->status) ? $this->status : $this->statut;
3212 
3213  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3214  }
3215 
3221  public function isCreditNoteUsed()
3222  {
3223  $isUsed = false;
3224 
3225  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3226  $resql = $this->db->query($sql);
3227  if (!empty($resql)) {
3228  $obj = $this->db->fetch_object($resql);
3229  if (!empty($obj->fk_invoice_supplier)) {
3230  $isUsed = true;
3231  }
3232  }
3233 
3234  return $isUsed;
3235  }
3236 }
3237 
3238 
3239 
3244 {
3248  public $element = 'facture_fourn_det';
3249 
3253  public $table_element = 'facture_fourn_det';
3254 
3255  public $oldline;
3256 
3261  public $ref;
3262 
3267  public $product_ref;
3268 
3274  public $ref_supplier;
3275 
3280  public $product_desc;
3281 
3288  public $pu_ht;
3289 
3294  public $subprice;
3295 
3300  public $pu_ttc;
3301 
3302 
3307  public $fk_facture_fourn;
3308 
3314  public $label;
3315 
3320  public $description;
3321 
3322  public $date_start;
3323  public $date_end;
3324 
3325  public $skip_update_total; // Skip update price total for special lines
3326 
3330  public $situation_percent;
3331 
3335  public $fk_prev_id;
3336 
3341  public $vat_src_code;
3342 
3347  public $tva_tx;
3348 
3353  public $localtax1_tx;
3354 
3359  public $localtax2_tx;
3360 
3365  public $qty;
3366 
3371  public $remise_percent;
3372 
3377  public $total_ht;
3378 
3383  public $total_ttc;
3384 
3389  public $total_tva;
3390 
3395  public $total_localtax1;
3396 
3401  public $total_localtax2;
3402 
3406  public $fk_product;
3407 
3412  public $product_type;
3413 
3418  public $product_label;
3419 
3426  public $info_bits;
3427 
3432  public $fk_remise_except;
3433 
3437  public $fk_parent_line;
3438 
3439  public $special_code;
3440 
3444  public $rang;
3445 
3450  public $localtax1_type;
3451 
3456  public $localtax2_type;
3457 
3458  // Multicurrency
3462  public $fk_multicurrency;
3463 
3464  public $multicurrency_code;
3465  public $multicurrency_subprice;
3466  public $multicurrency_total_ht;
3467  public $multicurrency_total_tva;
3468  public $multicurrency_total_ttc;
3469 
3470 
3476  public function __construct($db)
3477  {
3478  $this->db = $db;
3479  }
3480 
3487  public function fetch($rowid)
3488  {
3489  $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';
3490  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3491  $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';
3492  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3493  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3494  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3495  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3496  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3497  $sql .= ' ORDER BY f.rang, f.rowid';
3498 
3499  $query = $this->db->query($sql);
3500 
3501  if (!$query) {
3502  $this->errors[] = $this->db->error();
3503  return -1;
3504  }
3505 
3506  if (!$this->db->num_rows($query)) {
3507  return 0;
3508  }
3509 
3510  $obj = $this->db->fetch_object($query);
3511 
3512  $this->id = $obj->rowid;
3513  $this->rowid = $obj->rowid;
3514  $this->fk_facture_fourn = $obj->fk_facture_fourn;
3515  $this->description = $obj->description;
3516  $this->date_start = $obj->date_start;
3517  $this->date_end = $obj->date_end;
3518  $this->product_ref = $obj->product_ref;
3519  $this->ref_supplier = $obj->ref_supplier;
3520  $this->product_desc = $obj->product_desc;
3521 
3522  $this->subprice = $obj->pu_ht;
3523  $this->pu_ht = $obj->pu_ht;
3524  $this->pu_ttc = $obj->pu_ttc;
3525  $this->tva_tx = $obj->tva_tx;
3526  $this->localtax1_tx = $obj->localtax1_tx;
3527  $this->localtax2_tx = $obj->localtax2_tx;
3528  $this->localtax1_type = $obj->localtax1_type;
3529  $this->localtax2_type = $obj->localtax2_type;
3530 
3531  $this->qty = $obj->qty;
3532  $this->remise_percent = $obj->remise_percent;
3533  $this->fk_remise_except = $obj->fk_remise_except;
3534  //$this->tva = $obj->total_tva; // deprecated
3535  $this->total_ht = $obj->total_ht;
3536  $this->total_tva = $obj->total_tva;
3537  $this->total_localtax1 = $obj->total_localtax1;
3538  $this->total_localtax2 = $obj->total_localtax2;
3539  $this->total_ttc = $obj->total_ttc;
3540  $this->fk_product = $obj->fk_product;
3541  $this->product_type = $obj->product_type;
3542  $this->product_label = $obj->product_label;
3543  $this->info_bits = $obj->info_bits;
3544  $this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3545  $this->fk_parent_line = $obj->fk_parent_line;
3546  $this->special_code = $obj->special_code;
3547  $this->rang = $obj->rang;
3548  $this->fk_unit = $obj->fk_unit;
3549 
3550  $this->multicurrency_subprice = $obj->multicurrency_subprice;
3551  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3552  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3553  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3554 
3555  $this->fetch_optionals();
3556 
3557  return 1;
3558  }
3559 
3566  public function delete($notrigger = 0)
3567  {
3568  global $user, $conf;
3569 
3570  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
3571 
3572  $error = 0;
3573 
3574  $this->db->begin();
3575 
3576  if (!$notrigger) {
3577  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3578  $error++;
3579  }
3580  }
3581 
3582  $this->deleteObjectLinked();
3583 
3584  // Remove extrafields
3585  if (!$error) {
3586  $result = $this->deleteExtraFields();
3587  if ($result < 0) {
3588  $error++;
3589  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3590  }
3591  }
3592 
3593  if (!$error) {
3594  // Supprime ligne
3595  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3596  $sql .= " WHERE rowid = ".((int) $this->id);
3597  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3598  $resql = $this->db->query($sql);
3599  if (!$resql) {
3600  $error++;
3601  $this->error = $this->db->lasterror();
3602  }
3603  }
3604 
3605  if (!$error) {
3606  $this->db->commit();
3607  return 1;
3608  } else {
3609  $this->db->rollback();
3610  return -1;
3611  }
3612  }
3613 
3620  public function update($notrigger = 0)
3621  {
3622  global $conf;
3623 
3624  $pu = price2num($this->pu_ht);
3625  $qty = price2num($this->qty);
3626 
3627  // Check parameters
3628  if (empty($this->qty)) {
3629  $this->qty = 0;
3630  }
3631 
3632  if ($this->product_type < 0) {
3633  return -1;
3634  }
3635 
3636  // Clean parameters
3637  if (empty($this->remise_percent)) {
3638  $this->remise_percent = 0;
3639  }
3640  if (empty($this->tva_tx)) {
3641  $this->tva_tx = 0;
3642  }
3643  if (empty($this->localtax1_tx)) {
3644  $this->localtax1_tx = 0;
3645  }
3646  if (empty($this->localtax2_tx)) {
3647  $this->localtax2_tx = 0;
3648  }
3649 
3650  if (empty($this->pa_ht)) {
3651  $this->pa_ht = 0;
3652  }
3653  if (empty($this->multicurrency_subprice)) {
3654  $this->multicurrency_subprice = 0;
3655  }
3656  if (empty($this->multicurrency_total_ht)) {
3657  $this->multicurrency_total_ht = 0;
3658  }
3659  if (empty($this->multicurrency_total_tva)) {
3660  $this->multicurrency_total_tva = 0;
3661  }
3662  if (empty($this->multicurrency_total_ttc)) {
3663  $this->multicurrency_total_ttc = 0;
3664  }
3665 
3666  $fk_product = (int) $this->fk_product;
3667  $fk_unit = (int) $this->fk_unit;
3668 
3669  $this->db->begin();
3670 
3671  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3672  $sql .= " description = '".$this->db->escape($this->description)."'";
3673  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3674  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3675  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3676  $sql .= ", pu_ht = ".price2num($this->pu_ht);
3677  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3678  $sql .= ", qty = ".price2num($this->qty);
3679  $sql .= ", remise_percent = ".price2num($this->remise_percent);
3680  if ($this->fk_remise_except > 0) $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
3681  else $sql .= ", fk_remise_except=null";
3682  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3683  $sql .= ", tva_tx = ".price2num($this->tva_tx);
3684  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3685  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3686  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3687  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3688  $sql .= ", total_ht = ".price2num($this->total_ht);
3689  $sql .= ", tva= ".price2num($this->total_tva);
3690  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3691  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3692  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3693  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
3694  $sql .= ", product_type = ".((int) $this->product_type);
3695  $sql .= ", info_bits = ".((int) $this->info_bits);
3696  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
3697 
3698  if (!empty($this->rang)) {
3699  $sql .= ", rang=".((int) $this->rang);
3700  }
3701 
3702  // Multicurrency
3703  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3704  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3705  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3706  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3707 
3708  $sql .= " WHERE rowid = ".((int) $this->id);
3709 
3710  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3711  $resql = $this->db->query($sql);
3712 
3713  if (!$resql) {
3714  $this->db->rollback();
3715  $this->error = $this->db->lasterror();
3716  return -1;
3717  }
3718 
3719  $this->rowid = $this->id;
3720  $error = 0;
3721 
3722  if (!$error) {
3723  $result = $this->insertExtraFields();
3724  if ($result < 0) {
3725  $error++;
3726  }
3727  }
3728 
3729  if (!$error && !$notrigger) {
3730  global $langs, $user;
3731 
3732  // Call trigger
3733  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
3734  $this->db->rollback();
3735  return -1;
3736  }
3737  // End call triggers
3738  }
3739 
3740  if ($error) {
3741  $this->db->rollback();
3742  return -1;
3743  }
3744 
3745  $this->db->commit();
3746  return 1;
3747  }
3748 
3755  public function insert($notrigger = 0)
3756  {
3757  global $user, $conf, $langs;
3758 
3759  $error = 0;
3760 
3761  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3762 
3763  // Clean parameters
3764  $this->desc = trim($this->desc);
3765  if (empty($this->tva_tx)) {
3766  $this->tva_tx = 0;
3767  }
3768  if (empty($this->localtax1_tx)) {
3769  $this->localtax1_tx = 0;
3770  }
3771  if (empty($this->localtax2_tx)) {
3772  $this->localtax2_tx = 0;
3773  }
3774  if (empty($this->localtax1_type)) {
3775  $this->localtax1_type = '0';
3776  }
3777  if (empty($this->localtax2_type)) {
3778  $this->localtax2_type = '0';
3779  }
3780  if (empty($this->total_tva)) {
3781  $this->total_tva = 0;
3782  }
3783  if (empty($this->total_localtax1)) {
3784  $this->total_localtax1 = 0;
3785  }
3786  if (empty($this->total_localtax2)) {
3787  $this->total_localtax2 = 0;
3788  }
3789  if (empty($this->rang)) {
3790  $this->rang = 0;
3791  }
3792  if (empty($this->remise_percent)) {
3793  $this->remise_percent = 0;
3794  }
3795  if (empty($this->info_bits)) {
3796  $this->info_bits = 0;
3797  }
3798  if (empty($this->subprice)) {
3799  $this->subprice = 0;
3800  }
3801  if (empty($this->special_code)) {
3802  $this->special_code = 0;
3803  }
3804  if (empty($this->fk_parent_line)) {
3805  $this->fk_parent_line = 0;
3806  }
3807  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
3808  $this->situation_percent = 100;
3809  }
3810 
3811  if (empty($this->pa_ht)) {
3812  $this->pa_ht = 0;
3813  }
3814  if (empty($this->multicurrency_subprice)) {
3815  $this->multicurrency_subprice = 0;
3816  }
3817  if (empty($this->multicurrency_total_ht)) {
3818  $this->multicurrency_total_ht = 0;
3819  }
3820  if (empty($this->multicurrency_total_tva)) {
3821  $this->multicurrency_total_tva = 0;
3822  }
3823  if (empty($this->multicurrency_total_ttc)) {
3824  $this->multicurrency_total_ttc = 0;
3825  }
3826 
3827 
3828  // Check parameters
3829  if ($this->product_type < 0) {
3830  $this->error = 'ErrorProductTypeMustBe0orMore';
3831  return -1;
3832  }
3833  if (!empty($this->fk_product) && $this->fk_product > 0) {
3834  // Check product exists
3835  $result = Product::isExistingObject('product', $this->fk_product);
3836  if ($result <= 0) {
3837  $this->error = 'ErrorProductIdDoesNotExists';
3838  return -1;
3839  }
3840  }
3841 
3842  $this->db->begin();
3843 
3844  // Insertion dans base de la ligne
3845  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3846  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3847  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3848  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
3849  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3850  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3851  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3852  $sql .= ')';
3853  $sql .= " VALUES (".$this->fk_facture_fourn.",";
3854  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3855  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3856  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3857  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
3858  $sql .= " ".price2num($this->qty).",";
3859 
3860  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3861  $sql .= " ".price2num($this->tva_tx).",";
3862  $sql .= " ".price2num($this->localtax1_tx).",";
3863  $sql .= " ".price2num($this->localtax2_tx).",";
3864  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3865  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3866  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
3867  $sql .= " ".((int) $this->product_type).",";
3868  $sql .= " ".price2num($this->remise_percent).",";
3869  $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
3870  $sql .= " ".price2num($this->subprice).",";
3871  $sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
3872  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3873  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3874  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
3875  $sql .= ' '.((int) $this->rang).',';
3876  $sql .= ' '.((int) $this->special_code).',';
3877  $sql .= " ".((int) $this->info_bits).",";
3878  $sql .= " ".price2num($this->total_ht).",";
3879  $sql .= " ".price2num($this->total_tva).",";
3880  $sql .= " ".price2num($this->total_ttc).",";
3881  $sql .= " ".price2num($this->total_localtax1).",";
3882  $sql .= " ".price2num($this->total_localtax2);
3883  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3884  $sql .= ", ".(int) $this->fk_multicurrency;
3885  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3886  $sql .= ", ".price2num($this->multicurrency_subprice);
3887  $sql .= ", ".price2num($this->multicurrency_total_ht);
3888  $sql .= ", ".price2num($this->multicurrency_total_tva);
3889  $sql .= ", ".price2num($this->multicurrency_total_ttc);
3890  $sql .= ')';
3891 
3892  $resql = $this->db->query($sql);
3893  if ($resql) {
3894  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3895  $this->rowid = $this->id; // backward compatibility
3896 
3897  if (!$error) {
3898  $result = $this->insertExtraFields();
3899  if ($result < 0) {
3900  $error++;
3901  }
3902  }
3903 
3904  // Si fk_remise_except defini, on lie la remise a la facture
3905  // ce qui la flague comme "consommee".
3906  if ($this->fk_remise_except) {
3907  $discount = new DiscountAbsolute($this->db);
3908  $result = $discount->fetch($this->fk_remise_except);
3909  if ($result >= 0) {
3910  // Check if discount was found
3911  if ($result > 0) {
3912  // Check if discount not already affected to another invoice
3913  if ($discount->fk_facture_line > 0) {
3914  if (empty($noerrorifdiscountalreadylinked)) {
3915  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
3916  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3917  $this->db->rollback();
3918  return -3;
3919  }
3920  } else {
3921  $result = $discount->link_to_invoice($this->rowid, 0);
3922  if ($result < 0) {
3923  $this->error = $discount->error;
3924  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3925  $this->db->rollback();
3926  return -3;
3927  }
3928  }
3929  } else {
3930  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
3931  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3932  $this->db->rollback();
3933  return -3;
3934  }
3935  } else {
3936  $this->error = $discount->error;
3937  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3938  $this->db->rollback();
3939  return -3;
3940  }
3941  }
3942 
3943  if (!$error && !$notrigger) {
3944  // Call trigger
3945  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
3946  if ($result < 0) {
3947  $this->db->rollback();
3948  return -2;
3949  }
3950  // End call triggers
3951  }
3952 
3953  $this->db->commit();
3954  return $this->id;
3955  } else {
3956  $this->error = $this->db->error();
3957  $this->db->rollback();
3958  return -2;
3959  }
3960  }
3961 
3962  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3968  public function update_total()
3969  {
3970  // phpcs:enable
3971  $this->db->begin();
3972 
3973  // Mise a jour ligne en base
3974  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3975  $sql .= " total_ht = ".price2num($this->total_ht);
3976  $sql .= ", tva= ".price2num($this->total_tva);
3977  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
3978  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
3979  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3980  $sql .= " WHERE rowid = ".((int) $this->rowid);
3981 
3982  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
3983 
3984  $resql = $this->db->query($sql);
3985  if ($resql) {
3986  $this->db->commit();
3987  return 1;
3988  } else {
3989  $this->error = $this->db->error();
3990  $this->db->rollback();
3991  return -2;
3992  }
3993  }
3994 }
$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.
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)
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.
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:45
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:121
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:1401
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:1250
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:61
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
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.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
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.
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.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
div float
Buy price without taxes.
Definition: style.css.php:913
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:119
$conf db
API class for accounts.
Definition: inc.php:41