dolibarr  16.0.1
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.'/product/class/product.class.php';
42 
43 if (!empty($conf->accounting->enabled)) {
44  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
45 }
46 if (!empty($conf->accounting->enabled)) {
47  require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
48 }
49 
54 {
58  public $element = 'invoice_supplier';
59 
63  public $table_element = 'facture_fourn';
64 
68  public $table_element_line = 'facture_fourn_det';
69 
73  public $fk_element = 'fk_facture_fourn';
74 
78  public $picto = 'supplier_invoice';
79 
84  public $ismultientitymanaged = 1;
85 
90  public $restrictiononfksoc = 1;
91 
95  protected $table_ref_field = 'ref';
96 
100  public $rowid;
101 
105  public $ref;
106 
110  public $ref_supplier;
111 
115  public $label;
116 
117  public $socid;
118 
119  //Check constants for types
120  public $type = self::TYPE_STANDARD;
121 
127  public $statut;
128 
134  public $status;
135 
141  public $close_code;
142 
147  public $close_note;
148 
153  public $paye;
154 
155  public $author;
156 
162  public $datec;
163 
169  public $tms;
170 
176  public $date;
177 
183  public $date_echeance;
184 
189  public $amount = 0;
194  public $remise = 0;
195 
200  public $tva;
201 
202  // Warning: Do not set default value into property defintion. it must stay null.
203  // For example to avoid to have substition done when object is generic and not yet defined.
204  public $localtax1;
205  public $localtax2;
206  public $total_ht;
207  public $total_tva;
208  public $total_localtax1;
209  public $total_localtax2;
210  public $total_ttc;
211 
216  public $note;
217 
218  public $note_private;
219  public $note_public;
220  public $propalid;
221 
222  public $cond_reglement_id;
223  public $cond_reglement_code;
224  public $cond_reglement_label;
225  public $cond_reglement_doc;
226 
230  public $fk_account; // default bank account
231 
232  public $mode_reglement_id;
233  public $mode_reglement_code;
234 
238  public $transport_mode_id;
239 
240  public $extraparams = array();
241 
246  public $lines = array();
247 
251  public $fournisseur;
252 
253  // Multicurrency
257  public $fk_multicurrency;
258 
259  public $multicurrency_code;
260  public $multicurrency_tx;
261  public $multicurrency_total_ht;
262  public $multicurrency_total_tva;
263  public $multicurrency_total_ttc;
265 
268  public $fk_facture_source;
269 
270  public $fac_rec;
271 
272 
273  public $fields = array(
274  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
275  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
276  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefSupplier', 'enabled'=>1, 'visible'=>-1, 'position'=>20),
277  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>25, 'index'=>1),
278  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>30),
279  'type' =>array('type'=>'smallint(6)', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
280  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'notnull'=>1, 'position'=>40),
281  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
282  'datef' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
283  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>55),
284  'libelle' =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
285  'paye' =>array('type'=>'smallint(6)', 'label'=>'Paye', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
286  'amount' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
287  'remise' =>array('type'=>'double(24,8)', 'label'=>'Discount', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
288  'close_code' =>array('type'=>'varchar(16)', 'label'=>'CloseCode', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
289  'close_note' =>array('type'=>'varchar(128)', 'label'=>'CloseNote', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
290  'tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
291  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
292  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>100),
293  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
294  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'TotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
295  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
296  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
297  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>130),
298  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
299  'fk_facture_source' =>array('type'=>'integer', 'label'=>'Fk facture source', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
300  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>'$conf->project->enabled', 'visible'=>-1, 'position'=>145),
301  'fk_account' =>array('type'=>'integer', 'label'=>'Account', 'enabled'=>'$conf->banque->enabled', 'visible'=>-1, 'position'=>150),
302  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
303  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
304  'date_lim_reglement' =>array('type'=>'date', 'label'=>'DateLimReglement', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
305  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>170),
306  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>175),
307  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>180),
308  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
309  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
310  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
311  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'MulticurrencyId', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
312  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCode', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
313  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
314  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
315  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
316  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>230),
317  'date_pointoftax' =>array('type'=>'date', 'label'=>'Date pointoftax', 'enabled'=>1, 'visible'=>-1, 'position'=>235),
318  'date_valid' =>array('type'=>'date', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>240),
319  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>245),
320  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
321  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
322  );
323 
324 
328  const TYPE_STANDARD = 0;
329 
333  const TYPE_REPLACEMENT = 1;
334 
338  const TYPE_CREDIT_NOTE = 2;
339 
343  const TYPE_DEPOSIT = 3;
344 
348  const STATUS_DRAFT = 0;
349 
353  const STATUS_VALIDATED = 1;
354 
362  const STATUS_CLOSED = 2;
363 
371  const STATUS_ABANDONED = 3;
372 
373  const CLOSECODE_DISCOUNTVAT = 'discount_vat';
374  const CLOSECODE_BADCREDIT = 'badsupplier';
375  const CLOSECODE_ABANDONED = 'abandon';
376  const CLOSECODE_REPLACED = 'replaced';
377 
383  public function __construct($db)
384  {
385  $this->db = $db;
386  }
387 
394  public function create($user)
395  {
396  global $langs, $conf, $hookmanager;
397 
398  $error = 0;
399  $now = dol_now();
400 
401  // Clean parameters
402  if (isset($this->ref_supplier)) {
403  $this->ref_supplier = trim($this->ref_supplier);
404  }
405  if (empty($this->type)) {
406  $this->type = self::TYPE_STANDARD;
407  }
408  if (empty($this->date)) {
409  $this->date = $now;
410  }
411 
412  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
413  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
414  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
415  } else {
416  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
417  }
418  if (empty($this->fk_multicurrency)) {
419  $this->multicurrency_code = $conf->currency;
420  $this->fk_multicurrency = 0;
421  $this->multicurrency_tx = 1;
422  }
423 
424  $this->db->begin();
425 
426  // Create invoice from a template recurring invoice
427  if ($this->fac_rec > 0) {
428  $this->fk_fac_rec_source = $this->fac_rec;
429 
430  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
431  $_facrec = new FactureFournisseurRec($this->db);
432  $result = $_facrec->fetch($this->fac_rec);
433  $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
434 
435  // Define some dates
436  if (! empty($_facrec->frequency)) {
437  $originaldatewhen = $_facrec->date_when;
438  $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
439  $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
440  $this->socid = $_facrec->socid;
441  }
442 
443  $this->entity = $_facrec->entity; // Invoice created in same entity than template
444 
445  // 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
446  $this->fk_project = GETPOST('projectid', 'int') > 0 ? ((int) GETPOST('projectid', 'int')) : $_facrec->fk_projet;
447  $this->fk_projet = $this->fk_project;
448  $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
449  $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
450  $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
451  $this->cond_reglement_id = GETPOST('cond_reglement_id', 'int') > 0 ? ((int) GETPOST('cond_reglement_id', 'int')) : $_facrec->cond_reglement_id;
452  $this->mode_reglement_id = GETPOST('mode_reglement_id', 'int') > 0 ? ((int) GETPOST('mode_reglement_id', 'int')) : $_facrec->mode_reglement_id;
453  $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
454 
455  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
456  $this->total_ht = $_facrec->total_ht;
457  $this->total_ttc = $_facrec->total_ttc;
458 
459  // Fields always coming from template
460  $this->fk_incoterms = $_facrec->fk_incoterms;
461  $this->location_incoterms = $_facrec->location_incoterms;
462 
463  // Clean parameters
464  if (! $this->type) {
465  $this->type = self::TYPE_STANDARD;
466  }
467  if (! empty(GETPOST('ref_supplier'))) {
468  $this->ref_supplier = trim($this->ref_supplier);
469  } else {
470  $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
471  }
472  $this->note_public = trim($this->note_public);
473  $this->note_private = trim($this->note_private);
474  $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->titre));
475 
476  $this->array_options = $_facrec->array_options;
477 
478  if (! $this->mode_reglement_id) {
479  $this->mode_reglement_id = 0;
480  }
481  $this->brouillon = 1;
482  $this->status = self::STATUS_DRAFT;
483  $this->statut = self::STATUS_DRAFT;
484 
485  $this->linked_objects = $_facrec->linkedObjectsIds;
486  // We do not add link to template invoice or next invoice will be linked to all generated invoices
487  //$this->linked_objects['facturerec'][0] = $this->fac_rec;
488 
489  $forceduedate = $this->calculate_date_lim_reglement();
490 
491  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
492  if ($_facrec->frequency > 0) {
493  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
494  if (empty($_facrec->date_when)) {
495  $_facrec->date_when = $now;
496  }
497  $next_date = $_facrec->getNextDate(); // Calculate next date
498  $result = $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date', '', $user, '');
499  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
500  $result = $_facrec->setNextDate($next_date, 1);
501  }
502 
503  // Define lang of customer
504  $outputlangs = $langs;
505  $newlang = '';
506 
507  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->thirdparty->default_lang)) {
508  $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
509  }
510  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->default_lang)) {
511  $newlang = $this->default_lang; // for thirdparty
512  }
513  if (! empty($newlang)) {
514  $outputlangs = new Translate("", $conf);
515  $outputlangs->setDefaultLang($newlang);
516  }
517 
518  // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
519  $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
520  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
521  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
522  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
523  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
524  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
525  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
526  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
527  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
528  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y');
529  // Only for template invoice
530  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour');
531  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($nextdatewhen, 'dayhour');
532  $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($previousdaynextdatewhen, 'dayhour');
533  $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
534  $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
535 
536  complete_substitutions_array($substitutionarray, $outputlangs);
537 
538  $this->note_public = make_substitutions($this->note_public, $substitutionarray);
539  $this->note_private = make_substitutions($this->note_private, $substitutionarray);
540  }
541 
542  // Define due date if not already defined
543  if (! empty($forceduedate)) {
544  $this->date_echeance = $forceduedate;
545  }
546 
547  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
548  $sql .= "ref";
549  $sql .= ", ref_supplier";
550  $sql .= ", ref_ext";
551  $sql .= ", entity";
552  $sql .= ", type";
553  $sql .= ", libelle";
554  $sql .= ", fk_soc";
555  $sql .= ", datec";
556  $sql .= ", datef";
557  $sql .= ", fk_projet";
558  $sql .= ", fk_cond_reglement";
559  $sql .= ", fk_mode_reglement";
560  $sql .= ", fk_account";
561  $sql .= ", note_private";
562  $sql .= ", note_public";
563  $sql .= ", fk_user_author";
564  $sql .= ", date_lim_reglement";
565  $sql .= ", fk_incoterms, location_incoterms";
566  $sql .= ", fk_multicurrency";
567  $sql .= ", multicurrency_code";
568  $sql .= ", multicurrency_tx";
569  $sql .= ", fk_facture_source";
570  $sql .= ", fk_fac_rec_source";
571  $sql .= ")";
572  $sql .= " VALUES (";
573  $sql .= "'(PROV)'";
574  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
575  $sql .= ", '".$this->db->escape($this->ref_ext)."'";
576  $sql .= ", ".((int) $conf->entity);
577  $sql .= ", '".$this->db->escape($this->type)."'";
578  $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
579  $sql .= ", ".((int) $this->socid);
580  $sql .= ", '".$this->db->idate($now)."'";
581  $sql .= ", '".$this->db->idate($this->date)."'";
582  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
583  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
584  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
585  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
586  $sql .= ", '".$this->db->escape($this->note_private)."'";
587  $sql .= ", '".$this->db->escape($this->note_public)."'";
588  $sql .= ", ".((int) $user->id).",";
589  $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
590  $sql .= ", ".(int) $this->fk_incoterms;
591  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
592  $sql .= ", ".(int) $this->fk_multicurrency;
593  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
594  $sql .= ", ".(double) $this->multicurrency_tx;
595  $sql .= ", ".(isset($this->fk_facture_source) ? $this->fk_facture_source : "NULL");
596  $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
597  $sql .= ")";
598 
599  dol_syslog(get_class($this)."::create", LOG_DEBUG);
600  $resql = $this->db->query($sql);
601  if ($resql) {
602  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
603 
604  // Update ref with new one
605  $this->ref = '(PROV'.$this->id.')';
606  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
607 
608  dol_syslog(get_class($this)."::create", LOG_DEBUG);
609  $resql = $this->db->query($sql);
610  if (!$resql) {
611  $error++;
612  }
613 
614  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
615  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
616  }
617 
618  // Add object linked
619  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
620  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
621  if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
622  foreach ($tmp_origin_id as $origin_id) {
623  $ret = $this->add_object_linked($origin, $origin_id);
624  if (!$ret) {
625  dol_print_error($this->db);
626  $error++;
627  }
628  }
629  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
630  {
631  $origin_id = $tmp_origin_id;
632  $ret = $this->add_object_linked($origin, $origin_id);
633  if (!$ret) {
634  dol_print_error($this->db);
635  $error++;
636  }
637  }
638  }
639  }
640 
641  if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
642  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
643  foreach ($this->lines as $i => $val) {
644  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
645  $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').')';
646 
647  $resql_insert = $this->db->query($sql);
648  if ($resql_insert) {
649  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
650 
651  $res = $this->updateline(
652  $idligne,
653  $this->lines[$i]->description,
654  $this->lines[$i]->pu_ht,
655  $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
656  $this->lines[$i]->localtax1_tx,
657  $this->lines[$i]->localtax2_tx,
658  $this->lines[$i]->qty,
659  $this->lines[$i]->fk_product,
660  'HT',
661  (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
662  $this->lines[$i]->product_type,
663  $this->lines[$i]->remise_percent,
664  false,
665  $this->lines[$i]->date_start,
666  $this->lines[$i]->date_end,
667  $this->lines[$i]->array_options,
668  $this->lines[$i]->fk_unit,
669  $this->lines[$i]->multicurrency_subprice,
670  $this->lines[$i]->ref_supplier
671  );
672  } else {
673  $this->error = $this->db->lasterror();
674  $this->db->rollback();
675  return -5;
676  }
677  }
678  } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
679  dol_syslog("There is ".count($this->lines)." lines that are array lines");
680  foreach ($this->lines as $i => $val) {
681  $line = $this->lines[$i];
682 
683  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
684  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
685  if (!is_object($line)) {
686  $line = (object) $line;
687  }
688 
689  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
690  $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').')';
691 
692  $resql_insert = $this->db->query($sql);
693  if ($resql_insert) {
694  $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
695 
696  $this->updateline(
697  $idligne,
698  $line->description,
699  $line->pu_ht,
700  $line->tva_tx,
701  $line->localtax1_tx,
702  $line->localtax2_tx,
703  $line->qty,
704  $line->fk_product,
705  'HT',
706  (!empty($line->info_bits) ? $line->info_bits : ''),
707  $line->product_type,
708  $line->remise_percent,
709  0,
710  $line->date_start,
711  $line->date_end,
712  $line->array_options,
713  $line->fk_unit,
714  $line->multicurrency_subprice,
715  $line->ref_supplier
716  );
717  } else {
718  $this->error = $this->db->lasterror();
719  $this->db->rollback();
720  return -5;
721  }
722  }
723  }
724 
725  /*
726  * Insert lines of template invoices
727  */
728  if (! $error && $this->fac_rec > 0) {
729  foreach ($_facrec->lines as $i => $val) {
730  if ($_facrec->lines[$i]->fk_product) {
731  $prod = new Product($this->db);
732  $res = $prod->fetch($_facrec->lines[$i]->fk_product);
733  }
734 
735  // For line from template invoice, we use data from template invoice
736  /*
737  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
738  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
739  if (empty($tva_tx)) $tva_npr=0;
740  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
741  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
742  */
743  $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
744  $tva_npr = $_facrec->lines[$i]->info_bits;
745  if (empty($tva_tx)) {
746  $tva_npr = 0;
747  }
748  $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
749  $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
750 
751  $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
752  $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
753 
754  // If buyprice not defined from template invoice, we try to guess the best value
755  if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
756  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
757  $producttmp = new ProductFournisseur($this->db);
758  $producttmp->fetch($_facrec->lines[$i]->fk_product);
759 
760  // If margin module defined on costprice, we try the costprice
761  // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
762  // else we get the best supplier price
763  if ($conf->global->MARGIN_TYPE == 'costprice' && ! empty($producttmp->cost_price)) {
764  $buyprice = $producttmp->cost_price;
765  } elseif (! empty($conf->stock->enabled) && ($conf->global->MARGIN_TYPE == 'costprice' || $conf->global->MARGIN_TYPE == 'pmp') && ! empty($producttmp->pmp)) {
766  $buyprice = $producttmp->pmp;
767  } else {
768  if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
769  if ($producttmp->product_fourn_price_id > 0) {
770  $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
771  }
772  }
773  }
774  }
775 
776  $result_insert = $this->addline(
777  $_facrec->lines[$i]->description,
778  $_facrec->lines[$i]->pu_ht,
779  $tva_tx,
780  $localtax1_tx,
781  $localtax2_tx,
782  $_facrec->lines[$i]->qty,
783  $_facrec->lines[$i]->fk_product,
784  $_facrec->lines[$i]->remise_percent,
785  ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
786  ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
787  0,
788  $_facrec->lines[$i]->info_bits,
789  'HT',
790  0,
791  $_facrec->lines[$i]->rang,
792  false,
793  $_facrec->lines[$i]->array_options,
794  $_facrec->lines[$i]->fk_unit,
795  0,
796  0,
797  $_facrec->lines[$i]->ref_supplier,
798  $_facrec->lines[$i]->special_code,
799  0,
800  0
801  );
802  if ($result_insert < 0) {
803  $error++;
804  $this->error = $this->db->error();
805  break;
806  }
807  }
808  }
809 
810 
811  // Update total price
812  $result = $this->update_price();
813  if ($result > 0) {
814  // Actions on extra fields
815  if (!$error) {
816  $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
817  if ($result < 0) {
818  $error++;
819  }
820  }
821 
822  if (!$error) {
823  // Call trigger
824  $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
825  if ($result < 0) {
826  $error++;
827  }
828  // End call triggers
829  }
830 
831  if (!$error) {
832  $this->db->commit();
833  return $this->id;
834  } else {
835  $this->db->rollback();
836  return -4;
837  }
838  } else {
839  $this->error = $langs->trans('FailedToUpdatePrice');
840  $this->db->rollback();
841  return -3;
842  }
843  } else {
844  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
845  $this->error = $langs->trans('ErrorRefAlreadyExists');
846  $this->db->rollback();
847  return -1;
848  } else {
849  $this->error = $this->db->lasterror();
850  $this->db->rollback();
851  return -2;
852  }
853  }
854  }
855 
863  public function fetch($id = '', $ref = '')
864  {
865  global $langs;
866 
867  $sql = "SELECT";
868  $sql .= " t.rowid,";
869  $sql .= " t.ref,";
870  $sql .= " t.ref_supplier,";
871  $sql .= " t.ref_ext,";
872  $sql .= " t.entity,";
873  $sql .= " t.type,";
874  $sql .= " t.fk_soc,";
875  $sql .= " t.datec,";
876  $sql .= " t.datef,";
877  $sql .= " t.tms,";
878  $sql .= " t.libelle as label,";
879  $sql .= " t.paye,";
880  $sql .= " t.close_code,";
881  $sql .= " t.close_note,";
882  $sql .= " t.tva,";
883  $sql .= " t.localtax1,";
884  $sql .= " t.localtax2,";
885  $sql .= " t.total_ht,";
886  $sql .= " t.total_tva,";
887  $sql .= " t.total_ttc,";
888  $sql .= " t.fk_statut as status,";
889  $sql .= " t.fk_user_author,";
890  $sql .= " t.fk_user_valid,";
891  $sql .= " t.fk_facture_source,";
892  $sql .= " t.fk_fac_rec_source,";
893  $sql .= " t.fk_projet as fk_project,";
894  $sql .= " t.fk_cond_reglement,";
895  $sql .= " t.fk_account,";
896  $sql .= " t.fk_mode_reglement,";
897  $sql .= " t.date_lim_reglement,";
898  $sql .= " t.note_private,";
899  $sql .= " t.note_public,";
900  $sql .= " t.model_pdf,";
901  $sql .= " t.import_key,";
902  $sql .= " t.extraparams,";
903  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
904  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
905  $sql .= ' s.nom as socnom, s.rowid as socid,';
906  $sql .= ' t.fk_incoterms, t.location_incoterms,';
907  $sql .= " i.libelle as label_incoterms,";
908  $sql .= ' t.fk_transport_mode,';
909  $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
910  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
911  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
912  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
913  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
914  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
915  if ($id) {
916  $sql .= " WHERE t.rowid=".((int) $id);
917  }
918  if ($ref) {
919  $sql .= " WHERE t.ref='".$this->db->escape($ref)."' AND t.entity IN (".getEntity('supplier_invoice').")";
920  }
921 
922  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
923  $resql = $this->db->query($sql);
924  if ($resql) {
925  if ($this->db->num_rows($resql)) {
926  $obj = $this->db->fetch_object($resql);
927 
928  $this->id = $obj->rowid;
929  $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
930 
931  $this->ref_supplier = $obj->ref_supplier;
932  $this->ref_ext = $obj->ref_ext;
933  $this->entity = $obj->entity;
934  $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
935  $this->fk_soc = $obj->fk_soc;
936  $this->datec = $this->db->jdate($obj->datec);
937  $this->date = $this->db->jdate($obj->datef);
938  $this->datep = $this->db->jdate($obj->datef);
939  $this->tms = $this->db->jdate($obj->tms);
940  $this->libelle = $obj->label; // deprecated
941  $this->label = $obj->label;
942  $this->paye = $obj->paye;
943  $this->paid = $obj->paye;
944  $this->close_code = $obj->close_code;
945  $this->close_note = $obj->close_note;
946  $this->total_localtax1 = $obj->localtax1;
947  $this->total_localtax2 = $obj->localtax2;
948  $this->total_ht = $obj->total_ht;
949  $this->total_tva = $obj->total_tva;
950  $this->total_ttc = $obj->total_ttc;
951  $this->status = $obj->status;
952  $this->statut = $obj->status; // For backward compatibility
953  $this->fk_statut = $obj->status; // For backward compatibility
954  $this->fk_user_author = $obj->fk_user_author;
955  $this->author = $obj->fk_user_author;
956  $this->fk_user_valid = $obj->fk_user_valid;
957  $this->fk_facture_source = $obj->fk_facture_source;
958  $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
959  $this->fk_project = $obj->fk_project;
960  $this->cond_reglement_id = $obj->fk_cond_reglement;
961  $this->cond_reglement_code = $obj->cond_reglement_code;
962  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
963  $this->cond_reglement_label = $obj->cond_reglement_label;
964  $this->cond_reglement_doc = $obj->cond_reglement_doc;
965  $this->fk_account = $obj->fk_account;
966  $this->mode_reglement_id = $obj->fk_mode_reglement;
967  $this->mode_reglement_code = $obj->mode_reglement_code;
968  $this->mode_reglement = $obj->mode_reglement_label;
969  $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
970  $this->note = $obj->note_private; // deprecated
971  $this->note_private = $obj->note_private;
972  $this->note_public = $obj->note_public;
973  $this->model_pdf = $obj->model_pdf;
974  $this->modelpdf = $obj->model_pdf; // deprecated
975  $this->import_key = $obj->import_key;
976 
977  //Incoterms
978  $this->fk_incoterms = $obj->fk_incoterms;
979  $this->location_incoterms = $obj->location_incoterms;
980  $this->label_incoterms = $obj->label_incoterms;
981  $this->transport_mode_id = $obj->fk_transport_mode;
982 
983  // Multicurrency
984  $this->fk_multicurrency = $obj->fk_multicurrency;
985  $this->multicurrency_code = $obj->multicurrency_code;
986  $this->multicurrency_tx = $obj->multicurrency_tx;
987  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
988  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
989  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
990 
991  $this->extraparams = (array) json_decode($obj->extraparams, true);
992 
993  $this->socid = $obj->socid;
994  $this->socnom = $obj->socnom;
995 
996  // Retrieve all extrafield
997  // fetch optionals attributes and labels
998  $this->fetch_optionals();
999 
1000  if ($this->statut == self::STATUS_DRAFT) {
1001  $this->brouillon = 1;
1002  }
1003 
1004  $result = $this->fetch_lines();
1005  if ($result < 0) {
1006  $this->error = $this->db->lasterror();
1007  return -3;
1008  }
1009  } else {
1010  $this->error = 'Bill with id '.$id.' not found';
1011  dol_syslog(get_class($this).'::fetch '.$this->error);
1012  return 0;
1013  }
1014 
1015  $this->db->free($resql);
1016  return 1;
1017  } else {
1018  $this->error = "Error ".$this->db->lasterror();
1019  return -1;
1020  }
1021  }
1022 
1023 
1024  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1030  public function fetch_lines()
1031  {
1032  // phpcs:enable
1033  $this->lines = array();
1034 
1035  $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';
1036  $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';
1037  $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';
1038  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.description as product_desc';
1039  $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';
1040  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1041  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1042  $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1043  $sql .= ' ORDER BY f.rang, f.rowid';
1044 
1045  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1046 
1047  $resql_rows = $this->db->query($sql);
1048  if ($resql_rows) {
1049  $num_rows = $this->db->num_rows($resql_rows);
1050  if ($num_rows) {
1051  $i = 0;
1052  while ($i < $num_rows) {
1053  $obj = $this->db->fetch_object($resql_rows);
1054 
1055  $line = new SupplierInvoiceLine($this->db);
1056 
1057  $line->id = $obj->rowid;
1058  $line->rowid = $obj->rowid;
1059  $line->description = $obj->description;
1060  $line->date_start = $obj->date_start;
1061  $line->date_end = $obj->date_end;
1062 
1063  $line->product_ref = $obj->product_ref;
1064  $line->ref = $obj->product_ref;
1065  $line->ref_supplier = $obj->ref_supplier;
1066  $line->libelle = $obj->label;
1067  $line->label = $obj->label;
1068  $line->product_desc = $obj->product_desc;
1069  $line->subprice = $obj->pu_ht;
1070  $line->pu_ht = $obj->pu_ht;
1071  $line->pu_ttc = $obj->pu_ttc;
1072 
1073  $line->vat_src_code = $obj->vat_src_code;
1074  $line->tva_tx = $obj->tva_tx;
1075  $line->localtax1_tx = $obj->localtax1_tx;
1076  $line->localtax2_tx = $obj->localtax2_tx;
1077  $line->localtax1_type = $obj->localtax1_type;
1078  $line->localtax2_type = $obj->localtax2_type;
1079  $line->qty = $obj->qty;
1080  $line->remise_percent = $obj->remise_percent;
1081  $line->fk_remise_except = $obj->fk_remise_except;
1082  //$line->tva = $obj->total_tva; // deprecated
1083  $line->total_ht = $obj->total_ht;
1084  $line->total_ttc = $obj->total_ttc;
1085  $line->total_tva = $obj->total_tva;
1086  $line->total_localtax1 = $obj->total_localtax1;
1087  $line->total_localtax2 = $obj->total_localtax2;
1088  $line->fk_facture_fourn = $obj->fk_facture_fourn;
1089  $line->fk_product = $obj->fk_product;
1090  $line->product_type = $obj->product_type;
1091  $line->product_label = $obj->label;
1092  $line->info_bits = $obj->info_bits;
1093  $line->fk_parent_line = $obj->fk_parent_line;
1094  $line->special_code = $obj->special_code;
1095  $line->rang = $obj->rang;
1096  $line->fk_unit = $obj->fk_unit;
1097 
1098  // Accountancy
1099  $line->code_ventilation = $obj->fk_code_ventilation;
1100  $line->fk_accounting_account = $obj->fk_code_ventilation;
1101 
1102  // Multicurrency
1103  $line->fk_multicurrency = $obj->fk_multicurrency;
1104  $line->multicurrency_code = $obj->multicurrency_code;
1105  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1106  $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1107  $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1108  $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1109 
1110  // Extra fields
1111  $line->fetch_optionals();
1112 
1113  $this->lines[$i] = $line;
1114 
1115  $i++;
1116  }
1117  }
1118  $this->db->free($resql_rows);
1119  return 1;
1120  } else {
1121  $this->error = $this->db->error();
1122  return -3;
1123  }
1124  }
1125 
1126 
1134  public function update($user = null, $notrigger = 0)
1135  {
1136  global $conf, $langs;
1137  $error = 0;
1138 
1139  // Clean parameters
1140  if (empty($this->type)) {
1141  $this->type = self::TYPE_STANDARD;
1142  }
1143  if (isset($this->ref)) {
1144  $this->ref = trim($this->ref);
1145  }
1146  if (isset($this->ref_supplier)) {
1147  $this->ref_supplier = trim($this->ref_supplier);
1148  }
1149  if (isset($this->ref_ext)) {
1150  $this->ref_ext = trim($this->ref_ext);
1151  }
1152  if (isset($this->entity)) {
1153  $this->entity = trim($this->entity);
1154  }
1155  if (isset($this->type)) {
1156  $this->type = trim($this->type);
1157  }
1158  if (isset($this->fk_soc)) {
1159  $this->fk_soc = trim($this->fk_soc);
1160  }
1161  if (isset($this->label)) {
1162  $this->label = trim($this->label);
1163  }
1164  if (isset($this->libelle)) {
1165  $this->libelle = trim($this->libelle); // deprecated
1166  }
1167  if (isset($this->paye)) {
1168  $this->paye = trim($this->paye);
1169  }
1170  if (isset($this->close_code)) {
1171  $this->close_code = trim($this->close_code);
1172  }
1173  if (isset($this->close_note)) {
1174  $this->close_note = trim($this->close_note);
1175  }
1176  if (isset($this->localtax1)) {
1177  $this->localtax1 = trim($this->localtax1);
1178  }
1179  if (isset($this->localtax2)) {
1180  $this->localtax2 = trim($this->localtax2);
1181  }
1182  if (empty($this->total_ht)) {
1183  $this->total_ht = 0;
1184  }
1185  if (empty($this->total_tva)) {
1186  $this->total_tva = 0;
1187  }
1188  // if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
1189  // if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
1190  if (isset($this->total_ttc)) {
1191  $this->total_ttc = trim($this->total_ttc);
1192  }
1193  if (isset($this->statut)) {
1194  $this->statut = (int) $this->statut;
1195  }
1196  if (isset($this->status)) {
1197  $this->status = (int) $this->status;
1198  }
1199  if (isset($this->author)) {
1200  $this->author = trim($this->author);
1201  }
1202  if (isset($this->fk_user_valid)) {
1203  $this->fk_user_valid = trim($this->fk_user_valid);
1204  }
1205  if (isset($this->fk_facture_source)) {
1206  $this->fk_facture_source = trim($this->fk_facture_source);
1207  }
1208  if (isset($this->fk_project)) {
1209  if (empty($this->fk_project)) $this->fk_project = null;
1210  else $this->fk_project = intval($this->fk_project);
1211  }
1212  if (isset($this->cond_reglement_id)) {
1213  $this->cond_reglement_id = trim($this->cond_reglement_id);
1214  }
1215  if (isset($this->note_private)) {
1216  $this->note = trim($this->note_private);
1217  }
1218  if (isset($this->note_public)) {
1219  $this->note_public = trim($this->note_public);
1220  }
1221  if (isset($this->model_pdf)) {
1222  $this->model_pdf = trim($this->model_pdf);
1223  }
1224  if (isset($this->import_key)) {
1225  $this->import_key = trim($this->import_key);
1226  }
1227 
1228 
1229  // Check parameters
1230  // Put here code to add control on parameters values
1231 
1232  // Update request
1233  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1234  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1235  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1236  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1237  $sql .= " entity=".(isset($this->entity) ? $this->entity : "null").",";
1238  $sql .= " type=".(isset($this->type) ? $this->type : "null").",";
1239  $sql .= " fk_soc=".(isset($this->fk_soc) ? $this->fk_soc : "null").",";
1240  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1241  $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1242  if (dol_strlen($this->tms) != 0) {
1243  $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1244  }
1245  $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1246  $sql .= " paye=".(isset($this->paye) ? $this->paye : "null").",";
1247  $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1248  $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1249  //$sql .= " tva=".(isset($this->tva) ? $this->tva : "null").",";
1250  $sql .= " localtax1=".(isset($this->localtax1) ? $this->localtax1 : "null").",";
1251  $sql .= " localtax2=".(isset($this->localtax2) ? $this->localtax2 : "null").",";
1252  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1253  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1254  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1255  $sql .= " fk_statut=".(isset($this->status) ? $this->status : (isset($this->statut) ? $this->statut : "null")).",";
1256  $sql .= " fk_user_author=".(isset($this->author) ? $this->author : "null").",";
1257  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
1258  $sql .= " fk_facture_source=".(isset($this->fk_facture_source) ? $this->fk_facture_source : "null").",";
1259  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1260  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1261  $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1262  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1263  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1264  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1265  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1266  $sql .= " WHERE rowid=".((int) $this->id);
1267 
1268  $this->db->begin();
1269 
1270  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1271  $resql = $this->db->query($sql);
1272 
1273  if (!$resql) {
1274  $error++;
1275 
1276  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1277  $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1278  } else {
1279  $this->errors[] = "Error ".$this->db->lasterror();
1280  }
1281  }
1282 
1283  if (!$error) {
1284  $result = $this->insertExtraFields();
1285  if ($result < 0) {
1286  $error++;
1287  }
1288  }
1289 
1290  if (!$error) {
1291  if (!$notrigger) {
1292  // Call trigger
1293  $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1294  if ($result < 0) {
1295  $error++;
1296  }
1297  // End call triggers
1298  }
1299  }
1300 
1301  // Commit or rollback
1302  if ($error) {
1303  foreach ($this->errors as $errmsg) {
1304  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1305  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1306  }
1307  $this->db->rollback();
1308  return -1 * $error;
1309  } else {
1310  $this->db->commit();
1311  return 1;
1312  }
1313  }
1314 
1315  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1322  public function insert_discount($idremise)
1323  {
1324  // phpcs:enable
1325  global $conf, $langs;
1326 
1327  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1328  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1329 
1330  $this->db->begin();
1331 
1332  $remise = new DiscountAbsolute($this->db);
1333  $result = $remise->fetch($idremise);
1334 
1335  if ($result > 0) {
1336  if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1337  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1338  $this->db->rollback();
1339  return -5;
1340  }
1341 
1342  $facligne = new SupplierInvoiceLine($this->db);
1343  $facligne->fk_facture_fourn = $this->id;
1344  $facligne->fk_remise_except = $remise->id;
1345  $facligne->desc = $remise->description; // Description ligne
1346  $facligne->vat_src_code = $remise->vat_src_code;
1347  $facligne->tva_tx = $remise->tva_tx;
1348  $facligne->subprice = -$remise->amount_ht;
1349  $facligne->fk_product = 0; // Id produit predefini
1350  $facligne->product_type = 0;
1351  $facligne->qty = 1;
1352  $facligne->remise_percent = 0;
1353  $facligne->rang = -1;
1354  $facligne->info_bits = 2;
1355 
1356  if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
1357  $facligne->rang = 1;
1358  $linecount = count($this->lines);
1359  for ($ii = 1; $ii <= $linecount; $ii++) {
1360  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii+1);
1361  }
1362  }
1363 
1364  // Get buy/cost price of invoice that is source of discount
1365  if ($remise->fk_invoice_supplier_source > 0) {
1366  $srcinvoice = new FactureFournisseur($this->db);
1367  $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1368  $totalcostpriceofinvoice = 0;
1369  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1370  $formmargin = new FormMargin($this->db);
1371  $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1372  $facligne->pa_ht = $arraytmp['pa_total'];
1373  }
1374 
1375  $facligne->total_ht = -$remise->amount_ht;
1376  $facligne->total_tva = -$remise->amount_tva;
1377  $facligne->total_ttc = -$remise->amount_ttc;
1378 
1379  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1380  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1381  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1382  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1383 
1384  $lineid = $facligne->insert();
1385  if ($lineid > 0) {
1386  $result = $this->update_price(1);
1387  if ($result > 0) {
1388  // Create link between discount and invoice line
1389  $result = $remise->link_to_invoice($lineid, 0, 'supplier');
1390  if ($result < 0) {
1391  $this->error = $remise->error;
1392  $this->db->rollback();
1393  return -4;
1394  }
1395 
1396  $this->db->commit();
1397  return 1;
1398  } else {
1399  $this->error = $facligne->error;
1400  $this->db->rollback();
1401  return -1;
1402  }
1403  } else {
1404  $this->error = $facligne->error;
1405  $this->db->rollback();
1406  return -2;
1407  }
1408  } else {
1409  $this->db->rollback();
1410  return -3;
1411  }
1412  }
1413 
1414 
1422  public function delete(User $user, $notrigger = 0)
1423  {
1424  global $langs, $conf;
1425 
1426  $rowid = $this->id;
1427 
1428  dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1429 
1430  // TODO Test if there is at least on payment. If yes, refuse to delete.
1431 
1432  $error = 0;
1433  $this->db->begin();
1434 
1435  if (!$error && !$notrigger) {
1436  // Call trigger
1437  $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1438  if ($result < 0) {
1439  $this->db->rollback();
1440  return -1;
1441  }
1442  // Fin appel triggers
1443  }
1444 
1445  if (!$error) {
1446  // If invoice was converted into a discount not yet consumed, we remove discount
1447  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1448  $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1449  $sql .= ' AND fk_invoice_supplier_line IS NULL';
1450  $resql = $this->db->query($sql);
1451 
1452  // If invoice has consumned discounts
1453  $this->fetch_lines();
1454  $list_rowid_det = array();
1455  foreach ($this->lines as $key => $invoiceline) {
1456  $list_rowid_det[] = $invoiceline->rowid;
1457  }
1458 
1459  // Consumned discounts are freed
1460  if (count($list_rowid_det)) {
1461  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1462  $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1463  $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(join(',', $list_rowid_det)).')';
1464 
1465  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1466  if (!$this->db->query($sql)) {
1467  $error++;
1468  }
1469  }
1470  }
1471 
1472  if (!$error) {
1473  $main = MAIN_DB_PREFIX.'facture_fourn_det';
1474  $ef = $main."_extrafields";
1475  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1476  $resqlef = $this->db->query($sqlef);
1477  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1478  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1479  $resql = $this->db->query($sql);
1480  if ($resqlef && $resql) {
1481  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1482  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1483  $resql2 = $this->db->query($sql);
1484  if (!$resql2) {
1485  $error++;
1486  }
1487  } else {
1488  $error++;
1489  }
1490  }
1491 
1492  if (!$error) {
1493  // Delete linked object
1494  $res = $this->deleteObjectLinked();
1495  if ($res < 0) {
1496  $error++;
1497  }
1498  }
1499 
1500  if (!$error) {
1501  // Delete linked object
1502  $res = $this->deleteObjectLinked();
1503  if ($res < 0) {
1504  $error++;
1505  }
1506  }
1507 
1508  if (!$error) {
1509  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1510  $this->deleteEcmFiles();
1511 
1512  // We remove directory
1513  if ($conf->fournisseur->facture->dir_output) {
1514  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1515 
1516  $ref = dol_sanitizeFileName($this->ref);
1517  $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1518  $file = $dir."/".$ref.".pdf";
1519  if (file_exists($file)) {
1520  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1521  $this->error = 'ErrorFailToDeleteFile';
1522  $error++;
1523  }
1524  }
1525  if (file_exists($dir)) {
1526  $res = @dol_delete_dir_recursive($dir);
1527 
1528  if (!$res) {
1529  $this->error = 'ErrorFailToDeleteDir';
1530  $error++;
1531  }
1532  }
1533  }
1534  }
1535 
1536  // Remove extrafields
1537  if (!$error) {
1538  $result = $this->deleteExtraFields();
1539  if ($result < 0) {
1540  $error++;
1541  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1542  }
1543  }
1544 
1545  if (!$error) {
1546  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1547  $this->db->commit();
1548  return 1;
1549  } else {
1550  $this->error = $this->db->lasterror();
1551  $this->db->rollback();
1552  return -$error;
1553  }
1554  }
1555 
1556 
1557  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1568  public function set_paid($user, $close_code = '', $close_note = '')
1569  {
1570  // phpcs:enable
1571  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1572  return $this->setPaid($user, $close_code, $close_note);
1573  }
1574 
1583  public function setPaid($user, $close_code = '', $close_note = '')
1584  {
1585  $error = 0;
1586 
1587  if ($this->paye != 1) {
1588  $this->db->begin();
1589 
1590  $now = dol_now();
1591 
1592  dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1593 
1594  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1595  $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1596  if (!$close_code) {
1597  $sql .= ', paye=1';
1598  }
1599  if ($close_code) {
1600  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1601  }
1602  if ($close_note) {
1603  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1604  }
1605  $sql .= ', fk_user_closing = '.((int) $user->id);
1606  $sql .= ", date_closing = '".$this->db->idate($now)."'";
1607  $sql .= ' WHERE rowid = '.((int) $this->id);
1608 
1609  $resql = $this->db->query($sql);
1610  if ($resql) {
1611  // Call trigger
1612  $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1613  if ($result < 0) {
1614  $error++;
1615  }
1616  // End call triggers
1617  } else {
1618  $error++;
1619  $this->error = $this->db->error();
1620  dol_print_error($this->db);
1621  }
1622 
1623  if (!$error) {
1624  $this->db->commit();
1625  return 1;
1626  } else {
1627  $this->db->rollback();
1628  return -1;
1629  }
1630  } else {
1631  return 0;
1632  }
1633  }
1634 
1635  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1646  public function set_unpaid($user)
1647  {
1648  // phpcs:enable
1649  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1650  return $this->setUnpaid($user);
1651  }
1652 
1661  public function setUnpaid($user)
1662  {
1663  $error = 0;
1664 
1665  $this->db->begin();
1666 
1667  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1668  $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1669  $sql .= ' date_closing=null,';
1670  $sql .= ' fk_user_closing=null';
1671  $sql .= ' WHERE rowid = '.((int) $this->id);
1672 
1673  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1674  $resql = $this->db->query($sql);
1675  if ($resql) {
1676  // Call trigger
1677  $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1678  if ($result < 0) {
1679  $error++;
1680  }
1681  // End call triggers
1682  } else {
1683  $error++;
1684  $this->error = $this->db->error();
1685  dol_print_error($this->db);
1686  }
1687 
1688  if (!$error) {
1689  $this->db->commit();
1690  return 1;
1691  } else {
1692  $this->db->rollback();
1693  return -1;
1694  }
1695  }
1696 
1707  public function setCanceled($user, $close_code = '', $close_note = '')
1708  {
1709  dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1710 
1711  $this->db->begin();
1712 
1713  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1714  $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1715  if ($close_code) {
1716  $sql .= ", close_code='".$this->db->escape($close_code)."'";
1717  }
1718  if ($close_note) {
1719  $sql .= ", close_note='".$this->db->escape($close_note)."'";
1720  }
1721  $sql .= " WHERE rowid = ".((int) $this->id);
1722 
1723  $resql = $this->db->query($sql);
1724  if ($resql) {
1725  // Bound discounts are deducted from the invoice
1726  // as they have not been used since the invoice is abandoned.
1727  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1728  $sql .= ' SET fk_invoice_supplier = NULL';
1729  $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1730 
1731  $resql = $this->db->query($sql);
1732  if ($resql) {
1733  // Call trigger
1734  $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1735  if ($result < 0) {
1736  $this->db->rollback();
1737  return -1;
1738  }
1739  // End call triggers
1740 
1741  $this->db->commit();
1742  return 1;
1743  } else {
1744  $this->error = $this->db->error()." sql=".$sql;
1745  $this->db->rollback();
1746  return -1;
1747  }
1748  } else {
1749  $this->error = $this->db->error()." sql=".$sql;
1750  $this->db->rollback();
1751  return -2;
1752  }
1753  }
1754 
1764  public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1765  {
1766  global $conf, $langs;
1767 
1768  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1769 
1770  $now = dol_now();
1771 
1772  $error = 0;
1773  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1774 
1775  // Force to have object complete for checks
1776  $this->fetch_thirdparty();
1777  $this->fetch_lines();
1778 
1779  // Check parameters
1780  if ($this->statut > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1781  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1782  return 0;
1783  }
1784  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1785  $langs->load("errors");
1786  $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1787  return -1;
1788  }
1789  if (count($this->lines) <= 0) {
1790  $langs->load("errors");
1791  $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1792  return -1;
1793  }
1794 
1795  $this->db->begin();
1796 
1797  // Define new ref
1798  if ($force_number) {
1799  $num = $force_number;
1800  } elseif (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1801  $num = $this->getNextNumRef($this->thirdparty);
1802  } else {
1803  $num = $this->ref;
1804  }
1805  $this->newref = dol_sanitizeFileName($num);
1806 
1807  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1808  $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1809  $sql .= " WHERE rowid = ".((int) $this->id);
1810 
1811  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1812  $resql = $this->db->query($sql);
1813  if ($resql) {
1814  // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1815  if (!$error && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1816  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1817  $langs->load("agenda");
1818 
1819  $cpt = count($this->lines);
1820  for ($i = 0; $i < $cpt; $i++) {
1821  if ($this->lines[$i]->fk_product > 0) {
1822  $this->line = $this->lines[$i];
1823  $mouvP = new MouvementStock($this->db);
1824  $mouvP->origin = &$this;
1825  $mouvP->setOrigin($this->element, $this->id);
1826  // We increase stock for product
1827  $up_ht_disc = $this->lines[$i]->pu_ht;
1828  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1829  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1830  }
1832  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1833  } else {
1834  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1835  }
1836  if ($result < 0) {
1837  $error++;
1838  }
1839  unset($this->line);
1840  }
1841  }
1842  }
1843 
1844  // Triggers call
1845  if (!$error && empty($notrigger)) {
1846  // Call trigger
1847  $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1848  if ($result < 0) {
1849  $error++;
1850  }
1851  // End call triggers
1852  }
1853 
1854  if (!$error) {
1855  $this->oldref = $this->ref;
1856 
1857  // Rename directory if dir was a temporary ref
1858  if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1859  // Now we rename also files into index
1860  $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)."'";
1861  $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;
1862  $resql = $this->db->query($sql);
1863  if (!$resql) {
1864  $error++; $this->error = $this->db->lasterror();
1865  }
1866 
1867  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1868  $oldref = dol_sanitizeFileName($this->ref);
1869  $newref = dol_sanitizeFileName($num);
1870  $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1871  $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref;
1872  if (!$error && file_exists($dirsource)) {
1873  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1874 
1875  if (@rename($dirsource, $dirdest)) {
1876  dol_syslog("Rename ok");
1877  // Rename docs starting with $oldref with $newref
1878  $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1879  foreach ($listoffiles as $fileentry) {
1880  $dirsource = $fileentry['name'];
1881  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1882  $dirsource = $fileentry['path'].'/'.$dirsource;
1883  $dirdest = $fileentry['path'].'/'.$dirdest;
1884  @rename($dirsource, $dirdest);
1885  }
1886  }
1887  }
1888  }
1889  }
1890 
1891  // Set new ref and define current statut
1892  if (!$error) {
1893  $this->ref = $num;
1894  $this->statut = self::STATUS_VALIDATED;
1895  //$this->date_validation=$now; this is stored into log table
1896  }
1897 
1898  if (!$error) {
1899  $this->db->commit();
1900  return 1;
1901  } else {
1902  $this->db->rollback();
1903  return -1;
1904  }
1905  } else {
1906  $this->error = $this->db->error();
1907  $this->db->rollback();
1908  return -1;
1909  }
1910  }
1911 
1919  public function setDraft($user, $idwarehouse = -1)
1920  {
1921  // phpcs:enable
1922  global $conf, $langs;
1923 
1924  $error = 0;
1925 
1926  if ($this->statut == self::STATUS_DRAFT) {
1927  dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1928  return 0;
1929  }
1930 
1931  dol_syslog(__METHOD__, LOG_DEBUG);
1932 
1933  $this->db->begin();
1934 
1935  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1936  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1937  $sql .= " WHERE rowid = ".((int) $this->id);
1938 
1939  $result = $this->db->query($sql);
1940  if ($result) {
1941  if (!$error) {
1942  $this->oldcopy = clone $this;
1943  }
1944 
1945  // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
1946  if ($result >= 0 && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) {
1947  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1948  $langs->load("agenda");
1949 
1950  $cpt = count($this->lines);
1951  for ($i = 0; $i < $cpt; $i++) {
1952  if ($this->lines[$i]->fk_product > 0) {
1953  $mouvP = new MouvementStock($this->db);
1954  $mouvP->origin = &$this;
1955  $mouvP->setOrigin($this->element, $this->id);
1956  // We increase stock for product
1958  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1959  } else {
1960  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1961  }
1962  }
1963  }
1964  }
1965  // Triggers call
1966  if (!$error && empty($notrigger)) {
1967  // Call trigger
1968  $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
1969  if ($result < 0) {
1970  $error++;
1971  }
1972  // End call triggers
1973  }
1974  if ($error == 0) {
1975  $this->db->commit();
1976  return 1;
1977  } else {
1978  $this->db->rollback();
1979  return -1;
1980  }
1981  } else {
1982  $this->error = $this->db->error();
1983  $this->db->rollback();
1984  return -1;
1985  }
1986  }
1987 
1988 
2022  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)
2023  {
2024  global $langs, $mysoc, $conf;
2025 
2026  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);
2027  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2028 
2029  if ($this->statut == self::STATUS_DRAFT) {
2030  // Clean parameters
2031  if (empty($remise_percent)) {
2032  $remise_percent = 0;
2033  }
2034  if (empty($qty)) {
2035  $qty = 0;
2036  }
2037  if (empty($info_bits)) {
2038  $info_bits = 0;
2039  }
2040  if (empty($rang)) {
2041  $rang = 0;
2042  }
2043  if (empty($ventil)) {
2044  $ventil = 0;
2045  }
2046  if (empty($txtva)) {
2047  $txtva = 0;
2048  }
2049  if (empty($txlocaltax1)) {
2050  $txlocaltax1 = 0;
2051  }
2052  if (empty($txlocaltax2)) {
2053  $txlocaltax2 = 0;
2054  }
2055 
2056  $remise_percent = price2num($remise_percent);
2057  $qty = price2num($qty);
2058  $pu = price2num($pu);
2059  if (!preg_match('/\((.*)\)/', $txtva)) {
2060  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2061  }
2062  $txlocaltax1 = price2num($txlocaltax1);
2063  $txlocaltax2 = price2num($txlocaltax2);
2064 
2065  if ($date_start && $date_end && $date_start > $date_end) {
2066  $langs->load("errors");
2067  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2068  return -1;
2069  }
2070 
2071  $this->db->begin();
2072 
2073  if ($fk_product > 0) {
2074  if (!empty($conf->global->SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY)) {
2075  // Check quantity is enough
2076  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2077  $prod = new Product($this->db);
2078  if ($prod->fetch($fk_product) > 0) {
2079  $product_type = $prod->type;
2080  $label = $prod->label;
2081  $fk_prod_fourn_price = 0;
2082 
2083  // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2084  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2085  $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
2086  if ($result > 0) {
2087  if (empty($pu)) {
2088  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2089  }
2090  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2091  // is remise percent not keyed but present for the product we add it
2092  if ($remise_percent == 0 && $prod->remise_percent != 0) {
2093  $remise_percent = $prod->remise_percent;
2094  }
2095  }
2096  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2097  $langs->load("errors");
2098  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2099  $this->db->rollback();
2100  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2101  //$pu = $prod->fourn_pu; // We do not overwrite unit price
2102  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2103  return -1;
2104  }
2105  if ($result == -1) {
2106  $langs->load("errors");
2107  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2108  $this->db->rollback();
2109  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2110  return -1;
2111  }
2112  if ($result < -1) {
2113  $this->error = $prod->error;
2114  $this->db->rollback();
2115  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2116  return -1;
2117  }
2118  } else {
2119  $this->error = $prod->error;
2120  $this->db->rollback();
2121  return -1;
2122  }
2123  }
2124  } else {
2125  $product_type = $type;
2126  }
2127 
2128  if (!empty($conf->multicurrency->enabled) && $pu_devise > 0) {
2129  $pu = 0;
2130  }
2131 
2132  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2133 
2134  // Clean vat code
2135  $reg = array();
2136  $vat_src_code = '';
2137  if (preg_match('/\((.*)\)/', $txtva, $reg)) {
2138  $vat_src_code = $reg[1];
2139  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2140  }
2141 
2142  // Calcul du total TTC et de la TVA pour la ligne a partir de
2143  // qty, pu, remise_percent et txtva
2144  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2145  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2146 
2147  $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);
2148  $total_ht = $tabprice[0];
2149  $total_tva = $tabprice[1];
2150  $total_ttc = $tabprice[2];
2151  $total_localtax1 = $tabprice[9];
2152  $total_localtax2 = $tabprice[10];
2153  $pu_ht = $tabprice[3];
2154 
2155  // MultiCurrency
2156  $multicurrency_total_ht = $tabprice[16];
2157  $multicurrency_total_tva = $tabprice[17];
2158  $multicurrency_total_ttc = $tabprice[18];
2159  $pu_ht_devise = $tabprice[19];
2160 
2161  // Check parameters
2162  if ($type < 0) {
2163  return -1;
2164  }
2165 
2166  if ($rang < 0) {
2167  $rangmax = $this->line_max();
2168  $rang = $rangmax + 1;
2169  }
2170 
2171  // Insert line
2172  $this->line = new SupplierInvoiceLine($this->db);
2173 
2174  $this->line->context = $this->context;
2175 
2176  $this->line->fk_facture_fourn = $this->id;
2177  //$this->line->label=$label; // deprecated
2178  $this->line->desc = $desc;
2179  $this->line->ref_supplier = $ref_supplier;
2180 
2181  $this->line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2182  $this->line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2183 
2184  $this->line->vat_src_code = $vat_src_code;
2185  $this->line->tva_tx = $txtva;
2186  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2187  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2188  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2189  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2190 
2191  $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
2192  $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
2193  $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
2194  $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
2195  $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
2196 
2197  $this->line->fk_product = $fk_product;
2198  $this->line->product_type = $type;
2199  $this->line->remise_percent = $remise_percent;
2200  $this->line->date_start = $date_start;
2201  $this->line->date_end = $date_end;
2202  $this->line->fk_code_ventilation = $ventil;
2203  $this->line->rang = $rang;
2204  $this->line->info_bits = $info_bits;
2205  $this->line->fk_remise_except = $fk_remise_except;
2206 
2207  $this->line->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
2208  $this->line->fk_parent_line = $fk_parent_line;
2209  $this->line->origin = $this->origin;
2210  $this->line->origin_id = $origin_id;
2211  $this->line->fk_unit = $fk_unit;
2212 
2213  // Multicurrency
2214  $this->line->fk_multicurrency = $this->fk_multicurrency;
2215  $this->line->multicurrency_code = $this->multicurrency_code;
2216  $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
2217 
2218  $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
2219  $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
2220  $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
2221 
2222  if (is_array($array_options) && count($array_options) > 0) {
2223  $this->line->array_options = $array_options;
2224  }
2225 
2226  $result = $this->line->insert($notrigger);
2227  if ($result > 0) {
2228  // Reorder if child line
2229  if (!empty($fk_parent_line)) {
2230  $this->line_order(true, 'DESC');
2231  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2232  $linecount = count($this->lines);
2233  for ($ii = $rang; $ii <= $linecount; $ii++) {
2234  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2235  }
2236  }
2237 
2238  // Mise a jour informations denormalisees au niveau de la facture meme
2239  $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.
2240  if ($result > 0) {
2241  $this->db->commit();
2242  return $this->line->id;
2243  } else {
2244  $this->error = $this->db->error();
2245  $this->db->rollback();
2246  return -1;
2247  }
2248  } else {
2249  $this->error = $this->line->error;
2250  $this->errors = $this->line->errors;
2251  $this->db->rollback();
2252  return -2;
2253  }
2254  } else {
2255  return 0;
2256  }
2257  }
2258 
2284  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)
2285  {
2286  global $mysoc, $langs;
2287 
2288  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);
2289  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2290 
2291  $pu = price2num($pu);
2292  $qty = price2num($qty);
2293  $remise_percent = price2num($remise_percent);
2294  $pu_devise = price2num($pu_devise);
2295 
2296  // Check parameters
2297  //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2298  if ($type < 0) {
2299  return -1;
2300  }
2301 
2302  if ($date_start && $date_end && $date_start > $date_end) {
2303  $langs->load("errors");
2304  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2305  return -1;
2306  }
2307 
2308  // Clean parameters
2309  if (empty($vatrate)) {
2310  $vatrate = 0;
2311  }
2312  if (empty($txlocaltax1)) {
2313  $txlocaltax1 = 0;
2314  }
2315  if (empty($txlocaltax2)) {
2316  $txlocaltax2 = 0;
2317  }
2318 
2319  $txlocaltax1 = price2num($txlocaltax1);
2320  $txlocaltax2 = price2num($txlocaltax2);
2321 
2322  // Calcul du total TTC et de la TVA pour la ligne a partir de
2323  // qty, pu, remise_percent et txtva
2324  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2325  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2326 
2327  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2328 
2329  $reg = array();
2330 
2331  // Clean vat code
2332  $vat_src_code = '';
2333  if (preg_match('/\((.*)\)/', $vatrate, $reg)) {
2334  $vat_src_code = $reg[1];
2335  $vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate); // Remove code into vatrate.
2336  }
2337 
2338  $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);
2339  $total_ht = $tabprice[0];
2340  $total_tva = $tabprice[1];
2341  $total_ttc = $tabprice[2];
2342  $pu_ht = $tabprice[3];
2343  $pu_tva = $tabprice[4];
2344  $pu_ttc = $tabprice[5];
2345  $total_localtax1 = $tabprice[9];
2346  $total_localtax2 = $tabprice[10];
2347 
2348  // MultiCurrency
2349  $multicurrency_total_ht = $tabprice[16];
2350  $multicurrency_total_tva = $tabprice[17];
2351  $multicurrency_total_ttc = $tabprice[18];
2352  $pu_ht_devise = $tabprice[19];
2353 
2354  if (empty($info_bits)) {
2355  $info_bits = 0;
2356  }
2357 
2358  //Fetch current line from the database and then clone the object and set it in $oldline property
2359  $line = new SupplierInvoiceLine($this->db);
2360  $line->fetch($id);
2361  $line->fetch_optionals();
2362 
2363  $staticline = clone $line;
2364 
2365  if ($idproduct) {
2366  $product = new Product($this->db);
2367  $result = $product->fetch($idproduct);
2368  $product_type = $product->type;
2369  } else {
2370  $idproduct = $staticline->fk_product;
2371  $product_type = $type;
2372  }
2373 
2374  $line->oldline = $staticline;
2375  $line->context = $this->context;
2376 
2377  $line->description = $desc;
2378 
2379  $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
2380  $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2381  $line->pu_ht = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2382  $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2383 
2384  $line->remise_percent = $remise_percent;
2385  $line->ref_supplier = $ref_supplier;
2386 
2387  $line->date_start = $date_start;
2388  $line->date_end = $date_end;
2389 
2390  $line->vat_src_code = $vat_src_code;
2391  $line->tva_tx = $vatrate;
2392  $line->localtax1_tx = $txlocaltax1;
2393  $line->localtax2_tx = $txlocaltax2;
2394  $line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2395  $line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2396 
2397  $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht);
2398  $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
2399  $line->total_localtax1 = $total_localtax1;
2400  $line->total_localtax2 = $total_localtax2;
2401  $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
2402 
2403  $line->fk_product = $idproduct;
2404  $line->product_type = $product_type;
2405  $line->info_bits = $info_bits;
2406  $line->fk_unit = $fk_unit;
2407  $line->rang = $rang;
2408 
2409  if (is_array($array_options) && count($array_options) > 0) {
2410  // We replace values in this->line->array_options only for entries defined into $array_options
2411  foreach ($array_options as $key => $value) {
2412  $line->array_options[$key] = $array_options[$key];
2413  }
2414  }
2415 
2416  // Multicurrency
2417  $line->multicurrency_subprice = $pu_ht_devise;
2418  $line->multicurrency_total_ht = $multicurrency_total_ht;
2419  $line->multicurrency_total_tva = $multicurrency_total_tva;
2420  $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2421 
2422  $res = $line->update($notrigger);
2423 
2424  if ($res < 1) {
2425  $this->errors[] = $line->error;
2426  } else {
2427  // Update total price into invoice record
2428  $res = $this->update_price('', 'auto', 0, $this->thirdparty);
2429  }
2430 
2431  return $res;
2432  }
2433 
2441  public function deleteline($rowid, $notrigger = 0)
2442  {
2443  if (!$rowid) {
2444  $rowid = $this->id;
2445  }
2446 
2447  $this->db->begin();
2448 
2449  // Free the discount linked to a line of invoice
2450  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2451  $sql .= ' SET fk_invoice_supplier_line = NULL';
2452  $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2453 
2454  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2455  $result = $this->db->query($sql);
2456  if (!$result) {
2457  $this->error = $this->db->error();
2458  $this->db->rollback();
2459  return -2;
2460  }
2461 
2462  $line = new SupplierInvoiceLine($this->db);
2463 
2464  if ($line->fetch($rowid) < 1) {
2465  return -1;
2466  }
2467 
2468  $res = $line->delete($notrigger);
2469 
2470  if ($res < 1) {
2471  $this->errors[] = $line->error;
2472  $this->db->rollback();
2473  return -3;
2474  } else {
2475  $res = $this->update_price();
2476 
2477  if ($res > 0) {
2478  $this->db->commit();
2479  return 1;
2480  } else {
2481  $this->db->rollback();
2482  $this->error = $this->db->lasterror();
2483  return -4;
2484  }
2485  }
2486  }
2487 
2488 
2495  public function info($id)
2496  {
2497  $sql = 'SELECT c.rowid, datec, tms as datem, ';
2498  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2499  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2500  $sql .= ' WHERE c.rowid = '.((int) $id);
2501 
2502  $result = $this->db->query($sql);
2503  if ($result) {
2504  if ($this->db->num_rows($result)) {
2505  $obj = $this->db->fetch_object($result);
2506 
2507  $this->id = $obj->rowid;
2508  if ($obj->fk_user_author) {
2509  $cuser = new User($this->db);
2510  $cuser->fetch($obj->fk_user_author);
2511  $this->user_creation = $cuser;
2512  }
2513  if ($obj->fk_user_valid) {
2514  $vuser = new User($this->db);
2515  $vuser->fetch($obj->fk_user_valid);
2516  $this->user_validation = $vuser;
2517  }
2518  if ($obj->fk_user_modif) {
2519  $muser = new User($this->db);
2520  $muser->fetch($obj->fk_user_modif);
2521  $this->user_modification = $muser;
2522  }
2523  $this->date_creation = $this->db->jdate($obj->datec);
2524  $this->date_modification = $this->db->jdate($obj->datem);
2525  //$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)
2526  }
2527  $this->db->free($result);
2528  } else {
2529  dol_print_error($this->db);
2530  }
2531  }
2532 
2533  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2542  public function list_replacable_supplier_invoices($socid = 0)
2543  {
2544  // phpcs:enable
2545  global $conf;
2546 
2547  $return = array();
2548 
2549  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2550  $sql .= " ff.rowid as rowidnext";
2551  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2552  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2553  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2554  $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2555  $sql .= " AND f.entity = ".$conf->entity;
2556  $sql .= " AND f.paye = 0"; // Pas classee payee completement
2557  $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2558  $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
2559  if ($socid > 0) {
2560  $sql .= " AND f.fk_soc = ".((int) $socid);
2561  }
2562  $sql .= " ORDER BY f.ref";
2563 
2564  dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2565  $resql = $this->db->query($sql);
2566  if ($resql) {
2567  while ($obj = $this->db->fetch_object($resql)) {
2568  $return[$obj->rowid] = array(
2569  'id' => $obj->rowid,
2570  'ref' => $obj->ref,
2571  'status' => $obj->fk_statut
2572  );
2573  }
2574  //print_r($return);
2575  return $return;
2576  } else {
2577  $this->error = $this->db->error();
2578  return -1;
2579  }
2580  }
2581 
2582  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2592  public function list_qualified_avoir_supplier_invoices($socid = 0)
2593  {
2594  // phpcs:enable
2595  global $conf;
2596 
2597  $return = array();
2598 
2599  $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.paye, pf.fk_paiementfourn";
2600  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2601  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2602  $sql .= " WHERE f.entity = ".$conf->entity;
2603  $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2604  $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2605  $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2606  $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2607  if ($socid > 0) {
2608  $sql .= " AND f.fk_soc = ".((int) $socid);
2609  }
2610  $sql .= " ORDER BY f.ref";
2611 
2612  dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2613  $resql = $this->db->query($sql);
2614  if ($resql) {
2615  while ($obj = $this->db->fetch_object($resql)) {
2616  $qualified = 0;
2617  if ($obj->fk_statut == self::STATUS_VALIDATED) {
2618  $qualified = 1;
2619  }
2620  if ($obj->fk_statut == self::STATUS_CLOSED) {
2621  $qualified = 1;
2622  }
2623  if ($qualified) {
2624  $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2625  $return[$obj->rowid] = array('ref'=>$obj->ref, 'status'=>$obj->fk_statut, 'type'=>$obj->type, 'paye'=>$obj->paye, 'paymentornot'=>$paymentornot);
2626  }
2627  }
2628 
2629  return $return;
2630  } else {
2631  $this->error = $this->db->error();
2632  return -1;
2633  }
2634  }
2635 
2636  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2643  public function load_board($user)
2644  {
2645  // phpcs:enable
2646  global $conf, $langs;
2647 
2648  $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2649  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2650  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2651  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2652  }
2653  $sql .= ' WHERE ff.paye = 0';
2654  $sql .= ' AND ff.fk_statut > 0';
2655  $sql .= " AND ff.entity = ".$conf->entity;
2656  if ($user->socid) {
2657  $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2658  }
2659 
2660  $resql = $this->db->query($sql);
2661  if ($resql) {
2662  $langs->load("bills");
2663  $now = dol_now();
2664 
2665  $response = new WorkboardResponse();
2666  $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2667  $response->label = $langs->trans("SupplierBillsToPay");
2668  $response->labelShort = $langs->trans("StatusToPay");
2669 
2670  $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2671  $response->img = img_object($langs->trans("Bills"), "bill");
2672 
2673  $facturestatic = new FactureFournisseur($this->db);
2674 
2675  while ($obj = $this->db->fetch_object($resql)) {
2676  $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2677  $facturestatic->statut = $obj->status; // For backward compatibility
2678  $facturestatic->status = $obj->status;
2679 
2680  $response->nbtodo++;
2681  $response->total += $obj->total_ht;
2682 
2683  if ($facturestatic->hasDelay()) {
2684  $response->nbtodolate++;
2685  $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2686  }
2687  }
2688 
2689  $this->db->free($resql);
2690  return $response;
2691  } else {
2692  dol_print_error($this->db);
2693  $this->error = $this->db->error();
2694  return -1;
2695  }
2696  }
2697 
2698 
2712  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2713  {
2714  global $langs, $conf, $user, $hookmanager;
2715 
2716  $result = '';
2717 
2718  if ($option == 'withdraw') {
2719  $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2720  } elseif ($option == 'document') {
2721  $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2722  } else {
2723  $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2724  }
2725 
2726  if ($short) {
2727  return $url;
2728  }
2729 
2730  if ($option !== 'nolink') {
2731  // Add param to save lastsearch_values or not
2732  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2733  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2734  $add_save_lastsearch_values = 1;
2735  }
2736  if ($add_save_lastsearch_values) {
2737  $url .= '&save_lastsearch_values=1';
2738  }
2739  }
2740 
2741  $picto = $this->picto;
2742  if ($this->type == self::TYPE_REPLACEMENT) {
2743  $picto .= 'r'; // Replacement invoice
2744  }
2745  if ($this->type == self::TYPE_CREDIT_NOTE) {
2746  $picto .= 'a'; // Credit note
2747  }
2748  if ($this->type == self::TYPE_DEPOSIT) {
2749  $picto .= 'd'; // Deposit invoice
2750  }
2751 
2752  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2753  if ($this->type == self::TYPE_REPLACEMENT) {
2754  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2755  } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2756  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2757  } elseif ($this->type == self::TYPE_DEPOSIT) {
2758  $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2759  }
2760  if (isset($this->status)) {
2761  $alreadypaid = -1;
2762  if (isset($this->alreadypaid)) {
2763  $alreadypaid = $this->alreadypaid;
2764  }
2765 
2766  $label .= ' '.$this->getLibStatut(5, $alreadypaid);
2767  }
2768  if (!empty($this->ref)) {
2769  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2770  }
2771  if (!empty($this->ref_supplier)) {
2772  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2773  }
2774  if (!empty($this->label)) {
2775  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2776  }
2777  if (!empty($this->date)) {
2778  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2779  }
2780  if (!empty($this->total_ht)) {
2781  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2782  }
2783  if (!empty($this->total_tva)) {
2784  $label .= '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2785  }
2786  if (!empty($this->total_ttc)) {
2787  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2788  }
2789  if ($moretitle) {
2790  $label .= ' - '.$moretitle;
2791  }
2792 
2793  $ref = $this->ref;
2794  if (empty($ref)) {
2795  $ref = $this->id;
2796  }
2797 
2798  $linkclose = '';
2799  if (empty($notooltip)) {
2800  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2801  $label = $langs->trans("ShowSupplierInvoice");
2802  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2803  }
2804  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2805  $linkclose .= ' class="classfortooltip"';
2806  }
2807 
2808  $linkstart = '<a href="'.$url.'"';
2809  $linkstart .= $linkclose.'>';
2810  $linkend = '</a>';
2811 
2812  $result .= $linkstart;
2813  if ($withpicto) {
2814  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2815  }
2816  if ($withpicto != 2) {
2817  $result .= ($max ?dol_trunc($ref, $max) : $ref);
2818  }
2819  $result .= $linkend;
2820 
2821  if ($addlinktonotes) {
2822  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2823  if ($txttoshow) {
2824  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2825  $result .= ' <span class="note inline-block">';
2826  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2827  $result .= img_picto('', 'note');
2828  $result .= '</a>';
2829  $result .= '</span>';
2830  }
2831  }
2832  global $action;
2833  $hookmanager->initHooks(array($this->element . 'dao'));
2834  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2835  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2836  if ($reshook > 0) {
2837  $result = $hookmanager->resPrint;
2838  } else {
2839  $result .= $hookmanager->resPrint;
2840  }
2841  return $result;
2842  }
2843 
2852  public function getNextNumRef($soc, $mode = 'next')
2853  {
2854  global $db, $langs, $conf;
2855  $langs->load("orders");
2856 
2857  // Clean parameters (if not defined or using deprecated value)
2858  if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) {
2859  $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2860  }
2861 
2862  $mybool = false;
2863 
2864  $file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2865  $classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2866 
2867  // Include file with class
2868  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2869 
2870  foreach ($dirmodels as $reldir) {
2871  $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2872 
2873  // Load file with numbering class (if found)
2874  $mybool |= @include_once $dir.$file;
2875  }
2876 
2877  if ($mybool === false) {
2878  dol_print_error('', "Failed to include file ".$file);
2879  return '';
2880  }
2881 
2882  $obj = new $classname();
2883  $numref = "";
2884  $numref = $obj->getNumRef($soc, $this, $mode);
2885 
2886  if ($numref != "") {
2887  return $numref;
2888  } else {
2889  $this->error = $obj->error;
2890  return -1;
2891  }
2892  }
2893 
2894 
2903  public function initAsSpecimen($option = '')
2904  {
2905  global $langs, $conf;
2906  include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2907 
2908  $now = dol_now();
2909 
2910  // Load array of products prodids
2911  $num_prods = 0;
2912  $prodids = array();
2913 
2914  $sql = "SELECT rowid";
2915  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2916  $sql .= " WHERE entity IN (".getEntity('product').")";
2917  $sql .= $this->db->plimit(100);
2918 
2919  $resql = $this->db->query($sql);
2920  if ($resql) {
2921  $num_prods = $this->db->num_rows($resql);
2922  $i = 0;
2923  while ($i < $num_prods) {
2924  $i++;
2925  $row = $this->db->fetch_row($resql);
2926  $prodids[$i] = $row[0];
2927  }
2928  }
2929 
2930  // Initialise parametres
2931  $this->id = 0;
2932  $this->ref = 'SPECIMEN';
2933  $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
2934  $this->specimen = 1;
2935  $this->socid = 1;
2936  $this->date = $now;
2937  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2938  $this->cond_reglement_code = 'RECEP';
2939  $this->mode_reglement_code = 'CHQ';
2940 
2941  $this->note_public = 'This is a comment (public)';
2942  $this->note_private = 'This is a comment (private)';
2943 
2944  $this->multicurrency_tx = 1;
2945  $this->multicurrency_code = $conf->currency;
2946 
2947  $xnbp = 0;
2948  if (empty($option) || $option != 'nolines') {
2949  // Lines
2950  $nbp = 5;
2951  while ($xnbp < $nbp) {
2952  $line = new SupplierInvoiceLine($this->db);
2953  $line->desc = $langs->trans("Description")." ".$xnbp;
2954  $line->qty = 1;
2955  $line->subprice = 100;
2956  $line->pu_ht = 100; // the canelle template use pu_ht and not subprice
2957  $line->price = 100;
2958  $line->tva_tx = 19.6;
2959  $line->localtax1_tx = 0;
2960  $line->localtax2_tx = 0;
2961  if ($xnbp == 2) {
2962  $line->total_ht = 50;
2963  $line->total_ttc = 59.8;
2964  $line->total_tva = 9.8;
2965  $line->remise_percent = 50;
2966  } else {
2967  $line->total_ht = 100;
2968  $line->total_ttc = 119.6;
2969  $line->total_tva = 19.6;
2970  $line->remise_percent = 0;
2971  }
2972 
2973  if ($num_prods > 0) {
2974  $prodid = mt_rand(1, $num_prods);
2975  $line->fk_product = $prodids[$prodid];
2976  }
2977  $line->product_type = 0;
2978 
2979  $this->lines[$xnbp] = $line;
2980 
2981  $this->total_ht += $line->total_ht;
2982  $this->total_tva += $line->total_tva;
2983  $this->total_ttc += $line->total_ttc;
2984 
2985  $xnbp++;
2986  }
2987  }
2988 
2989  $this->amount_ht = $xnbp * 100;
2990  $this->total_ht = $xnbp * 100;
2991  $this->total_tva = $xnbp * 19.6;
2992  $this->total_ttc = $xnbp * 119.6;
2993  }
2994 
2995  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3001  public function load_state_board()
3002  {
3003  // phpcs:enable
3004  global $conf, $user;
3005 
3006  $this->nb = array();
3007 
3008  $clause = "WHERE";
3009 
3010  $sql = "SELECT count(f.rowid) as nb";
3011  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3012  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3013  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3014  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3015  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3016  $clause = "AND";
3017  }
3018  $sql .= " ".$clause." f.entity = ".$conf->entity;
3019 
3020  $resql = $this->db->query($sql);
3021  if ($resql) {
3022  while ($obj = $this->db->fetch_object($resql)) {
3023  $this->nb["supplier_invoices"] = $obj->nb;
3024  }
3025  $this->db->free($resql);
3026  return 1;
3027  } else {
3028  dol_print_error($this->db);
3029  $this->error = $this->db->error();
3030  return -1;
3031  }
3032  }
3033 
3042  public function createFromClone(User $user, $fromid, $invertdetail = 0)
3043  {
3044  global $conf, $langs;
3045 
3046  $error = 0;
3047 
3048  $object = new FactureFournisseur($this->db);
3049 
3050  $this->db->begin();
3051 
3052  // Load source object
3053  $object->fetch($fromid);
3054  $object->id = 0;
3055  $object->statut = self::STATUS_DRAFT; // For backward compatibility
3056  $object->status = self::STATUS_DRAFT;
3057 
3058  $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3059 
3060  // Clear fields
3061  $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3062  $object->author = $user->id;
3063  $object->user_valid = '';
3064  $object->fk_facture_source = 0;
3065  $object->date_creation = '';
3066  $object->date_validation = '';
3067  $object->date = (empty($this->date) ? '' : $this->date);
3068  $object->date_echeance = '';
3069  $object->ref_client = '';
3070  $object->close_code = '';
3071  $object->close_note = '';
3072  if ($conf->global->MAIN_DONT_KEEP_NOTE_ON_CLONING == 1) {
3073  $object->note_private = '';
3074  $object->note_public = '';
3075  }
3076 
3077  // Loop on each line of new invoice
3078  foreach ($object->lines as $i => $line) {
3079  if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3080  unset($object->lines[$i]);
3081  }
3082  }
3083 
3084  // Create clone
3085  $object->context['createfromclone'] = 'createfromclone';
3086  $result = $object->create($user);
3087 
3088  // Other options
3089  if ($result < 0) {
3090  $this->error = $object->error;
3091  $this->errors = $object->errors;
3092  $error++;
3093  }
3094 
3095  if (!$error) {
3096  }
3097 
3098  unset($object->context['createfromclone']);
3099 
3100  // End
3101  if (!$error) {
3102  $this->db->commit();
3103  return $object->id;
3104  } else {
3105  $this->db->rollback();
3106  return -1;
3107  }
3108  }
3109 
3121  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3122  {
3123  global $conf, $user, $langs;
3124 
3125  $langs->load("suppliers");
3126  $outputlangs->load("products");
3127 
3128  // Set the model on the model name to use
3129  if (empty($modele)) {
3130  if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF)) {
3131  $modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
3132  } else {
3133  $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3134  }
3135  }
3136 
3137  if (empty($modele)) {
3138  return 0;
3139  } else {
3140  $modelpath = "core/modules/supplier_invoice/doc/";
3141 
3142  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3143  }
3144  }
3145 
3150  public function getRights()
3151  {
3152  global $user;
3153 
3154  return $user->rights->fournisseur->facture;
3155  }
3156 
3165  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3166  {
3167  $tables = array(
3168  'facture_fourn'
3169  );
3170 
3171  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3172  }
3173 
3182  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3183  {
3184  $tables = array(
3185  'facture_fourn_det'
3186  );
3187 
3188  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3189  }
3190 
3196  public function hasDelay()
3197  {
3198  global $conf;
3199 
3200  $now = dol_now();
3201 
3202  if (!$this->date_echeance) {
3203  return false;
3204  }
3205 
3206  $status = isset($this->status) ? $this->status : $this->statut;
3207 
3208  return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3209  }
3210 
3216  public function isCreditNoteUsed()
3217  {
3218  $isUsed = false;
3219 
3220  $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3221  $resql = $this->db->query($sql);
3222  if (!empty($resql)) {
3223  $obj = $this->db->fetch_object($resql);
3224  if (!empty($obj->fk_invoice_supplier)) {
3225  $isUsed = true;
3226  }
3227  }
3228 
3229  return $isUsed;
3230  }
3231 }
3232 
3233 
3234 
3239 {
3243  public $element = 'facture_fourn_det';
3244 
3248  public $table_element = 'facture_fourn_det';
3249 
3250  public $oldline;
3251 
3256  public $ref;
3257 
3262  public $product_ref;
3263 
3269  public $ref_supplier;
3270 
3275  public $product_desc;
3276 
3283  public $pu_ht;
3284 
3289  public $subprice;
3290 
3295  public $pu_ttc;
3296 
3297 
3302  public $fk_facture_fourn;
3303 
3309  public $label;
3310 
3315  public $description;
3316 
3317  public $date_start;
3318  public $date_end;
3319 
3320  public $skip_update_total; // Skip update price total for special lines
3321 
3325  public $situation_percent;
3326 
3330  public $fk_prev_id;
3331 
3336  public $vat_src_code;
3337 
3342  public $tva_tx;
3343 
3348  public $localtax1_tx;
3349 
3354  public $localtax2_tx;
3355 
3360  public $qty;
3361 
3366  public $remise_percent;
3367 
3372  public $total_ht;
3373 
3378  public $total_ttc;
3379 
3384  public $total_tva;
3385 
3390  public $total_localtax1;
3391 
3396  public $total_localtax2;
3397 
3401  public $fk_product;
3402 
3407  public $product_type;
3408 
3413  public $product_label;
3414 
3421  public $info_bits;
3422 
3427  public $fk_remise_except;
3428 
3432  public $fk_parent_line;
3433 
3434  public $special_code;
3435 
3439  public $rang;
3440 
3445  public $localtax1_type;
3446 
3451  public $localtax2_type;
3452 
3453  // Multicurrency
3457  public $fk_multicurrency;
3458 
3459  public $multicurrency_code;
3460  public $multicurrency_subprice;
3461  public $multicurrency_total_ht;
3462  public $multicurrency_total_tva;
3463  public $multicurrency_total_ttc;
3464 
3465 
3471  public function __construct($db)
3472  {
3473  $this->db = $db;
3474  }
3475 
3482  public function fetch($rowid)
3483  {
3484  $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';
3485  $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
3486  $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';
3487  $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3488  $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3489  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3490  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3491  $sql .= ' WHERE f.rowid = '.((int) $rowid);
3492  $sql .= ' ORDER BY f.rang, f.rowid';
3493 
3494  $query = $this->db->query($sql);
3495 
3496  if (!$query) {
3497  $this->errors[] = $this->db->error();
3498  return -1;
3499  }
3500 
3501  if (!$this->db->num_rows($query)) {
3502  return 0;
3503  }
3504 
3505  $obj = $this->db->fetch_object($query);
3506 
3507  $this->id = $obj->rowid;
3508  $this->rowid = $obj->rowid;
3509  $this->fk_facture_fourn = $obj->fk_facture_fourn;
3510  $this->description = $obj->description;
3511  $this->date_start = $obj->date_start;
3512  $this->date_end = $obj->date_end;
3513  $this->product_ref = $obj->product_ref;
3514  $this->ref_supplier = $obj->ref_supplier;
3515  $this->product_desc = $obj->product_desc;
3516 
3517  $this->subprice = $obj->pu_ht;
3518  $this->pu_ht = $obj->pu_ht;
3519  $this->pu_ttc = $obj->pu_ttc;
3520  $this->tva_tx = $obj->tva_tx;
3521  $this->localtax1_tx = $obj->localtax1_tx;
3522  $this->localtax2_tx = $obj->localtax2_tx;
3523  $this->localtax1_type = $obj->localtax1_type;
3524  $this->localtax2_type = $obj->localtax2_type;
3525 
3526  $this->qty = $obj->qty;
3527  $this->remise_percent = $obj->remise_percent;
3528  $this->fk_remise_except = $obj->fk_remise_except;
3529  //$this->tva = $obj->total_tva; // deprecated
3530  $this->total_ht = $obj->total_ht;
3531  $this->total_tva = $obj->total_tva;
3532  $this->total_localtax1 = $obj->total_localtax1;
3533  $this->total_localtax2 = $obj->total_localtax2;
3534  $this->total_ttc = $obj->total_ttc;
3535  $this->fk_product = $obj->fk_product;
3536  $this->product_type = $obj->product_type;
3537  $this->product_label = $obj->product_label;
3538  $this->info_bits = $obj->info_bits;
3539  $this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3540  $this->fk_parent_line = $obj->fk_parent_line;
3541  $this->special_code = $obj->special_code;
3542  $this->rang = $obj->rang;
3543  $this->fk_unit = $obj->fk_unit;
3544 
3545  $this->multicurrency_subprice = $obj->multicurrency_subprice;
3546  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3547  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3548  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3549 
3550  $this->fetch_optionals();
3551 
3552  return 1;
3553  }
3554 
3561  public function delete($notrigger = 0)
3562  {
3563  global $user, $conf;
3564 
3565  dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
3566 
3567  $error = 0;
3568 
3569  $this->db->begin();
3570 
3571  if (!$notrigger) {
3572  if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3573  $error++;
3574  }
3575  }
3576 
3577  $this->deleteObjectLinked();
3578 
3579  // Remove extrafields
3580  if (!$error) {
3581  $result = $this->deleteExtraFields();
3582  if ($result < 0) {
3583  $error++;
3584  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3585  }
3586  }
3587 
3588  if (!$error) {
3589  // Supprime ligne
3590  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3591  $sql .= " WHERE rowid = ".((int) $this->id);
3592  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3593  $resql = $this->db->query($sql);
3594  if (!$resql) {
3595  $error++;
3596  $this->error = $this->db->lasterror();
3597  }
3598  }
3599 
3600  if (!$error) {
3601  $this->db->commit();
3602  return 1;
3603  } else {
3604  $this->db->rollback();
3605  return -1;
3606  }
3607  }
3608 
3615  public function update($notrigger = 0)
3616  {
3617  global $conf;
3618 
3619  $pu = price2num($this->pu_ht);
3620  $qty = price2num($this->qty);
3621 
3622  // Check parameters
3623  if (empty($this->qty)) {
3624  $this->qty = 0;
3625  }
3626 
3627  if ($this->product_type < 0) {
3628  return -1;
3629  }
3630 
3631  // Clean parameters
3632  if (empty($this->remise_percent)) {
3633  $this->remise_percent = 0;
3634  }
3635  if (empty($this->tva_tx)) {
3636  $this->tva_tx = 0;
3637  }
3638  if (empty($this->localtax1_tx)) {
3639  $this->localtax1_tx = 0;
3640  }
3641  if (empty($this->localtax2_tx)) {
3642  $this->localtax2_tx = 0;
3643  }
3644 
3645  if (empty($this->pa_ht)) {
3646  $this->pa_ht = 0;
3647  }
3648  if (empty($this->multicurrency_subprice)) {
3649  $this->multicurrency_subprice = 0;
3650  }
3651  if (empty($this->multicurrency_total_ht)) {
3652  $this->multicurrency_total_ht = 0;
3653  }
3654  if (empty($this->multicurrency_total_tva)) {
3655  $this->multicurrency_total_tva = 0;
3656  }
3657  if (empty($this->multicurrency_total_ttc)) {
3658  $this->multicurrency_total_ttc = 0;
3659  }
3660 
3661  $fk_product = (int) $this->fk_product;
3662  $fk_unit = (int) $this->fk_unit;
3663 
3664  $this->db->begin();
3665 
3666  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3667  $sql .= " description = '".$this->db->escape($this->description)."'";
3668  $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3669  $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3670  $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3671  $sql .= ", pu_ht = ".price2num($this->pu_ht);
3672  $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3673  $sql .= ", qty = ".price2num($this->qty);
3674  $sql .= ", remise_percent = ".price2num($this->remise_percent);
3675  if ($this->fk_remise_except > 0) $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
3676  else $sql .= ", fk_remise_except=null";
3677  $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3678  $sql .= ", tva_tx = ".price2num($this->tva_tx);
3679  $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3680  $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3681  $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3682  $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3683  $sql .= ", total_ht = ".price2num($this->total_ht);
3684  $sql .= ", tva= ".price2num($this->total_tva);
3685  $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3686  $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3687  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3688  $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
3689  $sql .= ", product_type = ".((int) $this->product_type);
3690  $sql .= ", info_bits = ".((int) $this->info_bits);
3691  $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
3692 
3693  if (!empty($this->rang)) {
3694  $sql .= ", rang=".((int) $this->rang);
3695  }
3696 
3697  // Multicurrency
3698  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3699  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3700  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3701  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3702 
3703  $sql .= " WHERE rowid = ".((int) $this->id);
3704 
3705  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3706  $resql = $this->db->query($sql);
3707 
3708  if (!$resql) {
3709  $this->db->rollback();
3710  $this->error = $this->db->lasterror();
3711  return -1;
3712  }
3713 
3714  $this->rowid = $this->id;
3715  $error = 0;
3716 
3717  if (!$error) {
3718  $result = $this->insertExtraFields();
3719  if ($result < 0) {
3720  $error++;
3721  }
3722  }
3723 
3724  if (!$error && !$notrigger) {
3725  global $langs, $user;
3726 
3727  // Call trigger
3728  if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
3729  $this->db->rollback();
3730  return -1;
3731  }
3732  // End call triggers
3733  }
3734 
3735  if ($error) {
3736  $this->db->rollback();
3737  return -1;
3738  }
3739 
3740  $this->db->commit();
3741  return 1;
3742  }
3743 
3750  public function insert($notrigger = 0)
3751  {
3752  global $user, $conf, $langs;
3753 
3754  $error = 0;
3755 
3756  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3757 
3758  // Clean parameters
3759  $this->desc = trim($this->desc);
3760  if (empty($this->tva_tx)) {
3761  $this->tva_tx = 0;
3762  }
3763  if (empty($this->localtax1_tx)) {
3764  $this->localtax1_tx = 0;
3765  }
3766  if (empty($this->localtax2_tx)) {
3767  $this->localtax2_tx = 0;
3768  }
3769  if (empty($this->localtax1_type)) {
3770  $this->localtax1_type = '0';
3771  }
3772  if (empty($this->localtax2_type)) {
3773  $this->localtax2_type = '0';
3774  }
3775  if (empty($this->total_tva)) {
3776  $this->total_tva = 0;
3777  }
3778  if (empty($this->total_localtax1)) {
3779  $this->total_localtax1 = 0;
3780  }
3781  if (empty($this->total_localtax2)) {
3782  $this->total_localtax2 = 0;
3783  }
3784  if (empty($this->rang)) {
3785  $this->rang = 0;
3786  }
3787  if (empty($this->remise_percent)) {
3788  $this->remise_percent = 0;
3789  }
3790  if (empty($this->info_bits)) {
3791  $this->info_bits = 0;
3792  }
3793  if (empty($this->subprice)) {
3794  $this->subprice = 0;
3795  }
3796  if (empty($this->special_code)) {
3797  $this->special_code = 0;
3798  }
3799  if (empty($this->fk_parent_line)) {
3800  $this->fk_parent_line = 0;
3801  }
3802  if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
3803  $this->situation_percent = 100;
3804  }
3805 
3806  if (empty($this->pa_ht)) {
3807  $this->pa_ht = 0;
3808  }
3809  if (empty($this->multicurrency_subprice)) {
3810  $this->multicurrency_subprice = 0;
3811  }
3812  if (empty($this->multicurrency_total_ht)) {
3813  $this->multicurrency_total_ht = 0;
3814  }
3815  if (empty($this->multicurrency_total_tva)) {
3816  $this->multicurrency_total_tva = 0;
3817  }
3818  if (empty($this->multicurrency_total_ttc)) {
3819  $this->multicurrency_total_ttc = 0;
3820  }
3821 
3822 
3823  // Check parameters
3824  if ($this->product_type < 0) {
3825  $this->error = 'ErrorProductTypeMustBe0orMore';
3826  return -1;
3827  }
3828  if (!empty($this->fk_product) && $this->fk_product > 0) {
3829  // Check product exists
3830  $result = Product::isExistingObject('product', $this->fk_product);
3831  if ($result <= 0) {
3832  $this->error = 'ErrorProductIdDoesNotExists';
3833  return -1;
3834  }
3835  }
3836 
3837  $this->db->begin();
3838 
3839  // Insertion dans base de la ligne
3840  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3841  $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3842  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3843  $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
3844  $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3845  $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3846  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3847  $sql .= ')';
3848  $sql .= " VALUES (".$this->fk_facture_fourn.",";
3849  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3850  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3851  $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3852  $sql .= " '".$this->db->escape($this->ref_supplier)."',";
3853  $sql .= " ".price2num($this->qty).",";
3854 
3855  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3856  $sql .= " ".price2num($this->tva_tx).",";
3857  $sql .= " ".price2num($this->localtax1_tx).",";
3858  $sql .= " ".price2num($this->localtax2_tx).",";
3859  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3860  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3861  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
3862  $sql .= " ".((int) $this->product_type).",";
3863  $sql .= " ".price2num($this->remise_percent).",";
3864  $sql .= ' '.(! empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
3865  $sql .= " ".price2num($this->subprice).",";
3866  $sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
3867  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3868  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3869  $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
3870  $sql .= ' '.((int) $this->rang).',';
3871  $sql .= ' '.((int) $this->special_code).',';
3872  $sql .= " ".((int) $this->info_bits).",";
3873  $sql .= " ".price2num($this->total_ht).",";
3874  $sql .= " ".price2num($this->total_tva).",";
3875  $sql .= " ".price2num($this->total_ttc).",";
3876  $sql .= " ".price2num($this->total_localtax1).",";
3877  $sql .= " ".price2num($this->total_localtax2);
3878  $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3879  $sql .= ", ".(int) $this->fk_multicurrency;
3880  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3881  $sql .= ", ".price2num($this->multicurrency_subprice);
3882  $sql .= ", ".price2num($this->multicurrency_total_ht);
3883  $sql .= ", ".price2num($this->multicurrency_total_tva);
3884  $sql .= ", ".price2num($this->multicurrency_total_ttc);
3885  $sql .= ')';
3886 
3887  $resql = $this->db->query($sql);
3888  if ($resql) {
3889  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3890  $this->rowid = $this->id; // backward compatibility
3891 
3892  if (!$error) {
3893  $result = $this->insertExtraFields();
3894  if ($result < 0) {
3895  $error++;
3896  }
3897  }
3898 
3899  // Si fk_remise_except defini, on lie la remise a la facture
3900  // ce qui la flague comme "consommee".
3901  if ($this->fk_remise_except) {
3902  $discount = new DiscountAbsolute($this->db);
3903  $result = $discount->fetch($this->fk_remise_except);
3904  if ($result >= 0) {
3905  // Check if discount was found
3906  if ($result > 0) {
3907  // Check if discount not already affected to another invoice
3908  if ($discount->fk_facture_line > 0) {
3909  if (empty($noerrorifdiscountalreadylinked)) {
3910  $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
3911  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3912  $this->db->rollback();
3913  return -3;
3914  }
3915  } else {
3916  $result = $discount->link_to_invoice($this->rowid, 0);
3917  if ($result < 0) {
3918  $this->error = $discount->error;
3919  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3920  $this->db->rollback();
3921  return -3;
3922  }
3923  }
3924  } else {
3925  $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
3926  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3927  $this->db->rollback();
3928  return -3;
3929  }
3930  } else {
3931  $this->error = $discount->error;
3932  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
3933  $this->db->rollback();
3934  return -3;
3935  }
3936  }
3937 
3938  if (!$error && !$notrigger) {
3939  // Call trigger
3940  $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
3941  if ($result < 0) {
3942  $this->db->rollback();
3943  return -2;
3944  }
3945  // End call triggers
3946  }
3947 
3948  $this->db->commit();
3949  return $this->id;
3950  } else {
3951  $this->error = $this->db->error();
3952  $this->db->rollback();
3953  return -2;
3954  }
3955  }
3956 
3957  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3963  public function update_total()
3964  {
3965  // phpcs:enable
3966  $this->db->begin();
3967 
3968  // Mise a jour ligne en base
3969  $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3970  $sql .= " total_ht = ".price2num($this->total_ht);
3971  $sql .= ", tva= ".price2num($this->total_tva);
3972  $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
3973  $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
3974  $sql .= ", total_ttc = ".price2num($this->total_ttc);
3975  $sql .= " WHERE rowid = ".((int) $this->rowid);
3976 
3977  dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
3978 
3979  $resql = $this->db->query($sql);
3980  if ($resql) {
3981  $this->db->commit();
3982  return 1;
3983  } else {
3984  $this->error = $this->db->error();
3985  $this->db->rollback();
3986  return -2;
3987  }
3988  }
3989 }
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
setDraft($user, $idwarehouse=-1)
Set draft status.
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
const TYPE_STANDARD
Standard invoice.
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date...
setCanceled($user, $close_code= '', $close_note= '')
Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never rece...
Class to manage stock movements.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto= 'UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
update($user=null, $notrigger=0)
Update database.
insert($notrigger=0)
Insert line into database.
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...
static isExistingObject($element, $id, $ref= '', $ref_ext= '')
Check an object id/ref exists If you don&#39;t need/want to instantiate object and just need to know if o...
info($id)
Loads the info order information into the invoice object.
const TYPE_DEPOSIT
Deposit invoice.
validate($user, $force_number= '', $idwarehouse=0, $notrigger=0)
Tag invoice as validated + call trigger BILL_VALIDATE.
$conf db
API class for accounts.
Definition: inc.php:41
list_replacable_supplier_invoices($socid=0)
Return list of replaceable invoices Status valid or abandoned for other reason + not paid + no paymen...
set_paid($user, $close_code= '', $close_note= '')
Tag invoice as a paid invoice.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
Class to manage line invoices.
createFromClone(User $user, $fromid, $invertdetail=0)
Load an object from its id and create a new one in database.
Class to manage products or services.
dol_now($mode= 'auto')
Return date for now.
line_order($renum=false, $rowidorder= 'ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $rowid= '', $f_user=null, $notrigger=0)
Delete all links between an object $this.
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
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
Class to manage Dolibarr users.
Definition: user.class.php:44
Class to manage Dolibarr database access.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
const TYPE_REPLACEMENT
Replacement invoice.
load_board($user)
Load indicators for dashboard (this-&gt;nbtodo and this-&gt;nbtodolate)
const STATUS_VALIDATED
Validated (need to be paid)
Class to manage suppliers invoices.
load_state_board()
Load indicators for dashboard (this-&gt;nbtodo and this-&gt;nbtodolate)
setUnpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this-&gt;socid or $this-&gt;fk_soc, into this-&gt;thirdparty.
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_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:121
update_total()
Mise a jour de l&#39;objet ligne de commande en base.
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...
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
insertExtraFields($trigger= '', $userused=null)
Add/Update all extra fields values for the current object.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
static getIdAndTxFromCode($dbs, $code, $date_document= '')
Get id and rate of currency from code.
set_unpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
Classe permettant la generation de composants html autre Only common components are here...
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:1382
initAsSpecimen($option= '')
Initialise an instance with random values.
deleteEcmFiles($mode=0)
Delete related files of object in database.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
list_qualified_avoir_supplier_invoices($socid=0)
Return list of qualifying invoices for correction by credit note Invoices that respect the following ...
getRights()
Returns the rights used for this class.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart= '')
Return a path to have a the directory according to object where files are stored. ...
setPaid($user, $close_code= '', $close_note= '')
Tag invoice as a paid invoice.
isCreditNoteUsed()
Is credit note used.
fetch($rowid)
Retrieves a supplier invoice line.
Class to manage invoice templates.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
deleteExtraFields()
Delete all extra fields values for the current object.
Class to manage translations.
insert_discount($idremise)
Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume...
dol_sanitizeFileName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a file name.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
static getIdFromCode($dbs, $code)
Get id of currency from code.
update($notrigger=0)
Update a supplier invoice line.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this-&gt;array_options This method is in most cases call...
deleteline($rowid, $notrigger=0)
Delete a detail line from database.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->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->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
Superclass for invoices classes.
const STATUS_CLOSED
Classified paid.
const TYPE_CREDIT_NOTE
Credit note invoice.
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add objects linked in llx_element_element.
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.
$object ref
Definition: info.php:77
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
Class to manage absolute discounts.
hasDelay()
Is the payment of the supplier invoice having a delay?
getNomUrl($withpicto=0, $option= '', $max=0, $short=0, $moretitle= '', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clicable name (with picto eventually)
const STATUS_ABANDONED
Classified abandoned and no payment done.
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;…&#39; if string larger than length. ...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=&gt;newva...
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
getNextNumRef($soc, $mode= 'next')
Return next reference of supplier invoice not already used (or last reference) according to numbering...
create($user)
Create supplier invoice into database.
fetch($id= '', $ref= '')
Load object in memory from database.
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)...
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:119
fetch_lines()
Load this-&gt;lines.
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:1230
Class to manage predefined suppliers products.
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 &quot;subst...
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid