dolibarr  17.0.4
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->socid = $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  dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1129  return -3;
1130  }
1131  }
1132 
1133 
1141  public function update($user = null, $notrigger = 0)
1142  {
1143  global $conf, $langs;
1144  $error = 0;
1145 
1146  // Clean parameters
1147  if (empty($this->type)) {
1148  $this->type = self::TYPE_STANDARD;
1149  }
1150  if (isset($this->ref)) {
1151  $this->ref = trim($this->ref);
1152  }
1153  if (isset($this->ref_supplier)) {
1154  $this->ref_supplier = trim($this->ref_supplier);
1155  }
1156  if (isset($this->ref_ext)) {
1157  $this->ref_ext = trim($this->ref_ext);
1158  }
1159  if (isset($this->entity)) {
1160  $this->entity = trim($this->entity);
1161  }
1162  if (isset($this->type)) {
1163  $this->type = trim($this->type);
1164  }
1165  if (isset($this->socid)) {
1166  $this->socid = trim($this->socid);
1167  }
1168  if (isset($this->label)) {
1169  $this->label = trim($this->label);
1170  }
1171  if (isset($this->libelle)) {
1172  $this->libelle = trim($this->libelle); // deprecated
1173  }
1174  if (isset($this->paye)) {
1175  $this->paye = trim($this->paye);
1176  }
1177  if (isset($this->close_code)) {
1178  $this->close_code = trim($this->close_code);
1179  }
1180  if (isset($this->close_note)) {
1181  $this->close_note = trim($this->close_note);
1182  }
1183  if (isset($this->localtax1)) {
1184  $this->localtax1 = trim($this->localtax1);
1185  }
1186  if (isset($this->localtax2)) {
1187  $this->localtax2 = trim($this->localtax2);
1188  }
1189  if (empty($this->total_ht)) {
1190  $this->total_ht = 0;
1191  }
1192  if (empty($this->total_tva)) {
1193  $this->total_tva = 0;
1194  }
1195  // if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
1196  // if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
1197  if (isset($this->total_ttc)) {
1198  $this->total_ttc = trim($this->total_ttc);
1199  }
1200  if (isset($this->statut)) {
1201  $this->statut = (int) $this->statut;
1202  }
1203  if (isset($this->status)) {
1204  $this->status = (int) $this->status;
1205  }
1206  if (isset($this->author)) {
1207  $this->author = trim($this->author);
1208  }
1209  if (isset($this->fk_user_valid)) {
1210  $this->fk_user_valid = trim($this->fk_user_valid);
1211  }
1212  if (isset($this->fk_facture_source)) {
1213  $this->fk_facture_source = trim($this->fk_facture_source);
1214  }
1215  if (isset($this->fk_project)) {
1216  if (empty($this->fk_project)) $this->fk_project = null;
1217  else $this->fk_project = intval($this->fk_project);
1218  }
1219  if (isset($this->cond_reglement_id)) {
1220  $this->cond_reglement_id = trim($this->cond_reglement_id);
1221  }
1222  if (isset($this->note_private)) {
1223  $this->note = trim($this->note_private);
1224  }
1225  if (isset($this->note_public)) {
1226  $this->note_public = trim($this->note_public);
1227  }
1228  if (isset($this->model_pdf)) {
1229  $this->model_pdf = trim($this->model_pdf);
1230  }
1231  if (isset($this->import_key)) {
1232  $this->import_key = trim($this->import_key);
1233  }
1234 
1235 
1236  // Check parameters
1237  // Put here code to add control on parameters values
1238 
1239  // Update request
1240  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1241  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1242  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1243  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1244  $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1245  $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1246  $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1247  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1248  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1249  if (dol_strlen($this->tms) != 0) {
1250  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1251  }
1252  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1253  $sql .= " paye=".(isset($this->paye) ? ((int) $this->paye) : "0").",";
1254  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1255  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1256  $sql .= " localtax1=".(isset($this->localtax1) ? ((float) $this->localtax1) : "null").",";
1257  $sql .= " localtax2=".(isset($this->localtax2) ? ((float) $this->localtax2) : "null").",";
1258  $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1259  $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1260  $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1261  $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1262  $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1263  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1264  $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1265  $sql .= " fk_projet=".(! empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1266  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1267  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1268  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1269  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1270  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1271  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1272  $sql .= " WHERE rowid=".((int) $this->id);
1273 
1274  $this->db->begin();
1275 
1276  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1277  $resql = $this->db->query($sql);
1278 
1279  if (!$resql) {
1280  $error++;
1281 
1282  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1283  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1284  } else {
1285  $this->errors[] = "Error ".$this->db->lasterror();
1286  }
1287  }
1288 
1289  if (!$error) {
1290  $result = $this->insertExtraFields();
1291  if ($result < 0) {
1292  $error++;
1293  }
1294  }
1295 
1296  if (!$error) {
1297  if (!$notrigger) {
1298  // Call trigger
1299  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1300  if ($result < 0) {
1301  $error++;
1302  }
1303  // End call triggers
1304  }
1305  }
1306 
1307  // Commit or rollback
1308  if ($error) {
1309  foreach ($this->errors as $errmsg) {
1310  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1311  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1312  }
1313  $this->db->rollback();
1314  return -1 * $error;
1315  } else {
1316  $this->db->commit();
1317  return 1;
1318  }
1319  }
1320 
1321  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1328  public function insert_discount($idremise)
1329  {
1330  // phpcs:enable
1331  global $conf, $langs;
1332 
1333  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1334  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1335 
1336  $this->db->begin();
1337 
1338  $remise = new DiscountAbsolute($this->db);
1339  $result = $remise->fetch($idremise);
1340 
1341  if ($result > 0) {
1342  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1343  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1344  $this->db->rollback();
1345  return -5;
1346  }
1347 
1348  $facligne = new SupplierInvoiceLine($this->db);
1349  $facligne->fk_facture_fourn = $this->id;
1350  $facligne->fk_remise_except = $remise->id;
1351  $facligne->desc = $remise->description; // Description ligne
1352  $facligne->vat_src_code = $remise->vat_src_code;
1353  $facligne->tva_tx = $remise->tva_tx;
1354  $facligne->subprice = -$remise->amount_ht;
1355  $facligne->fk_product = 0; // Id produit predefini
1356  $facligne->product_type = 0;
1357  $facligne->qty = 1;
1358  $facligne->remise_percent = 0;
1359  $facligne->rang = -1;
1360  $facligne->info_bits = 2;
1361 
1362  if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
1363  $facligne->rang = 1;
1364  $linecount = count($this->lines);
1365  for ($ii = 1; $ii <= $linecount; $ii++) {
1366  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii+1);
1367  }
1368  }
1369 
1370  // Get buy/cost price of invoice that is source of discount
1371  if ($remise->fk_invoice_supplier_source > 0) {
1372  $srcinvoice = new FactureFournisseur($this->db);
1373  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1374  $totalcostpriceofinvoice = 0;
1375  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1376  $formmargin = new FormMargin($this->db);
1377  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1378  $facligne->pa_ht = $arraytmp['pa_total'];
1379  }
1380 
1381  $facligne->total_ht = -$remise->amount_ht;
1382  $facligne->total_tva = -$remise->amount_tva;
1383  $facligne->total_ttc = -$remise->amount_ttc;
1384 
1385  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1386  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1387  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1388  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1389 
1390  $lineid = $facligne->insert();
1391  if ($lineid > 0) {
1392  $result = $this->update_price(1);
1393  if ($result > 0) {
1394  // Create link between discount and invoice line
1395  $result = $remise->link_to_invoice($lineid, 0);
1396  if ($result < 0) {
1397  $this->error = $remise->error;
1398  $this->db->rollback();
1399  return -4;
1400  }
1401 
1402  $this->db->commit();
1403  return 1;
1404  } else {
1405  $this->error = $facligne->error;
1406  $this->db->rollback();
1407  return -1;
1408  }
1409  } else {
1410  $this->error = $facligne->error;
1411  $this->db->rollback();
1412  return -2;
1413  }
1414  } else {
1415  $this->db->rollback();
1416  return -3;
1417  }
1418  }
1419 
1420 
1428  public function delete(User $user, $notrigger = 0)
1429  {
1430  global $langs, $conf;
1431 
1432  $rowid = $this->id;
1433 
1434  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1435 
1436  // TODO Test if there is at least on payment. If yes, refuse to delete.
1437 
1438  $error = 0;
1439  $this->db->begin();
1440 
1441  if (!$error && !$notrigger) {
1442  // Call trigger
1443  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1444  if ($result < 0) {
1445  $this->db->rollback();
1446  return -1;
1447  }
1448  // Fin appel triggers
1449  }
1450 
1451  if (!$error) {
1452  // If invoice was converted into a discount not yet consumed, we remove discount
1453  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1454  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1455  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1456  $resql = $this->db->query($sql);
1457 
1458  // If invoice has consumned discounts
1459  $this->fetch_lines();
1460  $list_rowid_det = array();
1461  foreach ($this->lines as $key => $invoiceline) {
1462  $list_rowid_det[] = $invoiceline->rowid;
1463  }
1464 
1465  // Consumned discounts are freed
1466  if (count($list_rowid_det)) {
1467  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1468  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1469  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(join(',', $list_rowid_det)).')';
1470 
1471  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1472  if (!$this->db->query($sql)) {
1473  $error++;
1474  }
1475  }
1476  }
1477 
1478  if (!$error) {
1479  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1480  $ef = $main."_extrafields";
1481  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1482  $resqlef = $this->db->query($sqlef);
1483  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1484  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1485  $resql = $this->db->query($sql);
1486  if ($resqlef && $resql) {
1487  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1488  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1489  $resql2 = $this->db->query($sql);
1490  if (!$resql2) {
1491  $error++;
1492  }
1493  } else {
1494  $error++;
1495  }
1496  }
1497 
1498  if (!$error) {
1499  // Delete linked object
1500  $res = $this->deleteObjectLinked();
1501  if ($res < 0) {
1502  $error++;
1503  }
1504  }
1505 
1506  if (!$error) {
1507  // Delete linked object
1508  $res = $this->deleteObjectLinked();
1509  if ($res < 0) {
1510  $error++;
1511  }
1512  }
1513 
1514  if (!$error) {
1515  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1516  $this->deleteEcmFiles();
1517 
1518  // We remove directory
1519  if ($conf->fournisseur->facture->dir_output) {
1520  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1521 
1522  $ref = dol_sanitizeFileName($this->ref);
1523  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1524  $file = $dir."/".$ref.".pdf";
1525  if (file_exists($file)) {
1526  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1527  $this->error = 'ErrorFailToDeleteFile';
1528  $error++;
1529  }
1530  }
1531  if (file_exists($dir)) {
1532  $res = @dol_delete_dir_recursive($dir);
1533 
1534  if (!$res) {
1535  $this->error = 'ErrorFailToDeleteDir';
1536  $error++;
1537  }
1538  }
1539  }
1540  }
1541 
1542  // Remove extrafields
1543  if (!$error) {
1544  $result = $this->deleteExtraFields();
1545  if ($result < 0) {
1546  $error++;
1547  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1548  }
1549  }
1550 
1551  if (!$error) {
1552  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1553  $this->db->commit();
1554  return 1;
1555  } else {
1556  $this->error = $this->db->lasterror();
1557  $this->db->rollback();
1558  return -$error;
1559  }
1560  }
1561 
1562 
1563  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1574  public function set_paid($user, $close_code = '', $close_note = '')
1575  {
1576  // phpcs:enable
1577  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1578  return $this->setPaid($user, $close_code, $close_note);
1579  }
1580 
1589  public function setPaid($user, $close_code = '', $close_note = '')
1590  {
1591  $error = 0;
1592 
1593  if ($this->paye != 1) {
1594  $this->db->begin();
1595 
1596  $now = dol_now();
1597 
1598  dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1599 
1600  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1601  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1602  if (!$close_code) {
1603  $sql .= ', paye=1';
1604  }
1605  if ($close_code) {
1606  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1607  }
1608  if ($close_note) {
1609  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1610  }
1611  $sql .= ', fk_user_closing = '.((int) $user->id);
1612  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1613  $sql .= ' WHERE rowid = '.((int) $this->id);
1614 
1615  $resql = $this->db->query($sql);
1616  if ($resql) {
1617  // Call trigger
1618  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1619  if ($result < 0) {
1620  $error++;
1621  }
1622  // End call triggers
1623  } else {
1624  $error++;
1625  $this->error = $this->db->error();
1626  dol_print_error($this->db);
1627  }
1628 
1629  if (!$error) {
1630  $this->db->commit();
1631  return 1;
1632  } else {
1633  $this->db->rollback();
1634  return -1;
1635  }
1636  } else {
1637  return 0;
1638  }
1639  }
1640 
1641  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1652  public function set_unpaid($user)
1653  {
1654  // phpcs:enable
1655  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1656  return $this->setUnpaid($user);
1657  }
1658 
1667  public function setUnpaid($user)
1668  {
1669  $error = 0;
1670 
1671  $this->db->begin();
1672 
1673  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1674  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1675  $sql .= ' date_closing=null,';
1676  $sql .= ' fk_user_closing=null';
1677  $sql .= ' WHERE rowid = '.((int) $this->id);
1678 
1679  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1680  $resql = $this->db->query($sql);
1681  if ($resql) {
1682  // Call trigger
1683  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1684  if ($result < 0) {
1685  $error++;
1686  }
1687  // End call triggers
1688  } else {
1689  $error++;
1690  $this->error = $this->db->error();
1691  dol_print_error($this->db);
1692  }
1693 
1694  if (!$error) {
1695  $this->db->commit();
1696  return 1;
1697  } else {
1698  $this->db->rollback();
1699  return -1;
1700  }
1701  }
1702 
1713  public function setCanceled($user, $close_code = '', $close_note = '')
1714  {
1715  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1716 
1717  $this->db->begin();
1718 
1719  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1720  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1721  if ($close_code) {
1722  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1723  }
1724  if ($close_note) {
1725  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1726  }
1727  $sql .= " WHERE rowid = ".((int) $this->id);
1728 
1729  $resql = $this->db->query($sql);
1730  if ($resql) {
1731  // Bound discounts are deducted from the invoice
1732  // as they have not been used since the invoice is abandoned.
1733  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1734  $sql .= ' SET fk_invoice_supplier = NULL';
1735  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1736 
1737  $resql = $this->db->query($sql);
1738  if ($resql) {
1739  // Call trigger
1740  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1741  if ($result < 0) {
1742  $this->db->rollback();
1743  return -1;
1744  }
1745  // End call triggers
1746 
1747  $this->db->commit();
1748  return 1;
1749  } else {
1750  $this->error = $this->db->error()." sql=".$sql;
1751  $this->db->rollback();
1752  return -1;
1753  }
1754  } else {
1755  $this->error = $this->db->error()." sql=".$sql;
1756  $this->db->rollback();
1757  return -2;
1758  }
1759  }
1760 
1770  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1771  {
1772  global $conf, $langs;
1773 
1774  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1775 
1776  $now = dol_now();
1777 
1778  $error = 0;
1779  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1780 
1781  // Force to have object complete for checks
1782  $this->fetch_thirdparty();
1783  $this->fetch_lines();
1784 
1785  // Check parameters
1786  if ($this->statut > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1787  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1788  return 0;
1789  }
1790  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1791  $langs->load("errors");
1792  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1793  return -1;
1794  }
1795  if (count($this->lines) <= 0) {
1796  $langs->load("errors");
1797  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1798  return -1;
1799  }
1800 
1801  $this->db->begin();
1802 
1803  // Define new ref
1804  if ($force_number) {
1805  $num = $force_number;
1806  } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1807  $num = $this->getNextNumRef($this->thirdparty);
1808  } else {
1809  $num = $this->ref;
1810  }
1811  $this->newref = dol_sanitizeFileName($num);
1812 
1813  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1814  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1815  $sql .= " WHERE rowid = ".((int) $this->id);
1816 
1817  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1818  $resql = $this->db->query($sql);
1819  if ($resql) {
1820  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1821  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1822  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1823  $langs->load("agenda");
1824 
1825  $cpt = count($this->lines);
1826  for ($i = 0; $i < $cpt; $i++) {
1827  if ($this->lines[$i]->fk_product > 0) {
1828  $this->line = $this->lines[$i];
1829  $mouvP = new MouvementStock($this->db);
1830  $mouvP->origin = &$this;
1831  $mouvP->setOrigin($this->element, $this->id);
1832  // We increase stock for product
1833  $up_ht_disc = $this->lines[$i]->pu_ht;
1834  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1835  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1836  }
1838  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1839  } else {
1840  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1841  }
1842  if ($result < 0) {
1843  $error++;
1844  }
1845  unset($this->line);
1846  }
1847  }
1848  }
1849 
1850  // Triggers call
1851  if (!$error && empty($notrigger)) {
1852  // Call trigger
1853  $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1854  if ($result < 0) {
1855  $error++;
1856  }
1857  // End call triggers
1858  }
1859 
1860  if (!$error) {
1861  $this->oldref = $this->ref;
1862 
1863  // Rename directory if dir was a temporary ref
1864  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1865  // Now we rename also files into index
1866  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1867  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1868  $resql = $this->db->query($sql);
1869  if (!$resql) {
1870  $error++; $this->error = $this->db->lasterror();
1871  }
1872 
1873  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1874  $oldref = dol_sanitizeFileName($this->ref);
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').$this->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 $this->newref
1883  $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1884  foreach ($listoffiles as $fileentry) {
1885  $dirsource = $fileentry['name'];
1886  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->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  //var_dump($this->line->origin);
2218  //var_dump($this->line->origin_id);
2219 
2220  // Multicurrency
2221  $this->line->fk_multicurrency = $this->fk_multicurrency;
2222  $this->line->multicurrency_code = $this->multicurrency_code;
2223  $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
2224 
2225  $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
2226  $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
2227  $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
2228 
2229  if (is_array($array_options) && count($array_options) > 0) {
2230  $this->line->array_options = $array_options;
2231  }
2232 
2233  $result = $this->line->insert($notrigger);
2234  if ($result > 0) {
2235  // Reorder if child line
2236  if (!empty($fk_parent_line)) {
2237  $this->line_order(true, 'DESC');
2238  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2239  $linecount = count($this->lines);
2240  for ($ii = $rang; $ii <= $linecount; $ii++) {
2241  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2242  }
2243  }
2244 
2245  // Mise a jour informations denormalisees au niveau de la facture meme
2246  $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.
2247  if ($result > 0) {
2248  $this->db->commit();
2249  return $this->line->id;
2250  } else {
2251  $this->error = $this->db->error();
2252  $this->db->rollback();
2253  return -1;
2254  }
2255  } else {
2256  $this->error = $this->line->error;
2257  $this->errors = $this->line->errors;
2258  $this->db->rollback();
2259  return -2;
2260  }
2261  } else {
2262  return 0;
2263  }
2264  }
2265 
2291  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)
2292  {
2293  global $mysoc, $langs;
2294 
2295  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);
2296  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2297 
2298  $pu = price2num($pu);
2299  $qty = price2num($qty);
2300  $remise_percent = price2num($remise_percent);
2301  $pu_devise = price2num($pu_devise);
2302 
2303  // Check parameters
2304  //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2305  if ($type < 0) {
2306  return -1;
2307  }
2308 
2309  if ($date_start && $date_end && $date_start > $date_end) {
2310  $langs->load("errors");
2311  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2312  return -1;
2313  }
2314 
2315  // Clean parameters
2316  if (empty($vatrate)) {
2317  $vatrate = 0;
2318  }
2319  if (empty($txlocaltax1)) {
2320  $txlocaltax1 = 0;
2321  }
2322  if (empty($txlocaltax2)) {
2323  $txlocaltax2 = 0;
2324  }
2325 
2326  $txlocaltax1 = price2num($txlocaltax1);
2327  $txlocaltax2 = price2num($txlocaltax2);
2328 
2329  // Calcul du total TTC et de la TVA pour la ligne a partir de
2330  // qty, pu, remise_percent et txtva
2331  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2332  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2333 
2334  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2335 
2336  $reg = array();
2337 
2338  // Clean vat code
2339  $vat_src_code = '';
2340  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2341  $vat_src_code = $reg[1];
2342  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2343  }
2344 
2345  $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);
2346  $total_ht = $tabprice[0];
2347  $total_tva = $tabprice[1];
2348  $total_ttc = $tabprice[2];
2349  $pu_ht = $tabprice[3];
2350  $pu_tva = $tabprice[4];
2351  $pu_ttc = $tabprice[5];
2352  $total_localtax1 = $tabprice[9];
2353  $total_localtax2 = $tabprice[10];
2354 
2355  // MultiCurrency
2356  $multicurrency_total_ht = $tabprice[16];
2357  $multicurrency_total_tva = $tabprice[17];
2358  $multicurrency_total_ttc = $tabprice[18];
2359  $pu_ht_devise = $tabprice[19];
2360 
2361  if (empty($info_bits)) {
2362  $info_bits = 0;
2363  }
2364 
2365  //Fetch current line from the database and then clone the object and set it in $oldline property
2366  $line = new SupplierInvoiceLine($this->db);
2367  $line->fetch($id);
2368  $line->fetch_optionals();
2369 
2370  $staticline = clone $line;
2371 
2372  if ($idproduct) {
2373  $product = new Product($this->db);
2374  $result = $product->fetch($idproduct);
2375  $product_type = $product->type;
2376  } else {
2377  $idproduct = $staticline->fk_product;
2378  $product_type = $type;
2379  }
2380 
2381  $line->oldline = $staticline;
2382  $line->context = $this->context;
2383 
2384  $line->description = $desc;
2385 
2386  $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2387  $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2388  $line->pu_ht = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2389  $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2390 
2391  $line->remise_percent = $remise_percent;
2392  $line->ref_supplier = $ref_supplier;
2393 
2394  $line->date_start = $date_start;
2395  $line->date_end = $date_end;
2396 
2397  $line->vat_src_code = $vat_src_code;
2398  $line->tva_tx = $vatrate;
2399  $line->localtax1_tx = $txlocaltax1;
2400  $line->localtax2_tx = $txlocaltax2;
2401  $line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2402  $line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2403 
2404  $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht);
2405  $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
2406  $line->total_localtax1 = $total_localtax1;
2407  $line->total_localtax2 = $total_localtax2;
2408  $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
2409 
2410  $line->fk_product = $idproduct;
2411  $line->product_type = $product_type;
2412  $line->info_bits = $info_bits;
2413  $line->fk_unit = $fk_unit;
2414  $line->rang = $rang;
2415 
2416  if (is_array($array_options) && count($array_options) > 0) {
2417  // We replace values in this->line->array_options only for entries defined into $array_options
2418  foreach ($array_options as $key => $value) {
2419  $line->array_options[$key] = $array_options[$key];
2420  }
2421  }
2422 
2423  // Multicurrency
2424  $line->multicurrency_subprice = $pu_ht_devise;
2425  $line->multicurrency_total_ht = $multicurrency_total_ht;
2426  $line->multicurrency_total_tva = $multicurrency_total_tva;
2427  $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2428 
2429  $res = $line->update($notrigger);
2430 
2431  if ($res < 1) {
2432  $this->errors[] = $line->error;
2433  } else {
2434  // Update total price into invoice record
2435  $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2436  }
2437 
2438  return $res;
2439  }
2440 
2448  public function deleteline($rowid, $notrigger = 0)
2449  {
2450  if (!$rowid) {
2451  $rowid = $this->id;
2452  }
2453 
2454  $this->db->begin();
2455 
2456  // Free the discount linked to a line of invoice
2457  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2458  $sql .= ' SET fk_invoice_supplier_line = NULL';
2459  $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2460 
2461  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2462  $result = $this->db->query($sql);
2463  if (!$result) {
2464  $this->error = $this->db->error();
2465  $this->db->rollback();
2466  return -2;
2467  }
2468 
2469  $line = new SupplierInvoiceLine($this->db);
2470 
2471  if ($line->fetch($rowid) < 1) {
2472  return -1;
2473  }
2474 
2475  $res = $line->delete($notrigger);
2476 
2477  if ($res < 1) {
2478  $this->errors[] = $line->error;
2479  $this->db->rollback();
2480  return -3;
2481  } else {
2482  $res = $this->update_price(1);
2483 
2484  if ($res > 0) {
2485  $this->db->commit();
2486  return 1;
2487  } else {
2488  $this->db->rollback();
2489  $this->error = $this->db->lasterror();
2490  return -4;
2491  }
2492  }
2493  }
2494 
2495 
2502  public function info($id)
2503  {
2504  $sql = 'SELECT c.rowid, datec, tms as datem, ';
2505  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2506  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2507  $sql .= ' WHERE c.rowid = '.((int) $id);
2508 
2509  $result = $this->db->query($sql);
2510  if ($result) {
2511  if ($this->db->num_rows($result)) {
2512  $obj = $this->db->fetch_object($result);
2513 
2514  $this->id = $obj->rowid;
2515  if ($obj->fk_user_author) {
2516  $cuser = new User($this->db);
2517  $cuser->fetch($obj->fk_user_author);
2518  $this->user_creation = $cuser;
2519  }
2520  if ($obj->fk_user_valid) {
2521  $vuser = new User($this->db);
2522  $vuser->fetch($obj->fk_user_valid);
2523  $this->user_validation = $vuser;
2524  }
2525  if ($obj->fk_user_modif) {
2526  $muser = new User($this->db);
2527  $muser->fetch($obj->fk_user_modif);
2528  $this->user_modification = $muser;
2529  }
2530  $this->date_creation = $this->db->jdate($obj->datec);
2531  $this->date_modification = $this->db->jdate($obj->datem);
2532  //$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)
2533  }
2534  $this->db->free($result);
2535  } else {
2536  dol_print_error($this->db);
2537  }
2538  }
2539 
2540  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2549  public function list_replacable_supplier_invoices($socid = 0)
2550  {
2551  // phpcs:enable
2552  global $conf;
2553 
2554  $return = array();
2555 
2556  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2557  $sql .= " ff.rowid as rowidnext";
2558  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2559  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2560  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2561  $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2562  $sql .= " AND f.entity = ".$conf->entity;
2563  $sql .= " AND f.paye = 0"; // Pas classee payee completement
2564  $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2565  $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
2566  if ($socid > 0) {
2567  $sql .= " AND f.fk_soc = ".((int) $socid);
2568  }
2569  $sql .= " ORDER BY f.ref";
2570 
2571  dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2572  $resql = $this->db->query($sql);
2573  if ($resql) {
2574  while ($obj = $this->db->fetch_object($resql)) {
2575  $return[$obj->rowid] = array(
2576  'id' => $obj->rowid,
2577  'ref' => $obj->ref,
2578  'status' => $obj->fk_statut
2579  );
2580  }
2581  //print_r($return);
2582  return $return;
2583  } else {
2584  $this->error = $this->db->error();
2585  return -1;
2586  }
2587  }
2588 
2589  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2599  public function list_qualified_avoir_supplier_invoices($socid = 0)
2600  {
2601  // phpcs:enable
2602  global $conf;
2603 
2604  $return = array();
2605 
2606  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.paye, pf.fk_paiementfourn";
2607  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2608  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2609  $sql .= " WHERE f.entity = ".$conf->entity;
2610  $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2611  $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2612  $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2613  $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2614  if ($socid > 0) {
2615  $sql .= " AND f.fk_soc = ".((int) $socid);
2616  }
2617  $sql .= " ORDER BY f.ref";
2618 
2619  dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2620  $resql = $this->db->query($sql);
2621  if ($resql) {
2622  while ($obj = $this->db->fetch_object($resql)) {
2623  $qualified = 0;
2624  if ($obj->fk_statut == self::STATUS_VALIDATED) {
2625  $qualified = 1;
2626  }
2627  if ($obj->fk_statut == self::STATUS_CLOSED) {
2628  $qualified = 1;
2629  }
2630  if ($qualified) {
2631  $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2632  $return[$obj->rowid] = array('ref'=>$obj->ref, 'status'=>$obj->fk_statut, 'type'=>$obj->type, 'paye'=>$obj->paye, 'paymentornot'=>$paymentornot);
2633  }
2634  }
2635 
2636  return $return;
2637  } else {
2638  $this->error = $this->db->error();
2639  return -1;
2640  }
2641  }
2642 
2643  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2650  public function load_board($user)
2651  {
2652  // phpcs:enable
2653  global $conf, $langs;
2654 
2655  $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2656  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2657  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2658  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2659  }
2660  $sql .= ' WHERE ff.paye = 0';
2661  $sql .= ' AND ff.fk_statut > 0';
2662  $sql .= " AND ff.entity = ".$conf->entity;
2663  if ($user->socid) {
2664  $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2665  }
2666 
2667  $resql = $this->db->query($sql);
2668  if ($resql) {
2669  $langs->load("bills");
2670  $now = dol_now();
2671 
2672  $response = new WorkboardResponse();
2673  $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2674  $response->label = $langs->trans("SupplierBillsToPay");
2675  $response->labelShort = $langs->trans("StatusToPay");
2676 
2677  $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2678  $response->img = img_object($langs->trans("Bills"), "bill");
2679 
2680  $facturestatic = new FactureFournisseur($this->db);
2681 
2682  while ($obj = $this->db->fetch_object($resql)) {
2683  $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2684  $facturestatic->statut = $obj->status; // For backward compatibility
2685  $facturestatic->status = $obj->status;
2686 
2687  $response->nbtodo++;
2688  $response->total += $obj->total_ht;
2689 
2690  if ($facturestatic->hasDelay()) {
2691  $response->nbtodolate++;
2692  $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2693  }
2694  }
2695 
2696  $this->db->free($resql);
2697  return $response;
2698  } else {
2699  dol_print_error($this->db);
2700  $this->error = $this->db->error();
2701  return -1;
2702  }
2703  }
2704 
2705 
2719  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2720  {
2721  global $langs, $conf, $user, $hookmanager;
2722 
2723  $result = '';
2724 
2725  if ($option == 'withdraw') {
2726  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2727  } elseif ($option == 'document') {
2728  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2729  } else {
2730  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2731  }
2732 
2733  if ($short) {
2734  return $url;
2735  }
2736 
2737  if ($option !== 'nolink') {
2738  // Add param to save lastsearch_values or not
2739  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2740  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2741  $add_save_lastsearch_values = 1;
2742  }
2743  if ($add_save_lastsearch_values) {
2744  $url .= '&save_lastsearch_values=1';
2745  }
2746  }
2747 
2748  $picto = $this->picto;
2749  if ($this->type == self::TYPE_REPLACEMENT) {
2750  $picto .= 'r'; // Replacement invoice
2751  }
2752  if ($this->type == self::TYPE_CREDIT_NOTE) {
2753  $picto .= 'a'; // Credit note
2754  }
2755  if ($this->type == self::TYPE_DEPOSIT) {
2756  $picto .= 'd'; // Deposit invoice
2757  }
2758 
2759  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2760  if ($this->type == self::TYPE_REPLACEMENT) {
2761  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2762  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2763  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2764  } elseif ($this->type == self::TYPE_DEPOSIT) {
2765  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2766  }
2767  if (isset($this->status)) {
2768  $alreadypaid = -1;
2769  if (isset($this->alreadypaid)) {
2770  $alreadypaid = $this->alreadypaid;
2771  }
2772 
2773  $label .= ' '.$this->getLibStatut(5, $alreadypaid);
2774  }
2775  if (!empty($this->ref)) {
2776  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2777  }
2778  if (!empty($this->ref_supplier)) {
2779  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2780  }
2781  if (!empty($this->label)) {
2782  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2783  }
2784  if (!empty($this->date)) {
2785  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2786  }
2787  if (!empty($this->date_echeance)) {
2788  $label .= '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2789  }
2790  if (!empty($this->total_ht)) {
2791  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2792  }
2793  if (!empty($this->total_tva)) {
2794  $label .= '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2795  }
2796  if (!empty($this->total_ttc)) {
2797  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2798  }
2799  if ($moretitle) {
2800  $label .= ' - '.$moretitle;
2801  }
2802 
2803  $ref = $this->ref;
2804  if (empty($ref)) {
2805  $ref = $this->id;
2806  }
2807 
2808  $linkclose = '';
2809  if (empty($notooltip)) {
2810  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2811  $label = $langs->trans("ShowSupplierInvoice");
2812  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2813  }
2814  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2815  $linkclose .= ' class="classfortooltip"';
2816  }
2817 
2818  $linkstart = '<a href="'.$url.'"';
2819  $linkstart .= $linkclose.'>';
2820  $linkend = '</a>';
2821 
2822  $result .= $linkstart;
2823  if ($withpicto) {
2824  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2825  }
2826  if ($withpicto != 2) {
2827  $result .= ($max ?dol_trunc($ref, $max) : $ref);
2828  }
2829  $result .= $linkend;
2830 
2831  if ($addlinktonotes) {
2832  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2833  if ($txttoshow) {
2834  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2835  $result .= ' <span class="note inline-block">';
2836  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2837  $result .= img_picto('', 'note');
2838  $result .= '</a>';
2839  $result .= '</span>';
2840  }
2841  }
2842  global $action;
2843  $hookmanager->initHooks(array($this->element . 'dao'));
2844  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2845  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2846  if ($reshook > 0) {
2847  $result = $hookmanager->resPrint;
2848  } else {
2849  $result .= $hookmanager->resPrint;
2850  }
2851  return $result;
2852  }
2853 
2862  public function getNextNumRef($soc, $mode = 'next')
2863  {
2864  global $db, $langs, $conf;
2865  $langs->load("orders");
2866 
2867  // Clean parameters (if not defined or using deprecated value)
2868  if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) {
2869  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2870  }
2871 
2872  $mybool = false;
2873 
2874  $file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2875  $classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2876 
2877  // Include file with class
2878  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2879 
2880  foreach ($dirmodels as $reldir) {
2881  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2882 
2883  // Load file with numbering class (if found)
2884  $mybool |= @include_once $dir.$file;
2885  }
2886 
2887  if ($mybool === false) {
2888  dol_print_error('', "Failed to include file ".$file);
2889  return '';
2890  }
2891 
2892  $obj = new $classname();
2893  $numref = "";
2894  $numref = $obj->getNumRef($soc, $this, $mode);
2895 
2896  if ($numref != "") {
2897  return $numref;
2898  } else {
2899  $this->error = $obj->error;
2900  return -1;
2901  }
2902  }
2903 
2904 
2913  public function initAsSpecimen($option = '')
2914  {
2915  global $langs, $conf;
2916  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2917 
2918  $now = dol_now();
2919 
2920  // Load array of products prodids
2921  $num_prods = 0;
2922  $prodids = array();
2923 
2924  $sql = "SELECT rowid";
2925  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2926  $sql .= " WHERE entity IN (".getEntity('product').")";
2927  $sql .= $this->db->plimit(100);
2928 
2929  $resql = $this->db->query($sql);
2930  if ($resql) {
2931  $num_prods = $this->db->num_rows($resql);
2932  $i = 0;
2933  while ($i < $num_prods) {
2934  $i++;
2935  $row = $this->db->fetch_row($resql);
2936  $prodids[$i] = $row[0];
2937  }
2938  }
2939 
2940  // Initialise parametres
2941  $this->id = 0;
2942  $this->ref = 'SPECIMEN';
2943  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
2944  $this->specimen = 1;
2945  $this->socid = 1;
2946  $this->date = $now;
2947  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2948  $this->cond_reglement_code = 'RECEP';
2949  $this->mode_reglement_code = 'CHQ';
2950 
2951  $this->note_public = 'This is a comment (public)';
2952  $this->note_private = 'This is a comment (private)';
2953 
2954  $this->multicurrency_tx = 1;
2955  $this->multicurrency_code = $conf->currency;
2956 
2957  $xnbp = 0;
2958  if (empty($option) || $option != 'nolines') {
2959  // Lines
2960  $nbp = 5;
2961  while ($xnbp < $nbp) {
2962  $line = new SupplierInvoiceLine($this->db);
2963  $line->desc = $langs->trans("Description")." ".$xnbp;
2964  $line->qty = 1;
2965  $line->subprice = 100;
2966  $line->pu_ht = 100; // the canelle template use pu_ht and not subprice
2967  $line->price = 100;
2968  $line->tva_tx = 19.6;
2969  $line->localtax1_tx = 0;
2970  $line->localtax2_tx = 0;
2971  if ($xnbp == 2) {
2972  $line->total_ht = 50;
2973  $line->total_ttc = 59.8;
2974  $line->total_tva = 9.8;
2975  $line->remise_percent = 50;
2976  } else {
2977  $line->total_ht = 100;
2978  $line->total_ttc = 119.6;
2979  $line->total_tva = 19.6;
2980  $line->remise_percent = 0;
2981  }
2982 
2983  if ($num_prods > 0) {
2984  $prodid = mt_rand(1, $num_prods);
2985  $line->fk_product = $prodids[$prodid];
2986  }
2987  $line->product_type = 0;
2988 
2989  $this->lines[$xnbp] = $line;
2990 
2991  $this->total_ht += $line->total_ht;
2992  $this->total_tva += $line->total_tva;
2993  $this->total_ttc += $line->total_ttc;
2994 
2995  $xnbp++;
2996  }
2997  }
2998 
2999  $this->amount_ht = $xnbp * 100;
3000  $this->total_ht = $xnbp * 100;
3001  $this->total_tva = $xnbp * 19.6;
3002  $this->total_ttc = $xnbp * 119.6;
3003  }
3004 
3005  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3011  public function load_state_board()
3012  {
3013  // phpcs:enable
3014  global $conf, $user;
3015 
3016  $this->nb = array();
3017 
3018  $clause = "WHERE";
3019 
3020  $sql = "SELECT count(f.rowid) as nb";
3021  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3022  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3023  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3024  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3025  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3026  $clause = "AND";
3027  }
3028  $sql .= " ".$clause." f.entity = ".$conf->entity;
3029 
3030  $resql = $this->db->query($sql);
3031  if ($resql) {
3032  while ($obj = $this->db->fetch_object($resql)) {
3033  $this->nb["supplier_invoices"] = $obj->nb;
3034  }
3035  $this->db->free($resql);
3036  return 1;
3037  } else {
3038  dol_print_error($this->db);
3039  $this->error = $this->db->error();
3040  return -1;
3041  }
3042  }
3043 
3052  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3053  {
3054  global $conf, $langs;
3055 
3056  $error = 0;
3057 
3058  $object = new FactureFournisseur($this->db);
3059 
3060  $this->db->begin();
3061 
3062  // Load source object
3063  $object->fetch($fromid);
3064  $object->id = 0;
3065  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3066  $object->status = self::STATUS_DRAFT;
3067 
3068  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3069 
3070  // Clear fields
3071  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3072  $object->author = $user->id;
3073  $object->user_valid = 0;
3074  $object->fk_facture_source = 0;
3075  $object->date_creation = '';
3076  $object->date_validation = '';
3077  $object->date = (empty($this->date) ? dol_now() : $this->date);
3078  $object->ref_client = '';
3079  $object->close_code = '';
3080  $object->close_note = '';
3081  if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3082  $object->note_private = '';
3083  $object->note_public = '';
3084  }
3085 
3086  $object->date_echeance = $object->calculate_date_lim_reglement();
3087 
3088  // Loop on each line of new invoice
3089  foreach ($object->lines as $i => $line) {
3090  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3091  unset($object->lines[$i]);
3092  }
3093  }
3094 
3095  // Create clone
3096  $object->context['createfromclone'] = 'createfromclone';
3097  $result = $object->create($user);
3098 
3099  // Other options
3100  if ($result < 0) {
3101  $this->error = $object->error;
3102  $this->errors = $object->errors;
3103  $error++;
3104  }
3105 
3106  if (!$error) {
3107  }
3108 
3109  unset($object->context['createfromclone']);
3110 
3111  // End
3112  if (!$error) {
3113  $this->db->commit();
3114  return $object->id;
3115  } else {
3116  $this->db->rollback();
3117  return -1;
3118  }
3119  }
3120 
3132  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3133  {
3134  global $conf, $user, $langs;
3135 
3136  $langs->load("suppliers");
3137  $outputlangs->load("products");
3138 
3139  // Set the model on the model name to use
3140  if (empty($modele)) {
3141  if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF)) {
3142  $modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
3143  } else {
3144  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3145  }
3146  }
3147 
3148  if (empty($modele)) {
3149  return 0;
3150  } else {
3151  $modelpath = "core/modules/supplier_invoice/doc/";
3152 
3153  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3154  }
3155  }
3156 
3161  public function getRights()
3162  {
3163  global $user;
3164 
3165  return $user->rights->fournisseur->facture;
3166  }
3167 
3176  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3177  {
3178  $tables = array(
3179  'facture_fourn'
3180  );
3181 
3182  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3183  }
3184 
3193  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3194  {
3195  $tables = array(
3196  'facture_fourn_det'
3197  );
3198 
3199  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3200  }
3201 
3207  public function hasDelay()
3208  {
3209  global $conf;
3210 
3211  $now = dol_now();
3212 
3213  if (!$this->date_echeance) {
3214  return false;
3215  }
3216 
3217  $status = isset($this->status) ? $this->status : $this->statut;
3218 
3219  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3220  }
3221 
3227  public function isCreditNoteUsed()
3228  {
3229  $isUsed = false;
3230 
3231  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3232  $resql = $this->db->query($sql);
3233  if (!empty($resql)) {
3234  $obj = $this->db->fetch_object($resql);
3235  if (!empty($obj->fk_invoice_supplier)) {
3236  $isUsed = true;
3237  }
3238  }
3239 
3240  return $isUsed;
3241  }
3242 }
3243 
3244 
3245 
3250 {
3254  public $element = 'facture_fourn_det';
3255 
3259  public $table_element = 'facture_fourn_det';
3260 
3261  public $oldline;
3262 
3267  public $ref;
3268 
3273  public $product_ref;
3274 
3280  public $ref_supplier;
3281 
3286  public $product_desc;
3287 
3294  public $pu_ht;
3295 
3300  public $subprice;
3301 
3306  public $pu_ttc;
3307 
3308 
3313  public $fk_facture_fourn;
3314 
3320  public $label;
3321 
3326  public $description;
3327 
3328  public $date_start;
3329  public $date_end;
3330 
3331  public $skip_update_total; // Skip update price total for special lines
3332 
3336  public $situation_percent;
3337 
3341  public $fk_prev_id;
3342 
3347  public $vat_src_code;
3348 
3353  public $tva_tx;
3354 
3359  public $localtax1_tx;
3360 
3365  public $localtax2_tx;
3366 
3371  public $qty;
3372 
3377  public $remise_percent;
3378 
3383  public $total_ht;
3384 
3389  public $total_ttc;
3390 
3395  public $total_tva;
3396 
3401  public $total_localtax1;
3402 
3407  public $total_localtax2;
3408 
3412  public $fk_product;
3413 
3418  public $product_type;
3419 
3424  public $product_label;
3425 
3432  public $info_bits;
3433 
3438  public $fk_remise_except;
3439 
3443  public $fk_parent_line;
3444 
3445  public $special_code;
3446 
3450  public $rang;
3451 
3456  public $localtax1_type;
3457 
3462  public $localtax2_type;
3463 
3464  // Multicurrency
3468  public $fk_multicurrency;
3469 
3470  public $multicurrency_code;
3471  public $multicurrency_subprice;
3472  public $multicurrency_total_ht;
3473  public $multicurrency_total_tva;
3474  public $multicurrency_total_ttc;
3475 
3476 
3482  public function __construct($db)
3483  {
3484  $this->db = $db;
3485  }
3486 
3493  public function fetch($rowid)
3494  {
3495  $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';
3496  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3497  $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';
3498  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3499  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3500  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3501  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3502  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3503  $sql .= ' ORDER BY f.rang, f.rowid';
3504 
3505  $query = $this->db->query($sql);
3506 
3507  if (!$query) {
3508  $this->errors[] = $this->db->error();
3509  return -1;
3510  }
3511 
3512  if (!$this->db->num_rows($query)) {
3513  return 0;
3514  }
3515 
3516  $obj = $this->db->fetch_object($query);
3517 
3518  $this->id = $obj->rowid;
3519  $this->rowid = $obj->rowid;
3520  $this->fk_facture_fourn = $obj->fk_facture_fourn;
3521  $this->description = $obj->description;
3522  $this->date_start = $obj->date_start;
3523  $this->date_end = $obj->date_end;
3524  $this->product_ref = $obj->product_ref;
3525  $this->ref_supplier = $obj->ref_supplier;
3526  $this->product_desc = $obj->product_desc;
3527 
3528  $this->subprice = $obj->pu_ht;
3529  $this->pu_ht = $obj->pu_ht;
3530  $this->pu_ttc = $obj->pu_ttc;
3531  $this->tva_tx = $obj->tva_tx;
3532  $this->localtax1_tx = $obj->localtax1_tx;
3533  $this->localtax2_tx = $obj->localtax2_tx;
3534  $this->localtax1_type = $obj->localtax1_type;
3535  $this->localtax2_type = $obj->localtax2_type;
3536 
3537  $this->qty = $obj->qty;
3538  $this->remise_percent = $obj->remise_percent;
3539  $this->fk_remise_except = $obj->fk_remise_except;
3540  //$this->tva = $obj->total_tva; // deprecated
3541  $this->total_ht = $obj->total_ht;
3542  $this->total_tva = $obj->total_tva;
3543  $this->total_localtax1 = $obj->total_localtax1;
3544  $this->total_localtax2 = $obj->total_localtax2;
3545  $this->total_ttc = $obj->total_ttc;
3546  $this->fk_product = $obj->fk_product;
3547  $this->product_type = $obj->product_type;
3548  $this->product_label = $obj->product_label;
3549  $this->info_bits = $obj->info_bits;
3550  $this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3551  $this->fk_parent_line = $obj->fk_parent_line;
3552  $this->special_code = $obj->special_code;
3553  $this->rang = $obj->rang;
3554  $this->fk_unit = $obj->fk_unit;
3555 
3556  $this->multicurrency_subprice = $obj->multicurrency_subprice;
3557  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3558  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3559  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3560 
3561  $this->fetch_optionals();
3562 
3563  return 1;
3564  }
3565 
3572  public function delete($notrigger = 0)
3573  {
3574  global $user, $conf;
3575 
3576  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
3577 
3578  $error = 0;
3579 
3580  $this->db->begin();
3581 
3582  if (!$notrigger) {
3583  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3584  $error++;
3585  }
3586  }
3587 
3588  $this->deleteObjectLinked();
3589 
3590  // Remove extrafields
3591  if (!$error) {
3592  $result = $this->deleteExtraFields();
3593  if ($result < 0) {
3594  $error++;
3595  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3596  }
3597  }
3598 
3599  if (!$error) {
3600  // Supprime ligne
3601  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3602  $sql .= " WHERE rowid = ".((int) $this->id);
3603  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3604  $resql = $this->db->query($sql);
3605  if (!$resql) {
3606  $error++;
3607  $this->error = $this->db->lasterror();
3608  }
3609  }
3610 
3611  if (!$error) {
3612  $this->db->commit();
3613  return 1;
3614  } else {
3615  $this->db->rollback();
3616  return -1;
3617  }
3618  }
3619 
3626  public function update($notrigger = 0)
3627  {
3628  global $conf;
3629 
3630  $pu = price2num($this->pu_ht);
3631  $qty = price2num($this->qty);
3632 
3633  // Check parameters
3634  if (empty($this->qty)) {
3635  $this->qty = 0;
3636  }
3637 
3638  if ($this->product_type < 0) {
3639  return -1;
3640  }
3641 
3642  // Clean parameters
3643  if (empty($this->remise_percent)) {
3644  $this->remise_percent = 0;
3645  }
3646  if (empty($this->tva_tx)) {
3647  $this->tva_tx = 0;
3648  }
3649  if (empty($this->localtax1_tx)) {
3650  $this->localtax1_tx = 0;
3651  }
3652  if (empty($this->localtax2_tx)) {
3653  $this->localtax2_tx = 0;
3654  }
3655 
3656  if (empty($this->pa_ht)) {
3657  $this->pa_ht = 0;
3658  }
3659  if (empty($this->multicurrency_subprice)) {
3660  $this->multicurrency_subprice = 0;
3661  }
3662  if (empty($this->multicurrency_total_ht)) {
3663  $this->multicurrency_total_ht = 0;
3664  }
3665  if (empty($this->multicurrency_total_tva)) {
3666  $this->multicurrency_total_tva = 0;
3667  }
3668  if (empty($this->multicurrency_total_ttc)) {
3669  $this->multicurrency_total_ttc = 0;
3670  }
3671 
3672  $fk_product = (int) $this->fk_product;
3673  $fk_unit = (int) $this->fk_unit;
3674 
3675  $this->db->begin();
3676 
3677  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3678  $sql .= " description = '".$this->db->escape($this->description)."'";
3679  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3680  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3681  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3682  $sql .= ", pu_ht = ".price2num($this->pu_ht);
3683  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3684  $sql .= ", qty = ".price2num($this->qty);
3685  $sql .= ", remise_percent = ".price2num($this->remise_percent);
3686  if ($this->fk_remise_except > 0) $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
3687  else $sql .= ", fk_remise_except=null";
3688  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3689  $sql .= ", tva_tx = ".price2num($this->tva_tx);
3690  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3691  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3692  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3693  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3694  $sql .= ", total_ht = ".price2num($this->total_ht);
3695  $sql .= ", tva= ".price2num($this->total_tva);
3696  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3697  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3698  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3699  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
3700  $sql .= ", product_type = ".((int) $this->product_type);
3701  $sql .= ", info_bits = ".((int) $this->info_bits);
3702  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
3703 
3704  if (!empty($this->rang)) {
3705  $sql .= ", rang=".((int) $this->rang);
3706  }
3707 
3708  // Multicurrency
3709  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3710  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3711  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3712  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3713 
3714  $sql .= " WHERE rowid = ".((int) $this->id);
3715 
3716  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3717  $resql = $this->db->query($sql);
3718 
3719  if (!$resql) {
3720  $this->db->rollback();
3721  $this->error = $this->db->lasterror();
3722  return -1;
3723  }
3724 
3725  $this->rowid = $this->id;
3726  $error = 0;
3727 
3728  if (!$error) {
3729  $result = $this->insertExtraFields();
3730  if ($result < 0) {
3731  $error++;
3732  }
3733  }
3734 
3735  if (!$error && !$notrigger) {
3736  global $langs, $user;
3737 
3738  // Call trigger
3739  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
3740  $this->db->rollback();
3741  return -1;
3742  }
3743  // End call triggers
3744  }
3745 
3746  if ($error) {
3747  $this->db->rollback();
3748  return -1;
3749  }
3750 
3751  $this->db->commit();
3752  return 1;
3753  }
3754 
3761  public function insert($notrigger = 0)
3762  {
3763  global $user, $conf, $langs;
3764 
3765  $error = 0;
3766 
3767  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3768 
3769  // Clean parameters
3770  $this->desc = trim($this->desc);
3771  if (empty($this->tva_tx)) {
3772  $this->tva_tx = 0;
3773  }
3774  if (empty($this->localtax1_tx)) {
3775  $this->localtax1_tx = 0;
3776  }
3777  if (empty($this->localtax2_tx)) {
3778  $this->localtax2_tx = 0;
3779  }
3780  if (empty($this->localtax1_type)) {
3781  $this->localtax1_type = '0';
3782  }
3783  if (empty($this->localtax2_type)) {
3784  $this->localtax2_type = '0';
3785  }
3786  if (empty($this->total_tva)) {
3787  $this->total_tva = 0;
3788  }
3789  if (empty($this->total_localtax1)) {
3790  $this->total_localtax1 = 0;
3791  }
3792  if (empty($this->total_localtax2)) {
3793  $this->total_localtax2 = 0;
3794  }
3795  if (empty($this->rang)) {
3796  $this->rang = 0;
3797  }
3798  if (empty($this->remise_percent)) {
3799  $this->remise_percent = 0;
3800  }
3801  if (empty($this->info_bits)) {
3802  $this->info_bits = 0;
3803  }
3804  if (empty($this->subprice)) {
3805  $this->subprice = 0;
3806  }
3807  if (empty($this->special_code)) {
3808  $this->special_code = 0;
3809  }
3810  if (empty($this->fk_parent_line)) {
3811  $this->fk_parent_line = 0;
3812  }
3813  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
3814  $this->situation_percent = 100;
3815  }
3816 
3817  if (empty($this->pa_ht)) {
3818  $this->pa_ht = 0;
3819  }
3820  if (empty($this->multicurrency_subprice)) {
3821  $this->multicurrency_subprice = 0;
3822  }
3823  if (empty($this->multicurrency_total_ht)) {
3824  $this->multicurrency_total_ht = 0;
3825  }
3826  if (empty($this->multicurrency_total_tva)) {
3827  $this->multicurrency_total_tva = 0;
3828  }
3829  if (empty($this->multicurrency_total_ttc)) {
3830  $this->multicurrency_total_ttc = 0;
3831  }
3832 
3833 
3834  // Check parameters
3835  if ($this->product_type < 0) {
3836  $this->error = 'ErrorProductTypeMustBe0orMore';
3837  return -1;
3838  }
3839  if (!empty($this->fk_product) && $this->fk_product > 0) {
3840  // Check product exists
3841  $result = Product::isExistingObject('product', $this->fk_product);
3842  if ($result <= 0) {
3843  $this->error = 'ErrorProductIdDoesNotExists';
3844  return -1;
3845  }
3846  }
3847 
3848  $this->db->begin();
3849 
3850  // Insertion dans base de la ligne
3851  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3852  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3853  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3854  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
3855  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3856  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3857  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3858  $sql .= ')';
3859  $sql .= " VALUES (".$this->fk_facture_fourn.",";
3860  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3861  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3862  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3863  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
3864  $sql .= " ".price2num($this->qty).",";
3865 
3866  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3867  $sql .= " ".price2num($this->tva_tx).",";
3868  $sql .= " ".price2num($this->localtax1_tx).",";
3869  $sql .= " ".price2num($this->localtax2_tx).",";
3870  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3871  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3872  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
3873  $sql .= " ".((int) $this->product_type).",";
3874  $sql .= " ".price2num($this->remise_percent).",";
3875  $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
3876  $sql .= " ".price2num($this->subprice).",";
3877  $sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
3878  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3879  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3880  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
3881  $sql .= ' '.((int) $this->rang).',';
3882  $sql .= ' '.((int) $this->special_code).',';
3883  $sql .= " ".((int) $this->info_bits).",";
3884  $sql .= " ".price2num($this->total_ht).",";
3885  $sql .= " ".price2num($this->total_tva).",";
3886  $sql .= " ".price2num($this->total_ttc).",";
3887  $sql .= " ".price2num($this->total_localtax1).",";
3888  $sql .= " ".price2num($this->total_localtax2);
3889  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3890  $sql .= ", ".(int) $this->fk_multicurrency;
3891  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3892  $sql .= ", ".price2num($this->multicurrency_subprice);
3893  $sql .= ", ".price2num($this->multicurrency_total_ht);
3894  $sql .= ", ".price2num($this->multicurrency_total_tva);
3895  $sql .= ", ".price2num($this->multicurrency_total_ttc);
3896  $sql .= ')';
3897 
3898  $resql = $this->db->query($sql);
3899  if ($resql) {
3900  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3901  $this->rowid = $this->id; // backward compatibility
3902 
3903  if (!$error) {
3904  $result = $this->insertExtraFields();
3905  if ($result < 0) {
3906  $error++;
3907  }
3908  }
3909 
3910  // Si fk_remise_except defini, on lie la remise a la facture
3911  // ce qui la flague comme "consommee".
3912  if ($this->fk_remise_except) {
3913  $discount = new DiscountAbsolute($this->db);
3914  $result = $discount->fetch($this->fk_remise_except);
3915  if ($result >= 0) {
3916  // Check if discount was found
3917  if ($result > 0) {
3918  // Check if discount not already affected to another invoice
3919  if ($discount->fk_facture_line > 0) {
3920  if (empty($noerrorifdiscountalreadylinked)) {
3921  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
3922  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3923  $this->db->rollback();
3924  return -3;
3925  }
3926  } else {
3927  $result = $discount->link_to_invoice($this->rowid, 0);
3928  if ($result < 0) {
3929  $this->error = $discount->error;
3930  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3931  $this->db->rollback();
3932  return -3;
3933  }
3934  }
3935  } else {
3936  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
3937  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3938  $this->db->rollback();
3939  return -3;
3940  }
3941  } else {
3942  $this->error = $discount->error;
3943  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3944  $this->db->rollback();
3945  return -3;
3946  }
3947  }
3948 
3949  if (!$error && !$notrigger) {
3950  // Call trigger
3951  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
3952  if ($result < 0) {
3953  $this->db->rollback();
3954  return -2;
3955  }
3956  // End call triggers
3957  }
3958 
3959  $this->db->commit();
3960  return $this->id;
3961  } else {
3962  $this->error = $this->db->error();
3963  $this->db->rollback();
3964  return -2;
3965  }
3966  }
3967 
3968  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3974  public function update_total()
3975  {
3976  // phpcs:enable
3977  $this->db->begin();
3978 
3979  // Mise a jour ligne en base
3980  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3981  $sql .= " total_ht = ".price2num($this->total_ht);
3982  $sql .= ", tva= ".price2num($this->total_tva);
3983  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
3984  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
3985  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3986  $sql .= " WHERE rowid = ".((int) $this->rowid);
3987 
3988  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
3989 
3990  $resql = $this->db->query($sql);
3991  if ($resql) {
3992  $this->db->commit();
3993  return 1;
3994  } else {
3995  $this->error = $this->db->error();
3996  $this->db->rollback();
3997  return -2;
3998  }
3999  }
4000 }
$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 commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty 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...
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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.
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:47
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:1402
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:1251
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