dolibarr  9.0.0
facture.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
5  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
6  * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
7  * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
10  * Copyright (C) 2010-2016 Juanjo Menent <jmenent@2byte.es>
11  * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12  * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
13  * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
14  * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
15  * Copyright (C) 2013 Cedric Gross <c.gross@kreiz-it.fr>
16  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
17  * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
18  * Copyright (C) 2018 Alexandre Spangaro <aspangaro@zendsi.com>
19  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
20  *
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License as published by
23  * the Free Software Foundation; either version 3 of the License, or
24  * (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program. If not, see <http://www.gnu.org/licenses/>.
33  */
34 
41 include_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
44 require_once DOL_DOCUMENT_ROOT.'/societe/class/client.class.php';
45 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
46 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
47 
48 if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
49 if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
50 
54 class Facture extends CommonInvoice
55 {
59  public $element='facture';
60 
64  public $table_element='facture';
65 
69  public $table_element_line = 'facturedet';
70 
74  public $fk_element = 'fk_facture';
75 
79  public $picto='bill';
80 
85  public $ismultientitymanaged = 1;
86 
91  public $restrictiononfksoc = 1;
92 
96  protected $table_ref_field = 'facnumber';
97 
98  public $socid;
99 
100  public $author;
101 
105  public $fk_user_author;
106 
110  public $fk_user_valid;
111 
112  public $date; // Date invoice
113  public $datem;
114  public $ref_client;
115  public $ref_int;
116  //Check constants for types
117  public $type = self::TYPE_STANDARD;
118 
119  //var $amount;
120  public $remise_absolue;
121  public $remise_percent;
122  public $total_ht=0;
123  public $total_tva=0;
124  public $total_localtax1=0;
125  public $total_localtax2=0;
126  public $total_ttc=0;
127  public $revenuestamp;
128 
131  public $close_code;
133  public $close_note;
135  public $paye;
139  public $pos_source;
144  public $linked_objects=array();
145  public $date_lim_reglement;
146  public $cond_reglement_code; // Code in llx_c_paiement
147  public $mode_reglement_code; // Code in llx_c_paiement
148 
152  public $fk_bank;
153 
157  public $products=array();
158 
162  public $lines=array();
163 
164  public $line;
165  public $extraparams=array();
166  public $specimen;
167 
168  public $fac_rec;
169 
170  // Multicurrency
174  public $fk_multicurrency;
175 
176  public $multicurrency_code;
177  public $multicurrency_tx;
178  public $multicurrency_total_ht;
179  public $multicurrency_total_tva;
180  public $multicurrency_total_ttc;
181 
185  public $situation_cycle_ref;
186 
190  public $situation_counter;
191 
195  public $situation_final;
196 
200  public $tab_previous_situation_invoice=array();
201 
205  public $tab_next_situation_invoice=array();
206 
207  public $oldcopy;
208 
212  const TYPE_STANDARD = 0;
213 
217  const TYPE_REPLACEMENT = 1;
218 
222  const TYPE_CREDIT_NOTE = 2;
223 
227  const TYPE_DEPOSIT = 3;
228 
232  const TYPE_PROFORMA = 4;
233 
237  const TYPE_SITUATION = 5;
238 
242  const STATUS_DRAFT = 0;
243 
247  const STATUS_VALIDATED = 1;
248 
256  const STATUS_CLOSED = 2;
257 
265  const STATUS_ABANDONED = 3;
266 
267  const CLOSECODE_DISCOUNTVAT = 'discount_vat'; // Abandonned remain - escompte
268  const CLOSECODE_BADDEBT = 'badcustomer'; // Abandonned - bad
269  const CLOSECODE_ABANDONED = 'abandon'; // Abandonned - other
270  const CLOSECODE_REPLACED = 'replaced'; // Closed after doing a replacement invoice
271 
277  function __construct($db)
278  {
279  $this->db = $db;
280  }
281 
292  function create(User $user, $notrigger=0, $forceduedate=0)
293  {
294  global $langs,$conf,$mysoc,$hookmanager;
295  $error=0;
296 
297  // Clean parameters
298  if (empty($this->type)) $this->type = self::TYPE_STANDARD;
299  $this->ref_client=trim($this->ref_client);
300  $this->note=(isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
301  $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note_private));
302  $this->note_public=trim($this->note_public);
303  if (! $this->cond_reglement_id) $this->cond_reglement_id = 0;
304  if (! $this->mode_reglement_id) $this->mode_reglement_id = 0;
305  $this->brouillon = 1;
306  if (empty($this->entity)) $this->entity = $conf->entity;
307 
308  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
309  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
310  else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
311  if (empty($this->fk_multicurrency))
312  {
313  $this->multicurrency_code = $conf->currency;
314  $this->fk_multicurrency = 0;
315  $this->multicurrency_tx = 1;
316  }
317 
318  dol_syslog(get_class($this)."::create user=".$user->id." date=".$this->date);
319 
320  // Check parameters
321  if (empty($this->date))
322  {
323  $this->error="Try to create an invoice with an empty parameter (date)";
324  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
325  return -3;
326  }
327  $soc = new Societe($this->db);
328  $result=$soc->fetch($this->socid);
329  if ($result < 0)
330  {
331  $this->error="Failed to fetch company: ".$soc->error;
332  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
333  return -2;
334  }
335 
336  $now=dol_now();
337 
338  $this->db->begin();
339 
340  $originaldatewhen=null;
341  $nextdatewhen=null;
342  $previousdaynextdatewhen=null;
343 
344  // Create invoice from a template invoice
345  if ($this->fac_rec > 0)
346  {
347  $this->fk_fac_rec_source = $this->fac_rec;
348 
349  require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
350  $_facrec = new FactureRec($this->db);
351  $result=$_facrec->fetch($this->fac_rec);
352  $result=$_facrec->fetchObjectLinked(); // This load $_facrec->linkedObjectsIds
353 
354  // Define some dates
355  $originaldatewhen = $_facrec->date_when;
356  $nextdatewhen=dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
357  $previousdaynextdatewhen=dol_time_plus_duree($nextdatewhen, -1, 'd');
358 
359  $this->socid = $_facrec->socid; // Invoice created on same thirdparty than template
360  $this->entity = $_facrec->entity; // Invoice created in same entity than template
361 
362  // 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
363  $this->fk_project = GETPOST('projectid','int') > 0 ? ((int) GETPOST('projectid','int')) : $_facrec->fk_project;
364  $this->note_public = GETPOST('note_public','none') ? GETPOST('note_public','none') : $_facrec->note_public;
365  $this->note_private = GETPOST('note_private','none') ? GETPOST('note_private','none') : $_facrec->note_private;
366  $this->modelpdf = GETPOST('model','alpha') ? GETPOST('model','apha') : $_facrec->modelpdf;
367  $this->cond_reglement_id = GETPOST('cond_reglement_id','int') > 0 ? ((int) GETPOST('cond_reglement_id','int')) : $_facrec->cond_reglement_id;
368  $this->mode_reglement_id = GETPOST('mode_reglement_id','int') > 0 ? ((int) GETPOST('mode_reglement_id','int')) : $_facrec->mode_reglement_id;
369  $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
370 
371  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
372  $this->total_ht = $_facrec->total_ht;
373  $this->total_ttc = $_facrec->total_ttc;
374 
375  // Fields always coming from template
376  $this->remise_absolue = $_facrec->remise_absolue;
377  $this->remise_percent = $_facrec->remise_percent;
378  $this->fk_incoterms = $_facrec->fk_incoterms;
379  $this->location_incoterms= $_facrec->location_incoterms;
380 
381  // Clean parameters
382  if (! $this->type) $this->type = self::TYPE_STANDARD;
383  $this->ref_client=trim($this->ref_client);
384  $this->note_public=trim($this->note_public);
385  $this->note_private=trim($this->note_private);
386  $this->note_private=dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->ref));
387 
388  $this->array_options=$_facrec->array_options;
389 
390  //if (! $this->remise) $this->remise = 0;
391  if (! $this->mode_reglement_id) $this->mode_reglement_id = 0;
392  $this->brouillon = 1;
393 
394  $this->linked_objects = $_facrec->linkedObjectsIds;
395 
396  $forceduedate = $this->calculate_date_lim_reglement();
397 
398  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
399  if ($_facrec->frequency > 0)
400  {
401  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
402  if (empty($_facrec->date_when)) $_facrec->date_when = $now;
403  $next_date = $_facrec->getNextDate(); // Calculate next date
404  $result = $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date', '', $user, '');
405  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
406  $result = $_facrec->setNextDate($next_date,1);
407  }
408 
409  // Define lang of customer
410  $outputlangs = $langs;
411  $newlang='';
412 
413  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->thirdparty->default_lang)) $newlang=$this->thirdparty->default_lang; // for proposal, order, invoice, ...
414  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->default_lang)) $newlang=$this->default_lang; // for thirdparty
415  if (! empty($newlang))
416  {
417  $outputlangs = new Translate("",$conf);
418  $outputlangs->setDefaultLang($newlang);
419  }
420 
421  // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
422  $substitutionarray=getCommonSubstitutionArray($outputlangs, 0, null, $this);
423  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
424  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
425  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
426  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
427  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
428  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
429  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
430  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
431  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y');
432  // Only for tempalte invoice
433  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour');
434  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($nextdatewhen, 'dayhour');
435  $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($previousdaynextdatewhen, 'dayhour');
436 
437  //var_dump($substitutionarray);exit;
438 
439  $substitutionisok=true;
440  complete_substitutions_array($substitutionarray, $outputlangs);
441 
442  $this->note_public=make_substitutions($this->note_public,$substitutionarray);
443  $this->note_private=make_substitutions($this->note_private,$substitutionarray);
444  }
445 
446  // Define due date if not already defined
447  $datelim=(empty($forceduedate)?$this->calculate_date_lim_reglement():$forceduedate);
448 
449  // Insert into database
450  $socid = $this->socid;
451 
452  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture (";
453  $sql.= " facnumber";
454  $sql.= ", entity";
455  $sql.= ", ref_ext";
456  $sql.= ", type";
457  $sql.= ", fk_soc";
458  $sql.= ", datec";
459  $sql.= ", remise_absolue";
460  $sql.= ", remise_percent";
461  $sql.= ", datef";
462  $sql.= ", date_pointoftax";
463  $sql.= ", note_private";
464  $sql.= ", note_public";
465  $sql.= ", ref_client, ref_int";
466  $sql.= ", fk_account";
467  $sql.= ", module_source, pos_source, fk_fac_rec_source, fk_facture_source, fk_user_author, fk_projet";
468  $sql.= ", fk_cond_reglement, fk_mode_reglement, date_lim_reglement, model_pdf";
469  $sql.= ", situation_cycle_ref, situation_counter, situation_final";
470  $sql.= ", fk_incoterms, location_incoterms";
471  $sql.= ", fk_multicurrency";
472  $sql.= ", multicurrency_code";
473  $sql.= ", multicurrency_tx";
474  $sql.= ")";
475  $sql.= " VALUES (";
476  $sql.= "'(PROV)'";
477  $sql.= ", ".$this->entity;
478  $sql.= ", ".($this->ref_ext?"'".$this->db->escape($this->ref_ext)."'":"null");
479  $sql.= ", '".$this->db->escape($this->type)."'";
480  $sql.= ", '".$socid."'";
481  $sql.= ", '".$this->db->idate($now)."'";
482  $sql.= ", ".($this->remise_absolue>0?$this->remise_absolue:'NULL');
483  $sql.= ", ".($this->remise_percent>0?$this->remise_percent:'NULL');
484  $sql.= ", '".$this->db->idate($this->date)."'";
485  $sql.= ", ".(strval($this->date_pointoftax)!='' ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null');
486  $sql.= ", ".($this->note_private?"'".$this->db->escape($this->note_private)."'":"null");
487  $sql.= ", ".($this->note_public?"'".$this->db->escape($this->note_public)."'":"null");
488  $sql.= ", ".($this->ref_client?"'".$this->db->escape($this->ref_client)."'":"null");
489  $sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
490  $sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
491  $sql.= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
492  $sql.= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
493  $sql.= ", ".($this->fk_fac_rec_source?"'".$this->db->escape($this->fk_fac_rec_source)."'":"null");
494  $sql.= ", ".($this->fk_facture_source?"'".$this->db->escape($this->fk_facture_source)."'":"null");
495  $sql.= ", ".($user->id > 0 ? "'".$user->id."'":"null");
496  $sql.= ", ".($this->fk_project?$this->fk_project:"null");
497  $sql.= ", ".$this->cond_reglement_id;
498  $sql.= ", ".$this->mode_reglement_id;
499  $sql.= ", '".$this->db->idate($datelim)."', '".$this->db->escape($this->modelpdf)."'";
500  $sql.= ", ".($this->situation_cycle_ref?"'".$this->db->escape($this->situation_cycle_ref)."'":"null");
501  $sql.= ", ".($this->situation_counter?"'".$this->db->escape($this->situation_counter)."'":"null");
502  $sql.= ", ".($this->situation_final?$this->situation_final:0);
503  $sql.= ", ".(int) $this->fk_incoterms;
504  $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
505  $sql.= ", ".(int) $this->fk_multicurrency;
506  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
507  $sql.= ", ".(double) $this->multicurrency_tx;
508  $sql.=")";
509 
510  $resql=$this->db->query($sql);
511  if ($resql)
512  {
513  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture');
514 
515  // Update ref with new one
516  $this->ref='(PROV'.$this->id.')';
517  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture SET facnumber='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
518 
519  $resql=$this->db->query($sql);
520  if (! $resql) $error++;
521 
522  if (! empty($this->linkedObjectsIds) && empty($this->linked_objects)) // To use new linkedObjectsIds instead of old linked_objects
523  {
524  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
525  }
526 
527  // Add object linked
528  if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
529  {
530  foreach($this->linked_objects as $origin => $tmp_origin_id)
531  {
532  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, ...))
533  {
534  foreach($tmp_origin_id as $origin_id)
535  {
536  $ret = $this->add_object_linked($origin, $origin_id);
537  if (! $ret)
538  {
539  $this->error=$this->db->lasterror();
540  $error++;
541  }
542  }
543  }
544  else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
545  {
546  $origin_id = $tmp_origin_id;
547  $ret = $this->add_object_linked($origin, $origin_id);
548  if (! $ret)
549  {
550  $this->error=$this->db->lasterror();
551  $error++;
552  }
553  }
554  }
555  }
556 
557  // Propagate contacts
558  if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id)) // Get contact from origin object
559  {
560  $originforcontact = $this->origin;
561  $originidforcontact = $this->origin_id;
562  if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
563  {
564  require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
565  $exp = new Expedition($this->db);
566  $exp->fetch($this->origin_id);
567  $exp->fetchObjectLinked();
568  if (count($exp->linkedObjectsIds['commande']) > 0)
569  {
570  foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
571  {
572  $originforcontact = 'commande';
573  if (is_object($value)) $originidforcontact = $value->id;
574  else $originidforcontact = $value;
575  break; // We take first one
576  }
577  }
578  }
579 
580  $sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
581  $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
582 
583  $resqlcontact = $this->db->query($sqlcontact);
584  if ($resqlcontact)
585  {
586  while($objcontact = $this->db->fetch_object($resqlcontact))
587  {
588  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
589  $this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source); // May failed because of duplicate key or because code of contact type does not exists for new object
590  }
591  }
592  else dol_print_error($resqlcontact);
593  }
594 
595  /*
596  * Insert lines of invoices, if not from template invoice, into database
597  */
598  if (! $error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) // If this->lines is array of InvoiceLines (preferred mode)
599  {
600  $fk_parent_line = 0;
601 
602  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
603  foreach ($this->lines as $i => $val)
604  {
605  $newinvoiceline=$this->lines[$i];
606  $newinvoiceline->fk_facture=$this->id;
607 
608  $newinvoiceline->origin = $this->lines[$i]->element;
609  $newinvoiceline->origin_id = $this->lines[$i]->id;
610 
611  // Auto set date of service ?
612  if ($this->lines[$i]->date_start_fill == 1 && $originaldatewhen) // $originaldatewhen is defined when generating from recurring invoice only
613  {
614  $newinvoiceline->date_start = $originaldatewhen;
615  }
616  if ($this->lines[$i]->date_end_fill == 1 && $previousdaynextdatewhen) // $previousdaynextdatewhen is defined when generating from recurring invoice only
617  {
618  $newinvoiceline->date_end = $previousdaynextdatewhen;
619  }
620 
621  if ($result >= 0)
622  {
623  // Reset fk_parent_line for no child products and special product
624  if (($newinvoiceline->product_type != 9 && empty($newinvoiceline->fk_parent_line)) || $newinvoiceline->product_type == 9) {
625  $fk_parent_line = 0;
626  }
627 
628  $newinvoiceline->fk_parent_line=$fk_parent_line;
629 
630  if($this->type === Facture::TYPE_REPLACEMENT && $newinvoiceline->fk_remise_except){
631  $discount = new DiscountAbsolute($this->db);
632  $discount->fetch($newinvoiceline->fk_remise_except);
633 
634  $discountId = $soc->set_remise_except($discount->amount_ht, $user, $discount->description, $discount->tva_tx);
635  $newinvoiceline->fk_remise_except = $discountId;
636  }
637 
638  $result=$newinvoiceline->insert();
639 
640  // Defined the new fk_parent_line
641  if ($result > 0 && $newinvoiceline->product_type == 9) {
642  $fk_parent_line = $result;
643  }
644  }
645  if ($result < 0)
646  {
647  $this->error=$newinvoiceline->error;
648  $error++;
649  break;
650  }
651  }
652  }
653  elseif (! $error && empty($this->fac_rec)) // If this->lines is an array of invoice line arrays
654  {
655  $fk_parent_line = 0;
656 
657  dol_syslog("There is ".count($this->lines)." lines that are array lines");
658 
659  foreach ($this->lines as $i => $val)
660  {
661  $line = $this->lines[$i];
662 
663  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
664  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
665  if (! is_object($line)) $line = (object) $line;
666 
667  if ($result >= 0)
668  {
669  // Reset fk_parent_line for no child products and special product
670  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
671  $fk_parent_line = 0;
672  }
673 
674  // Complete vat rate with code
675  $vatrate = $line->tva_tx;
676  if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
677 
678  $result = $this->addline(
679  $line->desc,
680  $line->subprice,
681  $line->qty,
682  $vatrate,
683  $line->localtax1_tx,
684  $line->localtax2_tx,
685  $line->fk_product,
686  $line->remise_percent,
687  $line->date_start,
688  $line->date_end,
689  $line->fk_code_ventilation,
690  $line->info_bits,
691  $line->fk_remise_except,
692  'HT',
693  0,
694  $line->product_type,
695  $line->rang,
696  $line->special_code,
697  $this->element,
698  $line->id,
699  $fk_parent_line,
700  $line->fk_fournprice,
701  $line->pa_ht,
702  $line->label,
703  $line->array_options,
704  $line->situation_percent,
705  $line->fk_prev_id,
706  $line->fk_unit,
707  $line->pu_ht_devise
708  );
709  if ($result < 0)
710  {
711  $this->error=$this->db->lasterror();
712  dol_print_error($this->db);
713  $this->db->rollback();
714  return -1;
715  }
716 
717  // Defined the new fk_parent_line
718  if ($result > 0 && $line->product_type == 9) {
719  $fk_parent_line = $result;
720  }
721  }
722  }
723  }
724 
725  /*
726  * Insert lines of predefined invoices
727  */
728  if (! $error && $this->fac_rec > 0)
729  {
730  foreach ($_facrec->lines as $i => $val)
731  {
732  if ($_facrec->lines[$i]->fk_product)
733  {
734  $prod = new Product($this->db);
735  $res=$prod->fetch($_facrec->lines[$i]->fk_product);
736  }
737 
738  // For line from template invoice, we use data from template invoice
739  /*
740  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
741  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
742  if (empty($tva_tx)) $tva_npr=0;
743  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
744  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
745  */
746  $tva_tx = $_facrec->lines[$i]->tva_tx.($_facrec->lines[$i]->vat_src_code ? '('.$_facrec->lines[$i]->vat_src_code.')' : '');
747  $tva_npr = $_facrec->lines[$i]->info_bits;
748  if (empty($tva_tx)) $tva_npr=0;
749  $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
750  $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
751 
752  $result_insert = $this->addline(
753  $_facrec->lines[$i]->desc,
754  $_facrec->lines[$i]->subprice,
755  $_facrec->lines[$i]->qty,
756  $tva_tx,
757  $localtax1_tx,
758  $localtax2_tx,
759  $_facrec->lines[$i]->fk_product,
760  $_facrec->lines[$i]->remise_percent,
761  ($_facrec->lines[$i]->date_start_fill == 1 && $originaldatewhen)?$originaldatewhen:'',
762  ($_facrec->lines[$i]->date_end_fill == 1 && $previousdaynextdatewhen)?$previousdaynextdatewhen:'',
763  0,
764  $tva_npr,
765  '',
766  'HT',
767  0,
768  $_facrec->lines[$i]->product_type,
769  $_facrec->lines[$i]->rang,
770  $_facrec->lines[$i]->special_code,
771  '',
772  0,
773  0,
774  null,
775  0,
776  $_facrec->lines[$i]->label,
777  empty($_facrec->lines[$i]->array_options)?null:$_facrec->lines[$i]->array_options,
778  $_facrec->lines[$i]->situation_percent,
779  '',
780  $_facrec->lines[$i]->fk_unit,
781  $_facrec->lines[$i]->pu_ht_devise
782  );
783 
784  if ( $result_insert < 0)
785  {
786  $error++;
787  $this->error=$this->db->error();
788  break;
789  }
790  }
791  }
792 
793  if (! $error)
794  {
795 
796  $result=$this->update_price(1);
797  if ($result > 0)
798  {
799  $action='create';
800 
801  // Actions on extra fields
802  if (! $error)
803  {
804  $result=$this->insertExtraFields();
805  if ($result < 0) $error++;
806  }
807 
808  if (! $error && ! $notrigger)
809  {
810  // Call trigger
811  $result=$this->call_trigger('BILL_CREATE',$user);
812  if ($result < 0) $error++;
813  // End call triggers
814  }
815 
816  if (! $error)
817  {
818  $this->db->commit();
819  return $this->id;
820  }
821  else
822  {
823  $this->db->rollback();
824  return -4;
825  }
826  }
827  else
828  {
829  $this->error=$langs->trans('FailedToUpdatePrice');
830  $this->db->rollback();
831  return -3;
832  }
833  }
834  else
835  {
836  dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
837  $this->db->rollback();
838  return -2;
839  }
840  }
841  else
842  {
843  $this->error=$this->db->error();
844  $this->db->rollback();
845  return -1;
846  }
847  }
848 
849 
857  function createFromCurrent(User $user, $invertdetail=0)
858  {
859  global $conf;
860 
861  // Charge facture source
862  $facture=new Facture($this->db);
863 
864  // Retreive all extrafield
865  // fetch optionals attributes and labels
866  $this->fetch_optionals();
867 
868  if(!empty($this->array_options)){
869  $facture->array_options = $this->array_options;
870  }
871 
872  foreach($this->lines as &$line){
873  $line->fetch_optionals();//fetch extrafields
874  }
875 
876  $facture->fk_facture_source = $this->fk_facture_source;
877  $facture->type = $this->type;
878  $facture->socid = $this->socid;
879  $facture->date = $this->date;
880  $facture->date_pointoftax = $this->date_pointoftax;
881  $facture->note_public = $this->note_public;
882  $facture->note_private = $this->note_private;
883  $facture->ref_client = $this->ref_client;
884  $facture->modelpdf = $this->modelpdf;
885  $facture->fk_project = $this->fk_project;
886  $facture->cond_reglement_id = $this->cond_reglement_id;
887  $facture->mode_reglement_id = $this->mode_reglement_id;
888  $facture->remise_absolue = $this->remise_absolue;
889  $facture->remise_percent = $this->remise_percent;
890 
891  $facture->origin = $this->origin;
892  $facture->origin_id = $this->origin_id;
893 
894  $facture->lines = $this->lines; // Tableau des lignes de factures
895  $facture->products = $this->lines; // Tant que products encore utilise
896  $facture->situation_counter = $this->situation_counter;
897  $facture->situation_cycle_ref=$this->situation_cycle_ref;
898  $facture->situation_final = $this->situation_final;
899 
900  // Loop on each line of new invoice
901  foreach($facture->lines as $i => $tmpline)
902  {
903  $facture->lines[$i]->fk_prev_id = $this->lines[$i]->rowid;
904  if ($invertdetail)
905  {
906  $facture->lines[$i]->subprice = -$facture->lines[$i]->subprice;
907  $facture->lines[$i]->total_ht = -$facture->lines[$i]->total_ht;
908  $facture->lines[$i]->total_tva = -$facture->lines[$i]->total_tva;
909  $facture->lines[$i]->total_localtax1 = -$facture->lines[$i]->total_localtax1;
910  $facture->lines[$i]->total_localtax2 = -$facture->lines[$i]->total_localtax2;
911  $facture->lines[$i]->total_ttc = -$facture->lines[$i]->total_ttc;
912  }
913  }
914 
915  dol_syslog(get_class($this)."::createFromCurrent invertdetail=".$invertdetail." socid=".$this->socid." nboflines=".count($facture->lines));
916 
917  $facid = $facture->create($user);
918  if ($facid <= 0)
919  {
920  $this->error=$facture->error;
921  $this->errors=$facture->errors;
922  }
923  elseif ($this->type == self::TYPE_SITUATION && !empty($conf->global->INVOICE_USE_SITUATION))
924  {
925  $this->fetchObjectLinked('', '', $facture->id, 'facture');
926 
927  foreach ($this->linkedObjectsIds as $typeObject => $Tfk_object)
928  {
929  foreach ($Tfk_object as $fk_object)
930  {
931  $facture->add_object_linked($typeObject, $fk_object);
932  }
933  }
934 
935  $facture->add_object_linked('facture', $this->fk_facture_source);
936  }
937 
938  return $facid;
939  }
940 
941 
948  function createFromClone($socid=0)
949  {
950  global $user,$hookmanager;
951 
952  $error=0;
953 
954  $this->db->begin();
955 
956  // get extrafields so they will be clone
957  foreach($this->lines as $line)
958  $line->fetch_optionals($line->rowid);
959 
960  // Load source object
961  $objFrom = clone $this;
962 
963  // Change socid if needed
964  if (! empty($socid) && $socid != $this->socid)
965  {
966  $objsoc = new Societe($this->db);
967 
968  if ($objsoc->fetch($socid)>0)
969  {
970  $this->socid = $objsoc->id;
971  $this->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
972  $this->mode_reglement_id = (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
973  $this->fk_project = '';
974  $this->fk_delivery_address = '';
975  }
976 
977  // TODO Change product price if multi-prices
978  }
979 
980  $this->id=0;
981  $this->statut= self::STATUS_DRAFT;
982 
983  // Clear fields
984  $this->date = dol_now(); // Date of invoice is set to current date when cloning. // TODO Best is to ask date into confirm box
985  $this->user_author = $user->id;
986  $this->user_valid = '';
987  $this->fk_facture_source = 0;
988  $this->date_creation = '';
989  $this->date_modification = '';
990  $this->date_validation = '';
991  $this->ref_client = '';
992  $this->close_code = '';
993  $this->close_note = '';
994  $this->products = $this->lines; // Tant que products encore utilise
995 
996  // Loop on each line of new invoice
997  foreach($this->lines as $i => $line)
998  {
999  if (($this->lines[$i]->info_bits & 0x02) == 0x02) // We do not clone line of discounts
1000  {
1001  unset($this->lines[$i]);
1002  unset($this->products[$i]); // Tant que products encore utilise
1003  }
1004  }
1005 
1006  // Create clone
1007  $this->context['createfromclone'] = 'createfromclone';
1008  $result=$this->create($user);
1009  if ($result < 0) $error++;
1010  else {
1011  // copy internal contacts
1012  if ($this->copy_linked_contact($objFrom, 'internal') < 0)
1013  $error++;
1014 
1015  // copy external contacts if same company
1016  elseif ($objFrom->socid == $this->socid)
1017  {
1018  if ($this->copy_linked_contact($objFrom, 'external') < 0)
1019  $error++;
1020  }
1021  }
1022 
1023  if (! $error)
1024  {
1025  // Hook of thirdparty module
1026  if (is_object($hookmanager))
1027  {
1028  $parameters=array('objFrom'=>$objFrom);
1029  $action='';
1030  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1031  if ($reshook < 0) $error++;
1032  }
1033  }
1034 
1035  unset($this->context['createfromclone']);
1036 
1037  // End
1038  if (! $error)
1039  {
1040  $this->db->commit();
1041  return $this->id;
1042  }
1043  else
1044  {
1045  $this->db->rollback();
1046  return -1;
1047  }
1048  }
1049 
1057  function createFromOrder($object, User $user)
1058  {
1059  global $hookmanager;
1060 
1061  $error=0;
1062 
1063  // Closed order
1064  $this->date = dol_now();
1065  $this->source = 0;
1066 
1067  $num=count($object->lines);
1068  for ($i = 0; $i < $num; $i++)
1069  {
1070  $line = new FactureLigne($this->db);
1071 
1072  $line->libelle = $object->lines[$i]->libelle;
1073  $line->label = $object->lines[$i]->label;
1074  $line->desc = $object->lines[$i]->desc;
1075  $line->subprice = $object->lines[$i]->subprice;
1076  $line->total_ht = $object->lines[$i]->total_ht;
1077  $line->total_tva = $object->lines[$i]->total_tva;
1078  $line->total_localtax1 = $object->lines[$i]->total_localtax1;
1079  $line->total_localtax2 = $object->lines[$i]->total_localtax2;
1080  $line->total_ttc = $object->lines[$i]->total_ttc;
1081  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1082  $line->tva_tx = $object->lines[$i]->tva_tx;
1083  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1084  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1085  $line->qty = $object->lines[$i]->qty;
1086  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1087  $line->remise_percent = $object->lines[$i]->remise_percent;
1088  $line->fk_product = $object->lines[$i]->fk_product;
1089  $line->info_bits = $object->lines[$i]->info_bits;
1090  $line->product_type = $object->lines[$i]->product_type;
1091  $line->rang = $object->lines[$i]->rang;
1092  $line->special_code = $object->lines[$i]->special_code;
1093  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1094  $line->fk_unit = $object->lines[$i]->fk_unit;
1095  $line->date_start = $object->lines[$i]->date_start;
1096  $line->date_end = $object->lines[$i]->date_end;
1097 
1098  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1099  $marginInfos = getMarginInfos($object->lines[$i]->subprice, $object->lines[$i]->remise_percent, $object->lines[$i]->tva_tx, $object->lines[$i]->localtax1_tx, $object->lines[$i]->localtax2_tx, $object->lines[$i]->fk_fournprice, $object->lines[$i]->pa_ht);
1100  $line->pa_ht = $marginInfos[0];
1101 
1102  // get extrafields from original line
1103  $object->lines[$i]->fetch_optionals();
1104  foreach($object->lines[$i]->array_options as $options_key => $value)
1105  $line->array_options[$options_key] = $value;
1106 
1107  $this->lines[$i] = $line;
1108  }
1109 
1110  $this->socid = $object->socid;
1111  $this->fk_project = $object->fk_project;
1112  $this->cond_reglement_id = $object->cond_reglement_id;
1113  $this->mode_reglement_id = $object->mode_reglement_id;
1114  $this->availability_id = $object->availability_id;
1115  $this->demand_reason_id = $object->demand_reason_id;
1116  $this->date_livraison = $object->date_livraison;
1117  $this->fk_delivery_address = $object->fk_delivery_address;
1118  $this->contact_id = $object->contactid;
1119  $this->ref_client = $object->ref_client;
1120  $this->note_private = $object->note_private;
1121  $this->note_public = $object->note_public;
1122 
1123  $this->origin = $object->element;
1124  $this->origin_id = $object->id;
1125 
1126  // get extrafields from original line
1127  $object->fetch_optionals($object->id);
1128  foreach($object->array_options as $options_key => $value)
1129  $this->array_options[$options_key] = $value;
1130 
1131  // Possibility to add external linked objects with hooks
1132  $this->linked_objects[$this->origin] = $this->origin_id;
1133  if (! empty($object->other_linked_objects) && is_array($object->other_linked_objects))
1134  {
1135  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1136  }
1137 
1138  $ret = $this->create($user);
1139 
1140  if ($ret > 0)
1141  {
1142  // Actions hooked (by external module)
1143  $hookmanager->initHooks(array('invoicedao'));
1144 
1145  $parameters=array('objFrom'=>$object);
1146  $action='';
1147  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1148  if ($reshook < 0) $error++;
1149 
1150  if (! $error)
1151  {
1152  return 1;
1153  }
1154  else return -1;
1155  }
1156  else return -1;
1157  }
1158 
1165  function getDirectExternalLink($withpicto=0)
1166  {
1167  global $dolibarr_main_url_root;
1168 
1169  // Define $urlwithroot
1170  $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
1171  $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1172  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1173 
1174  // TODO Read into ecmfile table to get entry and hash exists (PS: If not found, add it)
1175  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1176  $ecmfile=new EcmFiles($this->db);
1177  //$result = $ecmfile->get();
1178 
1179  $hashp='todo';
1180  return '<a href="'.$urlwithroot.'/document.php?modulepart=invoice&hashp='.$hashp.'" target="_download" rel="noindex, nofollow">'.$this->ref.'</a>';
1181  }
1182 
1196  function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $addlinktonotes=0, $save_lastsearch_value=-1)
1197  {
1198  global $langs, $conf, $user, $form;
1199 
1200  if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
1201 
1202  $result='';
1203 
1204  if ($option == 'withdraw') $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id;
1205  else $url = DOL_URL_ROOT.'/compta/facture/card.php?facid='.$this->id;
1206 
1207  if (!$user->rights->facture->lire)
1208  $option = 'nolink';
1209 
1210  if ($option !== 'nolink')
1211  {
1212  // Add param to save lastsearch_values or not
1213  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1214  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1215  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1216  }
1217 
1218  if ($short) return $url;
1219 
1220  $picto='bill';
1221  if ($this->type == self::TYPE_REPLACEMENT) $picto.='r'; // Replacement invoice
1222  if ($this->type == self::TYPE_CREDIT_NOTE) $picto.='a'; // Credit note
1223  if ($this->type == self::TYPE_DEPOSIT) $picto.='d'; // Deposit invoice
1224  $label='';
1225 
1226  if ($user->rights->facture->lire) {
1227  $label = '<u>' . $langs->trans("ShowInvoice") . '</u>';
1228  if ($this->type == self::TYPE_REPLACEMENT) $label='<u>' . $langs->transnoentitiesnoconv("ShowInvoiceReplace") . '</u>';
1229  if ($this->type == self::TYPE_CREDIT_NOTE) $label='<u>' . $langs->transnoentitiesnoconv("ShowInvoiceAvoir") . '</u>';
1230  if ($this->type == self::TYPE_DEPOSIT) $label='<u>' . $langs->transnoentitiesnoconv("ShowInvoiceDeposit") . '</u>';
1231  if ($this->type == self::TYPE_SITUATION) $label='<u>' . $langs->transnoentitiesnoconv("ShowInvoiceSituation") . '</u>';
1232  if (! empty($this->ref))
1233  $label .= '<br><b>'.$langs->trans('Ref') . ':</b> ' . $this->ref;
1234  if (! empty($this->ref_client))
1235  $label .= '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_client;
1236  if (! empty($this->total_ht))
1237  $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1238  if (! empty($this->total_tva))
1239  $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1240  if (! empty($this->total_localtax1) && $this->total_localtax1 != 0) // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
1241  $label.= '<br><b>' . $langs->trans('LT1') . ':</b> ' . price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
1242  if (! empty($this->total_localtax2) && $this->total_localtax2 != 0)
1243  $label.= '<br><b>' . $langs->trans('LT2') . ':</b> ' . price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
1244  if (! empty($this->total_ttc))
1245  $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1246  if ($moretitle) $label.=' - '.$moretitle;
1247  }
1248 
1249  $linkclose='';
1250  if (empty($notooltip) && $user->rights->facture->lire)
1251  {
1252  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1253  {
1254  $label=$langs->trans("ShowInvoice");
1255  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1256  }
1257  $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1258  $linkclose.=' class="classfortooltip"';
1259  }
1260 
1261  $linkstart='<a href="'.$url.'"';
1262  $linkstart.=$linkclose.'>';
1263  $linkend='</a>';
1264 
1265  if ($option == 'nolink') {
1266  $linkstart = '';
1267  $linkend = '';
1268  }
1269 
1270  $result .= $linkstart;
1271  if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1272  if ($withpicto != 2) $result.= ($max?dol_trunc($this->ref,$max):$this->ref);
1273  $result .= $linkend;
1274 
1275  if ($addlinktonotes)
1276  {
1277  $txttoshow=($user->socid > 0 ? $this->note_public : $this->note_private);
1278  if ($txttoshow)
1279  {
1280  $notetoshow=$langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow,1);
1281  $result.=' <span class="note inline-block">';
1282  $result.='<a href="'.DOL_URL_ROOT.'/compta/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1283  $result.=img_picto('','note');
1284  $result.='</a>';
1285  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1286  //$result.='</a>';
1287  $result.='</span>';
1288  }
1289  }
1290 
1291  return $result;
1292  }
1293 
1304  function fetch($rowid, $ref='', $ref_ext='', $ref_int='', $fetch_situation=false)
1305  {
1306  global $conf;
1307 
1308  if (empty($rowid) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
1309 
1310  $sql = 'SELECT f.rowid,f.entity,f.facnumber,f.ref_client,f.ref_ext,f.ref_int,f.type,f.fk_soc,f.amount';
1311  $sql.= ', f.tva, f.localtax1, f.localtax2, f.total, f.total_ttc, f.revenuestamp';
1312  $sql.= ', f.remise_percent, f.remise_absolue, f.remise';
1313  $sql.= ', f.datef as df, f.date_pointoftax';
1314  $sql.= ', f.date_lim_reglement as dlr';
1315  $sql.= ', f.datec as datec';
1316  $sql.= ', f.date_valid as datev';
1317  $sql.= ', f.tms as datem';
1318  $sql.= ', f.note_private, f.note_public, f.fk_statut, f.paye, f.close_code, f.close_note, f.fk_user_author, f.fk_user_valid, f.model_pdf, f.last_main_doc';
1319  $sql.= ', f.fk_facture_source';
1320  $sql.= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet, f.extraparams';
1321  $sql.= ', f.situation_cycle_ref, f.situation_counter, f.situation_final';
1322  $sql.= ', f.fk_account';
1323  $sql.= ", f.fk_multicurrency, f.multicurrency_code, f.multicurrency_tx, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc";
1324  $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1325  $sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
1326  $sql.= ', f.fk_incoterms, f.location_incoterms';
1327  $sql.= ", i.libelle as libelle_incoterms";
1328  $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as f';
1329  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid';
1330  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id';
1331  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON f.fk_incoterms = i.rowid';
1332 
1333  if ($rowid) $sql.= " WHERE f.rowid=".$rowid;
1334  else $sql.= ' WHERE f.entity IN ('.getEntity('facture').')'; // Dont't use entity if you use rowid
1335 
1336  if ($ref) $sql.= " AND f.facnumber='".$this->db->escape($ref)."'";
1337  if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
1338  if ($ref_int) $sql.= " AND f.ref_int='".$this->db->escape($ref_int)."'";
1339 
1340  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1341  $result = $this->db->query($sql);
1342  if ($result)
1343  {
1344  if ($this->db->num_rows($result))
1345  {
1346  $obj = $this->db->fetch_object($result);
1347 
1348  $this->id = $obj->rowid;
1349  $this->entity = $obj->entity;
1350 
1351  $this->ref = $obj->facnumber;
1352  $this->ref_client = $obj->ref_client;
1353  $this->ref_ext = $obj->ref_ext;
1354  $this->ref_int = $obj->ref_int;
1355  $this->type = $obj->type;
1356  $this->date = $this->db->jdate($obj->df);
1357  $this->date_pointoftax = $this->db->jdate($obj->date_pointoftax);
1358  $this->date_creation = $this->db->jdate($obj->datec);
1359  $this->date_validation = $this->db->jdate($obj->datev);
1360  $this->date_modification = $this->db->jdate($obj->datem);
1361  $this->datem = $this->db->jdate($obj->datem);
1362  $this->remise_percent = $obj->remise_percent;
1363  $this->remise_absolue = $obj->remise_absolue;
1364  $this->total_ht = $obj->total;
1365  $this->total_tva = $obj->tva;
1366  $this->total_localtax1 = $obj->localtax1;
1367  $this->total_localtax2 = $obj->localtax2;
1368  $this->total_ttc = $obj->total_ttc;
1369  $this->revenuestamp = $obj->revenuestamp;
1370  $this->paye = $obj->paye;
1371  $this->close_code = $obj->close_code;
1372  $this->close_note = $obj->close_note;
1373  $this->socid = $obj->fk_soc;
1374  $this->statut = $obj->fk_statut;
1375  $this->date_lim_reglement = $this->db->jdate($obj->dlr);
1376  $this->mode_reglement_id = $obj->fk_mode_reglement;
1377  $this->mode_reglement_code = $obj->mode_reglement_code;
1378  $this->mode_reglement = $obj->mode_reglement_libelle;
1379  $this->cond_reglement_id = $obj->fk_cond_reglement;
1380  $this->cond_reglement_code = $obj->cond_reglement_code;
1381  $this->cond_reglement = $obj->cond_reglement_libelle;
1382  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1383  $this->fk_account = ($obj->fk_account>0)?$obj->fk_account:null;
1384  $this->fk_project = $obj->fk_projet;
1385  $this->fk_facture_source = $obj->fk_facture_source;
1386  $this->note = $obj->note_private; // deprecated
1387  $this->note_private = $obj->note_private;
1388  $this->note_public = $obj->note_public;
1389  $this->user_author = $obj->fk_user_author;
1390  $this->user_valid = $obj->fk_user_valid;
1391  $this->modelpdf = $obj->model_pdf;
1392  $this->last_main_doc = $obj->last_main_doc;
1393  $this->situation_cycle_ref = $obj->situation_cycle_ref;
1394  $this->situation_counter = $obj->situation_counter;
1395  $this->situation_final = $obj->situation_final;
1396  $this->extraparams = (array) json_decode($obj->extraparams, true);
1397 
1398  //Incoterms
1399  $this->fk_incoterms = $obj->fk_incoterms;
1400  $this->location_incoterms = $obj->location_incoterms;
1401  $this->libelle_incoterms = $obj->libelle_incoterms;
1402 
1403  // Multicurrency
1404  $this->fk_multicurrency = $obj->fk_multicurrency;
1405  $this->multicurrency_code = $obj->multicurrency_code;
1406  $this->multicurrency_tx = $obj->multicurrency_tx;
1407  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1408  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1409  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1410 
1411  if (($this->type == self::TYPE_SITUATION || ($this->type == self::TYPE_CREDIT_NOTE && $this->situation_cycle_ref > 0)) && $fetch_situation)
1412  {
1414  }
1415 
1416  if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1417 
1418  // Retreive all extrafield
1419  // fetch optionals attributes and labels
1420  $this->fetch_optionals();
1421 
1422  /*
1423  * Lines
1424  */
1425 
1426  $this->lines = array();
1427 
1428  $result=$this->fetch_lines();
1429  if ($result < 0)
1430  {
1431  $this->error=$this->db->error();
1432  return -3;
1433  }
1434  return 1;
1435  }
1436  else
1437  {
1438  $this->error='Invoice with id='.$rowid.' or ref='.$ref.' or ref_ext='.$ref_ext.' not found';
1439  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
1440  return 0;
1441  }
1442  }
1443  else
1444  {
1445  $this->error=$this->db->error();
1446  return -1;
1447  }
1448  }
1449 
1450 
1451  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1457  function fetch_lines()
1458  {
1459  // phpcs:enable
1460  $this->lines=array();
1461 
1462  $sql = 'SELECT l.rowid, l.fk_facture, l.fk_product, l.fk_parent_line, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx,';
1463  $sql.= ' l.situation_percent, l.fk_prev_id,';
1464  $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,';
1465  $sql.= ' l.rang, l.special_code,';
1466  $sql.= ' l.date_start as date_start, l.date_end as date_end,';
1467  $sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_localtax1, l.total_localtax2, l.total_ttc, l.fk_code_ventilation, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht,';
1468  $sql.= ' l.fk_unit,';
1469  $sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1470  $sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
1471  $sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet as l';
1472  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
1473  $sql.= ' WHERE l.fk_facture = '.$this->id;
1474  $sql.= ' ORDER BY l.rang, l.rowid';
1475 
1476  dol_syslog(get_class($this).'::fetch_lines', LOG_DEBUG);
1477  $result = $this->db->query($sql);
1478  if ($result)
1479  {
1480  $num = $this->db->num_rows($result);
1481  $i = 0;
1482  while ($i < $num)
1483  {
1484  $objp = $this->db->fetch_object($result);
1485  $line = new FactureLigne($this->db);
1486 
1487  $line->id = $objp->rowid;
1488  $line->rowid = $objp->rowid; // deprecated
1489  $line->fk_facture = $objp->fk_facture;
1490  $line->label = $objp->custom_label; // deprecated
1491  $line->desc = $objp->description; // Description line
1492  $line->description = $objp->description; // Description line
1493  $line->product_type = $objp->product_type; // Type of line
1494  $line->ref = $objp->product_ref; // Ref product
1495  $line->product_ref = $objp->product_ref; // Ref product
1496  $line->libelle = $objp->product_label; // TODO deprecated
1497  $line->product_label = $objp->product_label; // Label product
1498  $line->product_desc = $objp->product_desc; // Description product
1499  $line->fk_product_type = $objp->fk_product_type; // Type of product
1500  $line->qty = $objp->qty;
1501  $line->subprice = $objp->subprice;
1502 
1503  $line->vat_src_code = $objp->vat_src_code;
1504  $line->tva_tx = $objp->tva_tx;
1505  $line->localtax1_tx = $objp->localtax1_tx;
1506  $line->localtax2_tx = $objp->localtax2_tx;
1507  $line->localtax1_type = $objp->localtax1_type;
1508  $line->localtax2_type = $objp->localtax2_type;
1509  $line->remise_percent = $objp->remise_percent;
1510  $line->fk_remise_except = $objp->fk_remise_except;
1511  $line->fk_product = $objp->fk_product;
1512  $line->date_start = $this->db->jdate($objp->date_start);
1513  $line->date_end = $this->db->jdate($objp->date_end);
1514  $line->date_start = $this->db->jdate($objp->date_start);
1515  $line->date_end = $this->db->jdate($objp->date_end);
1516  $line->info_bits = $objp->info_bits;
1517  $line->total_ht = $objp->total_ht;
1518  $line->total_tva = $objp->total_tva;
1519  $line->total_localtax1 = $objp->total_localtax1;
1520  $line->total_localtax2 = $objp->total_localtax2;
1521  $line->total_ttc = $objp->total_ttc;
1522  $line->code_ventilation = $objp->fk_code_ventilation;
1523  $line->fk_fournprice = $objp->fk_fournprice;
1524  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1525  $line->pa_ht = $marginInfos[0];
1526  $line->marge_tx = $marginInfos[1];
1527  $line->marque_tx = $marginInfos[2];
1528  $line->rang = $objp->rang;
1529  $line->special_code = $objp->special_code;
1530  $line->fk_parent_line = $objp->fk_parent_line;
1531  $line->situation_percent= $objp->situation_percent;
1532  $line->fk_prev_id = $objp->fk_prev_id;
1533  $line->fk_unit = $objp->fk_unit;
1534 
1535  // Accountancy
1536  $line->fk_accounting_account = $objp->fk_code_ventilation;
1537 
1538  // Multicurrency
1539  $line->fk_multicurrency = $objp->fk_multicurrency;
1540  $line->multicurrency_code = $objp->multicurrency_code;
1541  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1542  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1543  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1544  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1545 
1546  $line->fetch_optionals();
1547 
1548  $this->lines[$i] = $line;
1549 
1550  $i++;
1551  }
1552  $this->db->free($result);
1553  return 1;
1554  }
1555  else
1556  {
1557  $this->error=$this->db->error();
1558  return -3;
1559  }
1560  }
1561 
1568  {
1569  global $conf;
1570 
1571  $this->tab_previous_situation_invoice = array();
1572  $this->tab_next_situation_invoice = array();
1573 
1574  $sql = 'SELECT rowid, situation_counter FROM '.MAIN_DB_PREFIX.'facture WHERE rowid <> '.$this->id.' AND entity = '.$conf->entity.' AND situation_cycle_ref = '.(int) $this->situation_cycle_ref.' ORDER BY situation_counter ASC';
1575 
1576  dol_syslog(get_class($this).'::fetchPreviousNextSituationInvoice ', LOG_DEBUG);
1577  $result = $this->db->query($sql);
1578  if ($result && $this->db->num_rows($result) > 0)
1579  {
1580  while ($objp = $this->db->fetch_object($result))
1581  {
1582  $invoice = new Facture($this->db);
1583  if ($invoice->fetch($objp->rowid) > 0)
1584  {
1585  if ($objp->situation_counter < $this->situation_counter
1586  || ($objp->situation_counter == $this->situation_counter && $objp->rowid < $this->id) // This case appear when there are credit notes
1587  )
1588  {
1589  $this->tab_previous_situation_invoice[] = $invoice;
1590  }
1591  else
1592  {
1593  $this->tab_next_situation_invoice[] = $invoice;
1594  }
1595  }
1596  }
1597  }
1598  }
1599 
1607  function update(User $user, $notrigger=0)
1608  {
1609  global $conf;
1610 
1611  $error=0;
1612 
1613  // Clean parameters
1614  if (empty($this->type)) $this->type= self::TYPE_STANDARD;
1615  if (isset($this->facnumber)) $this->facnumber=trim($this->ref);
1616  if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1617  if (isset($this->increment)) $this->increment=trim($this->increment);
1618  if (isset($this->close_code)) $this->close_code=trim($this->close_code);
1619  if (isset($this->close_note)) $this->close_note=trim($this->close_note);
1620  if (isset($this->note) || isset($this->note_private)) $this->note=(isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
1621  if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1622  if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1623  if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1624  if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1625 
1626  // Check parameters
1627  // Put here code to add control on parameters values
1628 
1629  // Update request
1630  $sql = "UPDATE ".MAIN_DB_PREFIX."facture SET";
1631  $sql.= " facnumber=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1632  $sql.= " type=".(isset($this->type)?$this->db->escape($this->type):"null").",";
1633  $sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1634  $sql.= " increment=".(isset($this->increment)?"'".$this->db->escape($this->increment)."'":"null").",";
1635  $sql.= " fk_soc=".(isset($this->socid)?$this->db->escape($this->socid):"null").",";
1636  $sql.= " datec=".(strval($this->date_creation)!='' ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1637  $sql.= " datef=".(strval($this->date)!='' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1638  $sql.= " date_pointoftax=".(strval($this->date_pointoftax)!='' ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null').",";
1639  $sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1640  $sql.= " paye=".(isset($this->paye)?$this->db->escape($this->paye):"null").",";
1641  $sql.= " remise_percent=".(isset($this->remise_percent)?$this->db->escape($this->remise_percent):"null").",";
1642  $sql.= " remise_absolue=".(isset($this->remise_absolue)?$this->db->escape($this->remise_absolue):"null").",";
1643  $sql.= " close_code=".(isset($this->close_code)?"'".$this->db->escape($this->close_code)."'":"null").",";
1644  $sql.= " close_note=".(isset($this->close_note)?"'".$this->db->escape($this->close_note)."'":"null").",";
1645  $sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1646  $sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1647  $sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1648  $sql.= " total=".(isset($this->total_ht)?$this->total_ht:"null").",";
1649  $sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1650  $sql.= " revenuestamp=".((isset($this->revenuestamp) && $this->revenuestamp != '')?$this->db->escape($this->revenuestamp):"null").",";
1651  $sql.= " fk_statut=".(isset($this->statut)?$this->db->escape($this->statut):"null").",";
1652  $sql.= " fk_user_author=".(isset($this->user_author)?$this->db->escape($this->user_author):"null").",";
1653  $sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->db->escape($this->fk_user_valid):"null").",";
1654  $sql.= " fk_facture_source=".(isset($this->fk_facture_source)?$this->db->escape($this->fk_facture_source):"null").",";
1655  $sql.= " fk_projet=".(isset($this->fk_project)?$this->db->escape($this->fk_project):"null").",";
1656  $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->db->escape($this->cond_reglement_id):"null").",";
1657  $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->db->escape($this->mode_reglement_id):"null").",";
1658  $sql.= " date_lim_reglement=".(strval($this->date_lim_reglement)!='' ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'null').",";
1659  $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1660  $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1661  $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1662  $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null").",";
1663  $sql.= " situation_cycle_ref=".(empty($this->situation_cycle_ref)?"null":$this->db->escape($this->situation_cycle_ref)).",";
1664  $sql.= " situation_counter=".(empty($this->situation_counter)?"null":$this->db->escape($this->situation_counter)).",";
1665  $sql.= " situation_final=".(empty($this->situation_counter)?"0":$this->db->escape($this->situation_counter));
1666  $sql.= " WHERE rowid=".$this->id;
1667 
1668  $this->db->begin();
1669 
1670  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1671  $resql = $this->db->query($sql);
1672  if (! $resql) {
1673  $error++; $this->errors[]="Error ".$this->db->lasterror();
1674  }
1675 
1676  if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
1677  {
1678  $result=$this->insertExtraFields();
1679  if ($result < 0)
1680  {
1681  $error++;
1682  }
1683  }
1684 
1685  if (! $error && ! $notrigger)
1686  {
1687  // Call trigger
1688  $result=$this->call_trigger('BILL_MODIFY',$user);
1689  if ($result < 0) $error++;
1690  // End call triggers
1691  }
1692 
1693  // Commit or rollback
1694  if ($error)
1695  {
1696  foreach($this->errors as $errmsg)
1697  {
1698  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1699  $this->error.=($this->error?', '.$errmsg:$errmsg);
1700  }
1701  $this->db->rollback();
1702  return -1*$error;
1703  }
1704  else
1705  {
1706  $this->db->commit();
1707  return 1;
1708  }
1709  }
1710 
1711 
1712  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1719  function insert_discount($idremise)
1720  {
1721  // phpcs:enable
1722  global $langs;
1723 
1724  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1725  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1726 
1727  $this->db->begin();
1728 
1729  $remise=new DiscountAbsolute($this->db);
1730  $result=$remise->fetch($idremise);
1731 
1732  if ($result > 0)
1733  {
1734  if ($remise->fk_facture) // Protection against multiple submission
1735  {
1736  $this->error=$langs->trans("ErrorDiscountAlreadyUsed");
1737  $this->db->rollback();
1738  return -5;
1739  }
1740 
1741  $facligne=new FactureLigne($this->db);
1742  $facligne->fk_facture=$this->id;
1743  $facligne->fk_remise_except=$remise->id;
1744  $facligne->desc=$remise->description; // Description ligne
1745  $facligne->vat_src_code=$remise->vat_src_code;
1746  $facligne->tva_tx=$remise->tva_tx;
1747  $facligne->subprice = -$remise->amount_ht;
1748  $facligne->fk_product=0; // Id produit predefini
1749  $facligne->qty=1;
1750  $facligne->remise_percent=0;
1751  $facligne->rang=-1;
1752  $facligne->info_bits=2;
1753 
1754  // Get buy/cost price of invoice that is source of discount
1755  if ($remise->fk_facture_source > 0)
1756  {
1757  $srcinvoice=new Facture($this->db);
1758  $srcinvoice->fetch($remise->fk_facture_source);
1759  $totalcostpriceofinvoice=0;
1760  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1761  $formmargin=new FormMargin($this->db);
1762  $arraytmp=$formmargin->getMarginInfosArray($srcinvoice, false);
1763  $facligne->pa_ht = $arraytmp['pa_total'];
1764  }
1765 
1766  $facligne->total_ht = -$remise->amount_ht;
1767  $facligne->total_tva = -$remise->amount_tva;
1768  $facligne->total_ttc = -$remise->amount_ttc;
1769 
1770  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1771  $facligne->multicurrency_total_ht = -$remise->multicurrency_amount_ht;
1772  $facligne->multicurrency_total_tva = -$remise->multicurrency_amount_tva;
1773  $facligne->multicurrency_total_ttc = -$remise->multicurrency_amount_ttc;
1774 
1775  $lineid=$facligne->insert();
1776  if ($lineid > 0)
1777  {
1778  $result=$this->update_price(1);
1779  if ($result > 0)
1780  {
1781  // Create link between discount and invoice line
1782  $result=$remise->link_to_invoice($lineid,0);
1783  if ($result < 0)
1784  {
1785  $this->error=$remise->error;
1786  $this->db->rollback();
1787  return -4;
1788  }
1789 
1790  $this->db->commit();
1791  return 1;
1792  }
1793  else
1794  {
1795  $this->error=$facligne->error;
1796  $this->db->rollback();
1797  return -1;
1798  }
1799  }
1800  else
1801  {
1802  $this->error=$facligne->error;
1803  $this->db->rollback();
1804  return -2;
1805  }
1806  }
1807  else
1808  {
1809  $this->db->rollback();
1810  return -3;
1811  }
1812  }
1813 
1814  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1822  function set_ref_client($ref_client, $notrigger=0)
1823  {
1824  // phpcs:enable
1825  global $user;
1826 
1827  $error=0;
1828 
1829  $this->db->begin();
1830 
1831  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
1832  if (empty($ref_client))
1833  $sql .= ' SET ref_client = NULL';
1834  else
1835  $sql .= ' SET ref_client = \''.$this->db->escape($ref_client).'\'';
1836  $sql .= ' WHERE rowid = '.$this->id;
1837 
1838  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
1839  $resql=$this->db->query($sql);
1840  if (!$resql)
1841  {
1842  $this->errors[]=$this->db->error();
1843  $error++;
1844  }
1845 
1846  if (! $error)
1847  {
1848  $this->ref_client = $ref_client;
1849  }
1850 
1851  if (! $notrigger && empty($error))
1852  {
1853  // Call trigger
1854  $result=$this->call_trigger('BILL_MODIFY',$user);
1855  if ($result < 0) $error++;
1856  // End call triggers
1857  }
1858 
1859  if (! $error)
1860  {
1861 
1862  $this->ref_client = $ref_client;
1863 
1864  $this->db->commit();
1865  return 1;
1866  }
1867  else
1868  {
1869  foreach($this->errors as $errmsg)
1870  {
1871  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1872  $this->error.=($this->error?', '.$errmsg:$errmsg);
1873  }
1874  $this->db->rollback();
1875  return -1*$error;
1876  }
1877  }
1878 
1887  function delete($user, $notrigger=0, $idwarehouse=-1)
1888  {
1889  global $langs,$conf;
1890  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1891 
1892  $rowid=$this->id;
1893 
1894  dol_syslog(get_class($this)."::delete rowid=".$rowid.", ref=".$this->ref.", thirdparty=".$this->thirdparty->name, LOG_DEBUG);
1895 
1896  // Test to avoid invoice deletion (allowed if draft)
1897  $result = $this->is_erasable();
1898 
1899  if ($result <= 0) return 0;
1900 
1901  $error=0;
1902 
1903  $this->db->begin();
1904 
1905  if (! $error && ! $notrigger)
1906  {
1907  // Call trigger
1908  $result=$this->call_trigger('BILL_DELETE',$user);
1909  if ($result < 0) $error++;
1910  // End call triggers
1911  }
1912 
1913  // Removed extrafields
1914  if (! $error) {
1915  $result=$this->deleteExtraFields();
1916  if ($result < 0)
1917  {
1918  $error++;
1919  dol_syslog(get_class($this)."::delete error deleteExtraFields ".$this->error, LOG_ERR);
1920  }
1921  }
1922 
1923  if (! $error)
1924  {
1925  // Delete linked object
1926  $res = $this->deleteObjectLinked();
1927  if ($res < 0) $error++;
1928  }
1929 
1930  if (! $error)
1931  {
1932  // If invoice was converted into a discount not yet consumed, we remove discount
1933  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1934  $sql.= ' WHERE fk_facture_source = '.$rowid;
1935  $sql.= ' AND fk_facture_line IS NULL';
1936  $resql=$this->db->query($sql);
1937 
1938  // If invoice has consumned discounts
1939  $this->fetch_lines();
1940  $list_rowid_det=array();
1941  foreach($this->lines as $key => $invoiceline)
1942  {
1943  $list_rowid_det[]=$invoiceline->rowid;
1944  }
1945 
1946  // Consumned discounts are freed
1947  if (count($list_rowid_det))
1948  {
1949  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1950  $sql.= ' SET fk_facture = NULL, fk_facture_line = NULL';
1951  $sql.= ' WHERE fk_facture_line IN ('.join(',',$list_rowid_det).')';
1952 
1953  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1954  if (! $this->db->query($sql))
1955  {
1956  $this->error=$this->db->error()." sql=".$sql;
1957  $this->db->rollback();
1958  return -5;
1959  }
1960  }
1961 
1962  // If we decrement stock on invoice validation, we increment
1963  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL) && $idwarehouse!=-1)
1964  {
1965  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1966  $langs->load("agenda");
1967 
1968  $num=count($this->lines);
1969  for ($i = 0; $i < $num; $i++)
1970  {
1971  if ($this->lines[$i]->fk_product > 0)
1972  {
1973  $mouvP = new MouvementStock($this->db);
1974  $mouvP->origin = &$this;
1975  // We decrease stock for product
1976  if ($this->type == self::TYPE_CREDIT_NOTE) $result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceDeleteDolibarr",$this->ref));
1977  else $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("InvoiceDeleteDolibarr",$this->ref)); // we use 0 for price, to not change the weighted average value
1978  }
1979  }
1980  }
1981 
1982 
1983  // Delete invoice line
1984  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facturedet WHERE fk_facture = '.$rowid;
1985 
1986  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1987 
1988  if ($this->db->query($sql) && $this->delete_linked_contact())
1989  {
1990  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture WHERE rowid = '.$rowid;
1991 
1992  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1993 
1994  $resql=$this->db->query($sql);
1995  if ($resql)
1996  {
1997  // On efface le repertoire de pdf provisoire
1998  $ref = dol_sanitizeFileName($this->ref);
1999  if ($conf->facture->dir_output && !empty($this->ref))
2000  {
2001  $dir = $conf->facture->dir_output . "/" . $ref;
2002  $file = $conf->facture->dir_output . "/" . $ref . "/" . $ref . ".pdf";
2003  if (file_exists($file)) // We must delete all files before deleting directory
2004  {
2005  $ret=dol_delete_preview($this);
2006 
2007  if (! dol_delete_file($file,0,0,0,$this)) // For triggers
2008  {
2009  $langs->load("errors");
2010  $this->error=$langs->trans("ErrorFailToDeleteFile",$file);
2011  $this->db->rollback();
2012  return 0;
2013  }
2014  }
2015  if (file_exists($dir))
2016  {
2017  if (! dol_delete_dir_recursive($dir)) // For remove dir and meta
2018  {
2019  $langs->load("errors");
2020  $this->error=$langs->trans("ErrorFailToDeleteDir",$dir);
2021  $this->db->rollback();
2022  return 0;
2023  }
2024  }
2025  }
2026 
2027  $this->db->commit();
2028  return 1;
2029  }
2030  else
2031  {
2032  $this->error=$this->db->lasterror()." sql=".$sql;
2033  $this->db->rollback();
2034  return -6;
2035  }
2036  }
2037  else
2038  {
2039  $this->error=$this->db->lasterror()." sql=".$sql;
2040  $this->db->rollback();
2041  return -4;
2042  }
2043  }
2044  else
2045  {
2046  $this->db->rollback();
2047  return -2;
2048  }
2049  }
2050 
2051  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2061  function set_paid($user, $close_code='', $close_note='')
2062  {
2063  // phpcs:enable
2064  $error=0;
2065 
2066  if ($this->paye != 1)
2067  {
2068  $this->db->begin();
2069 
2070  dol_syslog(get_class($this)."::set_paid rowid=".$this->id, LOG_DEBUG);
2071 
2072  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture SET';
2073  $sql.= ' fk_statut='.self::STATUS_CLOSED;
2074  if (! $close_code) $sql.= ', paye=1';
2075  if ($close_code) $sql.= ", close_code='".$this->db->escape($close_code)."'";
2076  if ($close_note) $sql.= ", close_note='".$this->db->escape($close_note)."'";
2077  $sql.= ' WHERE rowid = '.$this->id;
2078 
2079  $resql = $this->db->query($sql);
2080  if ($resql)
2081  {
2082  // Call trigger
2083  $result=$this->call_trigger('BILL_PAYED',$user);
2084  if ($result < 0) $error++;
2085  // End call triggers
2086  }
2087  else
2088  {
2089  $error++;
2090  $this->error=$this->db->lasterror();
2091  }
2092 
2093  if (! $error)
2094  {
2095  $this->db->commit();
2096  return 1;
2097  }
2098  else
2099  {
2100  $this->db->rollback();
2101  return -1;
2102  }
2103  }
2104  else
2105  {
2106  return 0;
2107  }
2108  }
2109 
2110 
2111  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2120  function set_unpaid($user)
2121  {
2122  // phpcs:enable
2123  $error=0;
2124 
2125  $this->db->begin();
2126 
2127  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
2128  $sql.= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null';
2129  $sql.= ' WHERE rowid = '.$this->id;
2130 
2131  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
2132  $resql = $this->db->query($sql);
2133  if ($resql)
2134  {
2135  // Call trigger
2136  $result=$this->call_trigger('BILL_UNPAYED',$user);
2137  if ($result < 0) $error++;
2138  // End call triggers
2139  }
2140  else
2141  {
2142  $error++;
2143  $this->error=$this->db->error();
2144  dol_print_error($this->db);
2145  }
2146 
2147  if (! $error)
2148  {
2149  $this->db->commit();
2150  return 1;
2151  }
2152  else
2153  {
2154  $this->db->rollback();
2155  return -1;
2156  }
2157  }
2158 
2159 
2160  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2171  function set_canceled($user, $close_code='', $close_note='')
2172  {
2173  // phpcs:enable
2174 
2175  dol_syslog(get_class($this)."::set_canceled rowid=".$this->id, LOG_DEBUG);
2176 
2177  $this->db->begin();
2178 
2179  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture SET';
2180  $sql.= ' fk_statut='.self::STATUS_ABANDONED;
2181  if ($close_code) $sql.= ", close_code='".$this->db->escape($close_code)."'";
2182  if ($close_note) $sql.= ", close_note='".$this->db->escape($close_note)."'";
2183  $sql.= ' WHERE rowid = '.$this->id;
2184 
2185  $resql = $this->db->query($sql);
2186  if ($resql)
2187  {
2188  // On desaffecte de la facture les remises liees
2189  // car elles n'ont pas ete utilisees vu que la facture est abandonnee.
2190  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2191  $sql.= ' SET fk_facture = NULL';
2192  $sql.= ' WHERE fk_facture = '.$this->id;
2193 
2194  $resql=$this->db->query($sql);
2195  if ($resql)
2196  {
2197  // Call trigger
2198  $result=$this->call_trigger('BILL_CANCEL',$user);
2199  if ($result < 0)
2200  {
2201  $this->db->rollback();
2202  return -1;
2203  }
2204  // End call triggers
2205 
2206  $this->db->commit();
2207  return 1;
2208  }
2209  else
2210  {
2211  $this->error=$this->db->error()." sql=".$sql;
2212  $this->db->rollback();
2213  return -1;
2214  }
2215  }
2216  else
2217  {
2218  $this->error=$this->db->error()." sql=".$sql;
2219  $this->db->rollback();
2220  return -2;
2221  }
2222  }
2223 
2234  function validate($user, $force_number='', $idwarehouse=0, $notrigger=0)
2235  {
2236  global $conf,$langs;
2237  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2238 
2239  $now=dol_now();
2240 
2241  $error=0;
2242  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
2243 
2244  // Force to have object complete for checks
2245  $this->fetch_thirdparty();
2246  $this->fetch_lines();
2247 
2248  // Check parameters
2249  if (! $this->brouillon)
2250  {
2251  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
2252  return 0;
2253  }
2254  if (count($this->lines) <= 0)
2255  {
2256  $langs->load("errors");
2257  $this->error=$langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
2258  return -1;
2259  }
2260  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->facture->creer))
2261  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->facture->invoice_advance->validate)))
2262  {
2263  $this->error='Permission denied';
2264  dol_syslog(get_class($this)."::validate ".$this->error.' MAIN_USE_ADVANCED_PERMS='.$conf->global->MAIN_USE_ADVANCED_PERMS, LOG_ERR);
2265  return -1;
2266  }
2267 
2268  $this->db->begin();
2269 
2270  // Check parameters
2271  if ($this->type == self::TYPE_REPLACEMENT) // si facture de remplacement
2272  {
2273  // Controle que facture source connue
2274  if ($this->fk_facture_source <= 0)
2275  {
2276  $this->error=$langs->trans("ErrorFieldRequired",$langs->trans("InvoiceReplacement"));
2277  $this->db->rollback();
2278  return -10;
2279  }
2280 
2281  // Charge la facture source a remplacer
2282  $facreplaced=new Facture($this->db);
2283  $result=$facreplaced->fetch($this->fk_facture_source);
2284  if ($result <= 0)
2285  {
2286  $this->error=$langs->trans("ErrorBadInvoice");
2287  $this->db->rollback();
2288  return -11;
2289  }
2290 
2291  // Controle que facture source non deja remplacee par une autre
2292  $idreplacement=$facreplaced->getIdReplacingInvoice('validated');
2293  if ($idreplacement && $idreplacement != $this->id)
2294  {
2295  $facreplacement=new Facture($this->db);
2296  $facreplacement->fetch($idreplacement);
2297  $this->error=$langs->trans("ErrorInvoiceAlreadyReplaced",$facreplaced->ref,$facreplacement->ref);
2298  $this->db->rollback();
2299  return -12;
2300  }
2301 
2302  $result=$facreplaced->set_canceled($user, self::CLOSECODE_REPLACED, '');
2303  if ($result < 0)
2304  {
2305  $this->error=$facreplaced->error;
2306  $this->db->rollback();
2307  return -13;
2308  }
2309  }
2310 
2311  // Define new ref
2312  if ($force_number)
2313  {
2314  $num = $force_number;
2315  }
2316  else if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) // empty should not happened, but when it occurs, the test save life
2317  {
2318  if (! empty($conf->global->FAC_FORCE_DATE_VALIDATION)) // If option enabled, we force invoice date
2319  {
2320  $this->date=dol_now();
2321  $this->date_lim_reglement=$this->calculate_date_lim_reglement();
2322  }
2323  $num = $this->getNextNumRef($this->thirdparty);
2324  }
2325  else
2326  {
2327  $num = $this->ref;
2328  }
2329  $this->newref = $num;
2330 
2331  if ($num)
2332  {
2333  $this->update_price(1);
2334 
2335  // Validate
2336  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
2337  $sql.= " SET facnumber='".$num."', fk_statut = ".self::STATUS_VALIDATED.", fk_user_valid = ".($user->id > 0 ? $user->id : "null").", date_valid = '".$this->db->idate($now)."'";
2338  if (! empty($conf->global->FAC_FORCE_DATE_VALIDATION)) // If option enabled, we force invoice date
2339  {
2340  $sql.= ", datef='".$this->db->idate($this->date)."'";
2341  $sql.= ", date_lim_reglement='".$this->db->idate($this->date_lim_reglement)."'";
2342  }
2343  $sql.= ' WHERE rowid = '.$this->id;
2344 
2345  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
2346  $resql=$this->db->query($sql);
2347  if (! $resql)
2348  {
2349  dol_print_error($this->db);
2350  $error++;
2351  }
2352 
2353  // On verifie si la facture etait une provisoire
2354  if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref)))
2355  {
2356  // La verif qu'une remise n'est pas utilisee 2 fois est faite au moment de l'insertion de ligne
2357  }
2358 
2359  if (! $error)
2360  {
2361  // Define third party as a customer
2362  $result=$this->thirdparty->set_as_client();
2363 
2364  // Si active on decremente le produit principal et ses composants a la validation de facture
2365  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL) && $idwarehouse > 0)
2366  {
2367  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2368  $langs->load("agenda");
2369 
2370  // Loop on each line
2371  $cpt=count($this->lines);
2372  for ($i = 0; $i < $cpt; $i++)
2373  {
2374  if ($this->lines[$i]->fk_product > 0)
2375  {
2376  $mouvP = new MouvementStock($this->db);
2377  $mouvP->origin = &$this;
2378  // We decrease stock for product
2379  if ($this->type == self::TYPE_CREDIT_NOTE) $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("InvoiceValidatedInDolibarr",$num));
2380  else $result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceValidatedInDolibarr",$num));
2381  if ($result < 0) {
2382  $error++;
2383  $this->error = $mouvP->error;
2384  }
2385  }
2386  }
2387  }
2388  }
2389 
2390  // Trigger calls
2391  if (! $error && ! $notrigger)
2392  {
2393  // Call trigger
2394  $result=$this->call_trigger('BILL_VALIDATE',$user);
2395  if ($result < 0) $error++;
2396  // End call triggers
2397  }
2398 
2399  if (! $error)
2400  {
2401  $this->oldref = $this->ref;
2402 
2403  // Rename directory if dir was a temporary ref
2404  if (preg_match('/^[\(]?PROV/i', $this->ref))
2405  {
2406  // Rename of object directory ($this->ref = old ref, $num = new ref)
2407  // to not lose the linked files
2408  $oldref = dol_sanitizeFileName($this->ref);
2409  $newref = dol_sanitizeFileName($num);
2410  $dirsource = $conf->facture->dir_output.'/'.$oldref;
2411  $dirdest = $conf->facture->dir_output.'/'.$newref;
2412  if (file_exists($dirsource))
2413  {
2414  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2415 
2416  if (@rename($dirsource, $dirdest))
2417  {
2418  dol_syslog("Rename ok");
2419  // Rename docs starting with $oldref with $newref
2420  $listoffiles=dol_dir_list($conf->facture->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
2421  foreach($listoffiles as $fileentry)
2422  {
2423  $dirsource=$fileentry['name'];
2424  $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
2425  $dirsource=$fileentry['path'].'/'.$dirsource;
2426  $dirdest=$fileentry['path'].'/'.$dirdest;
2427  @rename($dirsource, $dirdest);
2428  }
2429  }
2430  }
2431  }
2432  }
2433 
2434  if (! $error && !$this->is_last_in_cycle())
2435  {
2436  if (! $this->updatePriceNextInvoice($langs))
2437  {
2438  $error++;
2439  }
2440  }
2441 
2442  // Set new ref and define current status
2443  if (! $error)
2444  {
2445  $this->ref = $num;
2446  $this->facnumber=$num;
2447  $this->statut= self::STATUS_VALIDATED;
2448  $this->brouillon=0;
2449  $this->date_validation=$now;
2450  $i = 0;
2451 
2452  if (!empty($conf->global->INVOICE_USE_SITUATION))
2453  {
2454  $final = true;
2455  $nboflines = count($this->lines);
2456  while (($i < $nboflines) && $final) {
2457  $final = ($this->lines[$i]->situation_percent == 100);
2458  $i++;
2459  }
2460 
2461  if (empty($final)) $this->situation_final = 0;
2462  else $this->situation_final = 1;
2463 
2464  $this->setFinal($user);
2465  }
2466  }
2467  }
2468  else
2469  {
2470  $error++;
2471  }
2472 
2473  if (! $error)
2474  {
2475  $this->db->commit();
2476  return 1;
2477  }
2478  else
2479  {
2480  $this->db->rollback();
2481  return -1;
2482  }
2483  }
2484 
2491  function updatePriceNextInvoice(&$langs)
2492  {
2493  foreach ($this->tab_next_situation_invoice as $next_invoice)
2494  {
2495  $is_last = $next_invoice->is_last_in_cycle();
2496 
2497  if ($next_invoice->brouillon && $is_last != 1)
2498  {
2499  $this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
2500  return false;
2501  }
2502 
2503  $next_invoice->brouillon = 1;
2504  foreach ($next_invoice->lines as $line)
2505  {
2506  $result = $next_invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent,
2507  $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type,
2508  $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent,
2509  $line->fk_unit);
2510 
2511  if ($result < 0)
2512  {
2513  $this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
2514  return false;
2515  }
2516  }
2517 
2518  break; // Only the next invoice and not each next invoice
2519  }
2520 
2521  return true;
2522  }
2523 
2524  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2532  function set_draft($user,$idwarehouse=-1)
2533  {
2534  // phpcs:enable
2535  global $conf,$langs;
2536 
2537  $error=0;
2538 
2539  if ($this->statut == self::STATUS_DRAFT)
2540  {
2541  dol_syslog(get_class($this)."::set_draft already draft status", LOG_WARNING);
2542  return 0;
2543  }
2544 
2545  $this->db->begin();
2546 
2547  $sql = "UPDATE ".MAIN_DB_PREFIX."facture";
2548  $sql.= " SET fk_statut = ".self::STATUS_DRAFT;
2549  $sql.= " WHERE rowid = ".$this->id;
2550 
2551  dol_syslog(get_class($this)."::set_draft", LOG_DEBUG);
2552  $result=$this->db->query($sql);
2553  if ($result)
2554  {
2555  // Si on decremente le produit principal et ses composants a la validation de facture, on réincrement
2556  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL))
2557  {
2558  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2559  $langs->load("agenda");
2560 
2561  $num=count($this->lines);
2562  for ($i = 0; $i < $num; $i++)
2563  {
2564  if ($this->lines[$i]->fk_product > 0)
2565  {
2566  $mouvP = new MouvementStock($this->db);
2567  $mouvP->origin = &$this;
2568  // We decrease stock for product
2569  if ($this->type == self::TYPE_CREDIT_NOTE) $result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr",$this->ref));
2570  else $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("InvoiceBackToDraftInDolibarr",$this->ref)); // we use 0 for price, to not change the weighted average value
2571  }
2572  }
2573  }
2574 
2575  if ($error == 0)
2576  {
2577  $old_statut=$this->statut;
2578  $this->brouillon = 1;
2579  $this->statut = self::STATUS_DRAFT;
2580  // Call trigger
2581  $result=$this->call_trigger('BILL_UNVALIDATE',$user);
2582  if ($result < 0)
2583  {
2584  $error++;
2585  $this->statut=$old_statut;
2586  $this->brouillon=0;
2587  }
2588  // End call triggers
2589  } else {
2590  $this->db->rollback();
2591  return -1;
2592  }
2593 
2594  if ($error == 0)
2595  {
2596  $this->db->commit();
2597  return 1;
2598  }
2599  else
2600  {
2601  $this->db->rollback();
2602  return -1;
2603  }
2604  }
2605  else
2606  {
2607  $this->error=$this->db->error();
2608  $this->db->rollback();
2609  return -1;
2610  }
2611  }
2612 
2613 
2652  function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $date_start='', $date_end='', $ventil=0, $info_bits=0, $fk_remise_except='', $price_base_type='HT', $pu_ttc=0, $type=self::TYPE_STANDARD, $rang=-1, $special_code=0, $origin='', $origin_id=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $situation_percent=100, $fk_prev_id=0, $fk_unit = null, $pu_ht_devise = 0)
2653  {
2654  // Deprecation warning
2655  if ($label) {
2656  dol_syslog(__METHOD__ . ": using line label is deprecated", LOG_WARNING);
2657  //var_dump(debug_backtrace(false));exit;
2658  }
2659 
2660  global $mysoc, $conf, $langs;
2661 
2662  dol_syslog(get_class($this)."::addline id=$this->id,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, fk_product=$fk_product,remise_percent=$remise_percent,date_start=$date_start,date_end=$date_end,ventil=$ventil,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type, fk_unit=$fk_unit", LOG_DEBUG);
2663  if (! empty($this->brouillon))
2664  {
2665  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2666 
2667  // Clean parameters
2668  if (empty($remise_percent)) $remise_percent=0;
2669  if (empty($qty)) $qty=0;
2670  if (empty($info_bits)) $info_bits=0;
2671  if (empty($rang)) $rang=0;
2672  if (empty($ventil)) $ventil=0;
2673  if (empty($txtva)) $txtva=0;
2674  if (empty($txlocaltax1)) $txlocaltax1=0;
2675  if (empty($txlocaltax2)) $txlocaltax2=0;
2676  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
2677  if (empty($fk_prev_id)) $fk_prev_id = 'null';
2678  if (! isset($situation_percent) || $situation_percent > 100 || (string) $situation_percent == '') $situation_percent = 100;
2679 
2680  $remise_percent=price2num($remise_percent);
2681  $qty=price2num($qty);
2682  $pu_ht=price2num($pu_ht);
2683  $pu_ht_devise=price2num($pu_ht_devise);
2684  $pu_ttc=price2num($pu_ttc);
2685  $pa_ht=price2num($pa_ht);
2686  if (!preg_match('/\((.*)\)/', $txtva)) {
2687  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2688  }
2689  $txlocaltax1=price2num($txlocaltax1);
2690  $txlocaltax2=price2num($txlocaltax2);
2691 
2692  if ($price_base_type=='HT')
2693  {
2694  $pu=$pu_ht;
2695  }
2696  else
2697  {
2698  $pu=$pu_ttc;
2699  }
2700 
2701  // Check parameters
2702  if ($type < 0) return -1;
2703 
2704  $this->db->begin();
2705 
2706  $product_type=$type;
2707  if (!empty($fk_product))
2708  {
2709  $product=new Product($this->db);
2710  $result=$product->fetch($fk_product);
2711  $product_type=$product->type;
2712 
2713  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE) && $product_type == 0 && $product->stock_reel < $qty) {
2714  $langs->load("errors");
2715  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
2716  $this->db->rollback();
2717  return -3;
2718  }
2719  }
2720 
2721  $localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
2722 
2723  // Clean vat code
2724  $vat_src_code='';
2725  if (preg_match('/\((.*)\)/', $txtva, $reg))
2726  {
2727  $vat_src_code = $reg[1];
2728  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2729  }
2730 
2731  // Calcul du total TTC et de la TVA pour la ligne a partir de
2732  // qty, pu, remise_percent et txtva
2733  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2734  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2735 
2736  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, $situation_percent, $this->multicurrency_tx, $pu_ht_devise);
2737 
2738  $total_ht = $tabprice[0];
2739  $total_tva = $tabprice[1];
2740  $total_ttc = $tabprice[2];
2741  $total_localtax1 = $tabprice[9];
2742  $total_localtax2 = $tabprice[10];
2743  $pu_ht = $tabprice[3];
2744 
2745  // MultiCurrency
2746  $multicurrency_total_ht = $tabprice[16];
2747  $multicurrency_total_tva = $tabprice[17];
2748  $multicurrency_total_ttc = $tabprice[18];
2749  $pu_ht_devise = $tabprice[19];
2750 
2751  // Rank to use
2752  $rangtouse = $rang;
2753  if ($rangtouse == -1)
2754  {
2755  $rangmax = $this->line_max($fk_parent_line);
2756  $rangtouse = $rangmax + 1;
2757  }
2758 
2759  // Insert line
2760  $this->line=new FactureLigne($this->db);
2761 
2762  $this->line->context = $this->context;
2763 
2764  $this->line->fk_facture=$this->id;
2765  $this->line->label=$label; // deprecated
2766  $this->line->desc=$desc;
2767 
2768  $this->line->qty= ($this->type==self::TYPE_CREDIT_NOTE?abs($qty):$qty); // For credit note, quantity is always positive and unit price negative
2769  $this->line->subprice= ($this->type==self::TYPE_CREDIT_NOTE?-abs($pu_ht):$pu_ht); // For credit note, unit price always negative, always positive otherwise
2770 
2771  $this->line->vat_src_code=$vat_src_code;
2772  $this->line->tva_tx=$txtva;
2773  $this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
2774  $this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
2775  $this->line->localtax1_type = $localtaxes_type[0];
2776  $this->line->localtax2_type = $localtaxes_type[2];
2777 
2778  $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
2779  $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
2780  $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
2781  $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
2782  $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
2783 
2784  $this->line->fk_product=$fk_product;
2785  $this->line->product_type=$product_type;
2786  $this->line->remise_percent=$remise_percent;
2787  $this->line->date_start=$date_start;
2788  $this->line->date_end=$date_end;
2789  $this->line->ventil=$ventil;
2790  $this->line->rang=$rangtouse;
2791  $this->line->info_bits=$info_bits;
2792  $this->line->fk_remise_except=$fk_remise_except;
2793 
2794  $this->line->special_code=$special_code;
2795  $this->line->fk_parent_line=$fk_parent_line;
2796  $this->line->origin=$origin;
2797  $this->line->origin_id=$origin_id;
2798  $this->line->situation_percent = $situation_percent;
2799  $this->line->fk_prev_id = $fk_prev_id;
2800  $this->line->fk_unit=$fk_unit;
2801 
2802  // infos marge
2803  $this->line->fk_fournprice = $fk_fournprice;
2804  $this->line->pa_ht = $pa_ht;
2805 
2806  // Multicurrency
2807  $this->line->fk_multicurrency = $this->fk_multicurrency;
2808  $this->line->multicurrency_code = $this->multicurrency_code;
2809  $this->line->multicurrency_subprice = $pu_ht_devise;
2810  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2811  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2812  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2813 
2814  if (is_array($array_options) && count($array_options)>0) {
2815  $this->line->array_options=$array_options;
2816  }
2817 
2818  $result=$this->line->insert();
2819  if ($result > 0)
2820  {
2821  // Reorder if child line
2822  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
2823 
2824  // Mise a jour informations denormalisees au niveau de la facture meme
2825  $result=$this->update_price(1,'auto',0,$mysoc); // The addline method is designed to add line from user input so total calculation with update_price must be done using 'auto' mode.
2826 
2827  if ($result > 0)
2828  {
2829  $this->db->commit();
2830  return $this->line->id;
2831  }
2832  else
2833  {
2834  $this->error=$this->db->lasterror();
2835  $this->db->rollback();
2836  return -1;
2837  }
2838  }
2839  else
2840  {
2841  $this->error=$this->line->error;
2842  $this->db->rollback();
2843  return -2;
2844  }
2845  }
2846  else
2847  {
2848  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
2849  return -3;
2850  }
2851  }
2852 
2882  function updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $txtva, $txlocaltax1=0, $txlocaltax2=0, $price_base_type='HT', $info_bits=0, $type= self::TYPE_STANDARD, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $situation_percent=100, $fk_unit = null, $pu_ht_devise = 0, $notrigger=0)
2883  {
2884  global $conf,$user;
2885  // Deprecation warning
2886  if ($label) {
2887  dol_syslog(__METHOD__ . ": using line label is deprecated", LOG_WARNING);
2888  }
2889 
2890  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2891 
2892  global $mysoc,$langs;
2893 
2894  dol_syslog(get_class($this)."::updateline rowid=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, date_start=$date_start, date_end=$date_end, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, type=$type, fk_parent_line=$fk_parent_line pa_ht=$pa_ht, special_code=$special_code, fk_unit=$fk_unit, pu_ht_devise=$pu_ht_devise", LOG_DEBUG);
2895 
2896  if ($this->brouillon)
2897  {
2898  if (!$this->is_last_in_cycle() && empty($this->error))
2899  {
2900  if (!$this->checkProgressLine($rowid, $situation_percent))
2901  {
2902  if (!$this->error) $this->error=$langs->trans('invoiceLineProgressError');
2903  return -3;
2904  }
2905  }
2906 
2907  $this->db->begin();
2908 
2909  // Clean parameters
2910  if (empty($qty)) $qty=0;
2911  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
2912  if (empty($special_code) || $special_code == 3) $special_code=0;
2913  if (! isset($situation_percent) || $situation_percent > 100 || (string) $situation_percent == '') $situation_percent = 100;
2914 
2915  $remise_percent = price2num($remise_percent);
2916  $qty = price2num($qty);
2917  $pu = price2num($pu);
2918  $pu_ht_devise = price2num($pu_ht_devise);
2919  $pa_ht = price2num($pa_ht);
2920  $txtva = price2num($txtva);
2921  $txlocaltax1 = price2num($txlocaltax1);
2922  $txlocaltax2 = price2num($txlocaltax2);
2923 
2924  // Check parameters
2925  if ($type < 0) return -1;
2926 
2927  // Calculate total with, without tax and tax from qty, pu, remise_percent and txtva
2928  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2929  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2930 
2931  $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty, $mysoc);
2932 
2933  // Clean vat code
2934  $vat_src_code='';
2935  if (preg_match('/\((.*)\)/', $txtva, $reg))
2936  {
2937  $vat_src_code = $reg[1];
2938  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2939  }
2940 
2941  $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, $situation_percent, $this->multicurrency_tx, $pu_ht_devise);
2942 
2943  $total_ht = $tabprice[0];
2944  $total_tva = $tabprice[1];
2945  $total_ttc = $tabprice[2];
2946  $total_localtax1=$tabprice[9];
2947  $total_localtax2=$tabprice[10];
2948  $pu_ht = $tabprice[3];
2949  $pu_tva = $tabprice[4];
2950  $pu_ttc = $tabprice[5];
2951 
2952  // MultiCurrency
2953  $multicurrency_total_ht = $tabprice[16];
2954  $multicurrency_total_tva = $tabprice[17];
2955  $multicurrency_total_ttc = $tabprice[18];
2956  $pu_ht_devise = $tabprice[19];
2957 
2958  // Old properties: $price, $remise (deprecated)
2959  $price = $pu;
2960  $remise = 0;
2961  if ($remise_percent > 0)
2962  {
2963  $remise = round(($pu * $remise_percent / 100),2);
2964  $price = ($pu - $remise);
2965  }
2966  $price = price2num($price);
2967 
2968  //Fetch current line from the database and then clone the object and set it in $oldline property
2969  $line = new FactureLigne($this->db);
2970  $line->fetch($rowid);
2971 
2972  if (!empty($line->fk_product))
2973  {
2974  $product=new Product($this->db);
2975  $result=$product->fetch($line->fk_product);
2976  $product_type=$product->type;
2977 
2978  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE) && $product_type == 0 && $product->stock_reel < $qty) {
2979  $langs->load("errors");
2980  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
2981  $this->db->rollback();
2982  return -3;
2983  }
2984  }
2985 
2986  $staticline = clone $line;
2987 
2988  $line->oldline = $staticline;
2989  $this->line = $line;
2990  $this->line->context = $this->context;
2991 
2992  // Reorder if fk_parent_line change
2993  if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
2994  {
2995  $rangmax = $this->line_max($fk_parent_line);
2996  $this->line->rang = $rangmax + 1;
2997  }
2998 
2999  $this->line->rowid = $rowid;
3000  $this->line->label = $label;
3001  $this->line->desc = $desc;
3002  $this->line->qty = ($this->type==self::TYPE_CREDIT_NOTE?abs($qty):$qty); // For credit note, quantity is always positive and unit price negative
3003 
3004  $this->line->vat_src_code = $vat_src_code;
3005  $this->line->tva_tx = $txtva;
3006  $this->line->localtax1_tx = $txlocaltax1;
3007  $this->line->localtax2_tx = $txlocaltax2;
3008  $this->line->localtax1_type = $localtaxes_type[0];
3009  $this->line->localtax2_type = $localtaxes_type[2];
3010 
3011  $this->line->remise_percent = $remise_percent;
3012  $this->line->subprice = ($this->type==2?-abs($pu_ht):$pu_ht); // For credit note, unit price always negative, always positive otherwise
3013  $this->line->date_start = $date_start;
3014  $this->line->date_end = $date_end;
3015  $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
3016  $this->line->total_tva = (($this->type==self::TYPE_CREDIT_NOTE||$qty<0)?-abs($total_tva):$total_tva);
3017  $this->line->total_localtax1 = $total_localtax1;
3018  $this->line->total_localtax2 = $total_localtax2;
3019  $this->line->total_ttc = (($this->type==self::TYPE_CREDIT_NOTE||$qty<0)?-abs($total_ttc):$total_ttc);
3020  $this->line->info_bits = $info_bits;
3021  $this->line->special_code = $special_code;
3022  $this->line->product_type = $type;
3023  $this->line->fk_parent_line = $fk_parent_line;
3024  $this->line->skip_update_total = $skip_update_total;
3025  $this->line->situation_percent = $situation_percent;
3026  $this->line->fk_unit = $fk_unit;
3027 
3028  $this->line->fk_fournprice = $fk_fournprice;
3029  $this->line->pa_ht = $pa_ht;
3030 
3031  // Multicurrency
3032  $this->line->multicurrency_subprice = $pu_ht_devise;
3033  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3034  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3035  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3036 
3037  if (is_array($array_options) && count($array_options)>0) {
3038  $this->line->array_options=$array_options;
3039  }
3040 
3041  $result=$this->line->update($user, $notrigger);
3042  if ($result > 0)
3043  {
3044  // Reorder if child line
3045  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
3046 
3047  // Mise a jour info denormalisees au niveau facture
3048  $this->update_price(1);
3049  $this->db->commit();
3050  return $result;
3051  }
3052  else
3053  {
3054  $this->error=$this->line->error;
3055  $this->db->rollback();
3056  return -1;
3057  }
3058  }
3059  else
3060  {
3061  $this->error="Invoice statut makes operation forbidden";
3062  return -2;
3063  }
3064  }
3065 
3073  function checkProgressLine($idline, $situation_percent)
3074  {
3075  $sql = 'SELECT fd.situation_percent FROM '.MAIN_DB_PREFIX.'facturedet fd
3076  INNER JOIN '.MAIN_DB_PREFIX.'facture f ON (fd.fk_facture = f.rowid)
3077  WHERE fd.fk_prev_id = '.$idline.'
3078  AND f.fk_statut <> 0';
3079 
3080  $result = $this->db->query($sql);
3081  if (! $result)
3082  {
3083  $this->error=$this->db->error();
3084  return false;
3085  }
3086 
3087  $obj = $this->db->fetch_object($result);
3088 
3089  if ($obj === null) return true;
3090  else return $situation_percent < $obj->situation_percent;
3091  }
3092 
3093  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3101  function update_percent($line, $percent)
3102  {
3103  // phpcs:enable
3104  global $mysoc,$user;
3105 
3106  include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
3107 
3108  // Cap percentages to 100
3109  if ($percent > 100) $percent = 100;
3110  $line->situation_percent = $percent;
3111  $tabprice = calcul_price_total($line->qty, $line->subprice, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 0, 'HT', 0, $line->product_type, $mysoc, '', $percent);
3112  $line->total_ht = $tabprice[0];
3113  $line->total_tva = $tabprice[1];
3114  $line->total_ttc = $tabprice[2];
3115  $line->total_localtax1 = $tabprice[9];
3116  $line->total_localtax2 = $tabprice[10];
3117  $line->multicurrency_total_ht = $tabprice[16];
3118  $line->multicurrency_total_tva = $tabprice[17];
3119  $line->multicurrency_total_ttc = $tabprice[18];
3120  $line->update($user);
3121  $this->update_price(1);
3122  $this->db->commit();
3123  }
3124 
3131  function deleteline($rowid)
3132  {
3133  global $user;
3134 
3135  dol_syslog(get_class($this)."::deleteline rowid=".$rowid, LOG_DEBUG);
3136 
3137  if (! $this->brouillon)
3138  {
3139  $this->error='ErrorDeleteLineNotAllowedByObjectStatus';
3140  return -1;
3141  }
3142 
3143  $this->db->begin();
3144 
3145  // Libere remise liee a ligne de facture
3146  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
3147  $sql.= ' SET fk_facture_line = NULL';
3148  $sql.= ' WHERE fk_facture_line = '.$rowid;
3149 
3150  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
3151  $result = $this->db->query($sql);
3152  if (! $result)
3153  {
3154  $this->error=$this->db->error();
3155  $this->db->rollback();
3156  return -1;
3157  }
3158 
3159  $line=new FactureLigne($this->db);
3160 
3161  $line->context = $this->context;
3162 
3163  // For triggers
3164  $result = $line->fetch($rowid);
3165  if (! ($result > 0)) dol_print_error($this->db, $line->error, $line->errors);
3166 
3167  if ($line->delete($user) > 0)
3168  {
3169  $result=$this->update_price(1);
3170 
3171  if ($result > 0)
3172  {
3173  $this->db->commit();
3174  return 1;
3175  }
3176  else
3177  {
3178  $this->db->rollback();
3179  $this->error=$this->db->lasterror();
3180  return -1;
3181  }
3182  }
3183  else
3184  {
3185  $this->db->rollback();
3186  $this->error=$line->error;
3187  return -1;
3188  }
3189  }
3190 
3191  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3200  function set_remise($user, $remise, $notrigger=0)
3201  {
3202  // phpcs:enable
3203  // Clean parameters
3204  if (empty($remise)) $remise=0;
3205 
3206  if ($user->rights->facture->creer)
3207  {
3208  $remise=price2num($remise);
3209 
3210  $error=0;
3211 
3212  $this->db->begin();
3213 
3214  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3215  $sql.= ' SET remise_percent = '.$remise;
3216  $sql.= ' WHERE rowid = '.$this->id;
3217  $sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
3218 
3219  dol_syslog(__METHOD__, LOG_DEBUG);
3220  $resql=$this->db->query($sql);
3221  if (!$resql)
3222  {
3223  $this->errors[]=$this->db->error();
3224  $error++;
3225  }
3226 
3227  if (! $notrigger && empty($error))
3228  {
3229  // Call trigger
3230  $result=$this->call_trigger('BILL_MODIFY',$user);
3231  if ($result < 0) $error++;
3232  // End call triggers
3233  }
3234 
3235  if (! $error)
3236  {
3237  $this->remise_percent = $remise;
3238  $this->update_price(1);
3239 
3240  $this->db->commit();
3241  return 1;
3242  }
3243  else
3244  {
3245  foreach($this->errors as $errmsg)
3246  {
3247  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3248  $this->error.=($this->error?', '.$errmsg:$errmsg);
3249  }
3250  $this->db->rollback();
3251  return -1*$error;
3252  }
3253  }
3254  }
3255 
3256 
3257  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3266  function set_remise_absolue($user, $remise, $notrigger=0)
3267  {
3268  // phpcs:enable
3269  if (empty($remise)) $remise=0;
3270 
3271  if ($user->rights->facture->creer)
3272  {
3273  $error=0;
3274 
3275  $this->db->begin();
3276 
3277  $remise=price2num($remise);
3278 
3279  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3280  $sql.= ' SET remise_absolue = '.$remise;
3281  $sql.= ' WHERE rowid = '.$this->id;
3282  $sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
3283 
3284  dol_syslog(__METHOD__, LOG_DEBUG);
3285  $resql=$this->db->query($sql);
3286  if (!$resql)
3287  {
3288  $this->errors[]=$this->db->error();
3289  $error++;
3290  }
3291 
3292  if (! $error)
3293  {
3294  $this->oldcopy= clone $this;
3295  $this->remise_absolue = $remise;
3296  $this->update_price(1);
3297  }
3298 
3299  if (! $notrigger && empty($error))
3300  {
3301  // Call trigger
3302  $result=$this->call_trigger('BILL_MODIFY',$user);
3303  if ($result < 0) $error++;
3304  // End call triggers
3305  }
3306 
3307  if (! $error)
3308  {
3309  $this->db->commit();
3310  return 1;
3311  }
3312  else
3313  {
3314  foreach($this->errors as $errmsg)
3315  {
3316  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3317  $this->error.=($this->error?', '.$errmsg:$errmsg);
3318  }
3319  $this->db->rollback();
3320  return -1*$error;
3321  }
3322  }
3323  }
3324 
3333  function getNextNumRef($soc,$mode='next')
3334  {
3335  global $conf, $langs;
3336  $langs->load("bills");
3337 
3338  // Clean parameters (if not defined or using deprecated value)
3339  if (empty($conf->global->FACTURE_ADDON)) $conf->global->FACTURE_ADDON='mod_facture_terre';
3340  else if ($conf->global->FACTURE_ADDON=='terre') $conf->global->FACTURE_ADDON='mod_facture_terre';
3341  else if ($conf->global->FACTURE_ADDON=='mercure') $conf->global->FACTURE_ADDON='mod_facture_mercure';
3342 
3343  if (! empty($conf->global->FACTURE_ADDON))
3344  {
3345  dol_syslog("Call getNextNumRef with FACTURE_ADDON = ".$conf->global->FACTURE_ADDON.", thirdparty=".$soc->nom.", type=".$soc->typent_code, LOG_DEBUG);
3346 
3347  $mybool=false;
3348 
3349  $file = $conf->global->FACTURE_ADDON.".php";
3350  $classname = $conf->global->FACTURE_ADDON;
3351 
3352  // Include file with class
3353  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3354 
3355  foreach ($dirmodels as $reldir) {
3356 
3357  $dir = dol_buildpath($reldir."core/modules/facture/");
3358 
3359  // Load file with numbering class (if found)
3360  if (is_file($dir.$file) && is_readable($dir.$file))
3361  {
3362  $mybool |= include_once $dir . $file;
3363  }
3364  }
3365 
3366  // For compatibility
3367  if (! $mybool)
3368  {
3369  $file = $conf->global->FACTURE_ADDON."/".$conf->global->FACTURE_ADDON.".modules.php";
3370  $classname = "mod_facture_".$conf->global->FACTURE_ADDON;
3371  $classname = preg_replace('/\-.*$/','',$classname);
3372  // Include file with class
3373  foreach ($conf->file->dol_document_root as $dirroot)
3374  {
3375  $dir = $dirroot."/core/modules/facture/";
3376 
3377  // Load file with numbering class (if found)
3378  if (is_file($dir.$file) && is_readable($dir.$file)) {
3379  $mybool |= include_once $dir . $file;
3380  }
3381  }
3382  }
3383 
3384  if (! $mybool)
3385  {
3386  dol_print_error('',"Failed to include file ".$file);
3387  return '';
3388  }
3389 
3390  $obj = new $classname();
3391  $numref = "";
3392  $numref = $obj->getNextValue($soc,$this,$mode);
3393 
3398  if ($mode != 'last' && !$numref) {
3399  $this->error=$obj->error;
3400  //dol_print_error($this->db,"Facture::getNextNumRef ".$obj->error);
3401  return "";
3402  }
3403 
3404  return $numref;
3405  }
3406  else
3407  {
3408  $langs->load("errors");
3409  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3410  return "";
3411  }
3412  }
3413 
3420  function info($id)
3421  {
3422  $sql = 'SELECT c.rowid, datec, date_valid as datev, tms as datem,';
3423  $sql.= ' fk_user_author, fk_user_valid';
3424  $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as c';
3425  $sql.= ' WHERE c.rowid = '.$id;
3426 
3427  $result=$this->db->query($sql);
3428  if ($result)
3429  {
3430  if ($this->db->num_rows($result))
3431  {
3432  $obj = $this->db->fetch_object($result);
3433  $this->id = $obj->rowid;
3434  if ($obj->fk_user_author)
3435  {
3436  $cuser = new User($this->db);
3437  $cuser->fetch($obj->fk_user_author);
3438  $this->user_creation = $cuser;
3439  }
3440  if ($obj->fk_user_valid)
3441  {
3442  $vuser = new User($this->db);
3443  $vuser->fetch($obj->fk_user_valid);
3444  $this->user_validation = $vuser;
3445  }
3446  $this->date_creation = $this->db->jdate($obj->datec);
3447  $this->date_modification = $this->db->jdate($obj->datem);
3448  $this->date_validation = $this->db->jdate($obj->datev); // Should be in log table
3449  }
3450  $this->db->free($result);
3451  }
3452  else
3453  {
3454  dol_print_error($this->db);
3455  }
3456  }
3457 
3458 
3459  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3473  function liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='f.datef,f.rowid', $sortorder='DESC')
3474  {
3475  // phpcs:enable
3476  global $conf,$user;
3477 
3478  $ga = array();
3479 
3480  $sql = "SELECT s.rowid, s.nom as name, s.client,";
3481  $sql.= " f.rowid as fid, f.facnumber as ref, f.datef as df";
3482  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
3483  $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture as f";
3484  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
3485  $sql.= " WHERE f.entity = ".$conf->entity;
3486  $sql.= " AND f.fk_soc = s.rowid";
3487  if (! $user->rights->societe->client->voir && ! $socid) //restriction
3488  {
3489  $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
3490  }
3491  if ($socid) $sql.= " AND s.rowid = ".$socid;
3492  if ($draft) $sql.= " AND f.fk_statut = ".self::STATUS_DRAFT;
3493  if (is_object($excluser)) $sql.= " AND f.fk_user_author <> ".$excluser->id;
3494  $sql.= $this->db->order($sortfield,$sortorder);
3495  $sql.= $this->db->plimit($limit,$offset);
3496 
3497  $result=$this->db->query($sql);
3498  if ($result)
3499  {
3500  $numc = $this->db->num_rows($result);
3501  if ($numc)
3502  {
3503  $i = 0;
3504  while ($i < $numc)
3505  {
3506  $obj = $this->db->fetch_object($result);
3507 
3508  if ($shortlist == 1)
3509  {
3510  $ga[$obj->fid] = $obj->ref;
3511  }
3512  else if ($shortlist == 2)
3513  {
3514  $ga[$obj->fid] = $obj->ref.' ('.$obj->name.')';
3515  }
3516  else
3517  {
3518  $ga[$i]['id'] = $obj->fid;
3519  $ga[$i]['ref'] = $obj->ref;
3520  $ga[$i]['name'] = $obj->name;
3521  }
3522  $i++;
3523  }
3524  }
3525  return $ga;
3526  }
3527  else
3528  {
3529  dol_print_error($this->db);
3530  return -1;
3531  }
3532  }
3533 
3534 
3535  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3544  function list_replacable_invoices($socid=0)
3545  {
3546  // phpcs:enable
3547  global $conf;
3548 
3549  $return = array();
3550 
3551  $sql = "SELECT f.rowid as rowid, f.facnumber, f.fk_statut,";
3552  $sql.= " ff.rowid as rowidnext";
3553  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3554  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture";
3555  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as ff ON f.rowid = ff.fk_facture_source";
3556  $sql.= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
3557  $sql.= " AND f.entity = ".$conf->entity;
3558  $sql.= " AND f.paye = 0"; // Pas classee payee completement
3559  $sql.= " AND pf.fk_paiement IS NULL"; // Aucun paiement deja fait
3560  $sql.= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
3561  if ($socid > 0) $sql.=" AND f.fk_soc = ".$socid;
3562  $sql.= " ORDER BY f.facnumber";
3563 
3564  dol_syslog(get_class($this)."::list_replacable_invoices", LOG_DEBUG);
3565  $resql=$this->db->query($sql);
3566  if ($resql)
3567  {
3568  while ($obj=$this->db->fetch_object($resql))
3569  {
3570  $return[$obj->rowid]=array( 'id' => $obj->rowid,
3571  'ref' => $obj->facnumber,
3572  'status' => $obj->fk_statut);
3573  }
3574  //print_r($return);
3575  return $return;
3576  }
3577  else
3578  {
3579  $this->error=$this->db->error();
3580  return -1;
3581  }
3582  }
3583 
3584 
3585  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3595  {
3596  // phpcs:enable
3597  global $conf;
3598 
3599  $return = array();
3600 
3601 
3602  $sql = "SELECT f.rowid as rowid, f.facnumber, f.fk_statut, f.type, f.paye, pf.fk_paiement";
3603  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3604  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture";
3605  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as ff ON (f.rowid = ff.fk_facture_source AND ff.type=".self::TYPE_REPLACEMENT.")";
3606  $sql.= " WHERE f.entity = ".$conf->entity;
3607  $sql.= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
3608  // $sql.= " WHERE f.fk_statut >= 1";
3609  // $sql.= " AND (f.paye = 1"; // Classee payee completement
3610  // $sql.= " OR f.close_code IS NOT NULL)"; // Classee payee partiellement
3611  $sql.= " AND ff.type IS NULL"; // Renvoi vrai si pas facture de remplacement
3612  $sql.= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
3613 
3614  if($conf->global->INVOICE_USE_SITUATION_CREDIT_NOTE){
3615  // Select the last situation invoice
3616  $sqlSit = 'SELECT MAX(fs.rowid)';
3617  $sqlSit.= " FROM ".MAIN_DB_PREFIX."facture as fs";
3618  $sqlSit.= " WHERE fs.entity = ".$conf->entity;
3619  $sqlSit.= " AND fs.type = ".self::TYPE_SITUATION;
3620  $sqlSit.= " AND fs.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
3621  $sqlSit.= " GROUP BY fs.situation_cycle_ref";
3622  $sqlSit.= " ORDER BY fs.situation_counter";
3623  $sql.= " AND ( f.type != ".self::TYPE_SITUATION . " OR f.rowid IN (".$sqlSit.") )"; // Type non 5 si facture non avoir
3624  }
3625  else
3626  {
3627  $sql.= " AND f.type != ".self::TYPE_SITUATION ; // Type non 5 si facture non avoir
3628  }
3629 
3630  if ($socid > 0) $sql.=" AND f.fk_soc = ".$socid;
3631  $sql.= " ORDER BY f.facnumber";
3632 
3633  dol_syslog(get_class($this)."::list_qualified_avoir_invoices", LOG_DEBUG);
3634  $resql=$this->db->query($sql);
3635  if ($resql)
3636  {
3637  while ($obj=$this->db->fetch_object($resql))
3638  {
3639  $qualified=0;
3640  if ($obj->fk_statut == self::STATUS_VALIDATED) $qualified=1;
3641  if ($obj->fk_statut == self::STATUS_CLOSED) $qualified=1;
3642  if ($qualified)
3643  {
3644  //$ref=$obj->facnumber;
3645  $paymentornot=($obj->fk_paiement?1:0);
3646  $return[$obj->rowid]=array('ref'=>$obj->facnumber,'status'=>$obj->fk_statut,'type'=>$obj->type,'paye'=>$obj->paye,'paymentornot'=>$paymentornot);
3647  }
3648  }
3649 
3650  return $return;
3651  }
3652  else
3653  {
3654  $this->error=$this->db->error();
3655  return -1;
3656  }
3657  }
3658 
3659 
3660  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3669  function demande_prelevement($fuser, $amount=0)
3670  {
3671  // phpcs:enable
3672 
3673  $error=0;
3674 
3675  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3676 
3677  if ($this->statut > self::STATUS_DRAFT && $this->paye == 0)
3678  {
3679  require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
3680  $bac = new CompanyBankAccount($this->db);
3681  $bac->fetch(0,$this->socid);
3682 
3683  $sql = 'SELECT count(*)';
3684  $sql.= ' FROM '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3685  $sql.= ' WHERE fk_facture = '.$this->id;
3686  $sql.= ' AND traite = 0';
3687 
3688  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3689  $resql=$this->db->query($sql);
3690  if ($resql)
3691  {
3692  $row = $this->db->fetch_row($resql);
3693  if ($row[0] == 0)
3694  {
3695  $now=dol_now();
3696 
3697  $totalpaye = $this->getSommePaiement();
3698  $totalcreditnotes = $this->getSumCreditNotesUsed();
3699  $totaldeposits = $this->getSumDepositsUsed();
3700  //print "totalpaye=".$totalpaye." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits;
3701 
3702  // We can also use bcadd to avoid pb with floating points
3703  // For example print 239.2 - 229.3 - 9.9; does not return 0.
3704  //$resteapayer=bcadd($this->total_ttc,$totalpaye,$conf->global->MAIN_MAX_DECIMALS_TOT);
3705  //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
3706  if (empty($amount)) $amount = price2num($this->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits,'MT');
3707 
3708  if (is_numeric($amount) && $amount != 0)
3709  {
3710  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3711  $sql .= ' (fk_facture, amount, date_demande, fk_user_demande, code_banque, code_guichet, number, cle_rib)';
3712  $sql .= ' VALUES ('.$this->id;
3713  $sql .= ",'".price2num($amount)."'";
3714  $sql .= ",'".$this->db->idate($now)."'";
3715  $sql .= ",".$fuser->id;
3716  $sql .= ",'".$bac->code_banque."'";
3717  $sql .= ",'".$bac->code_guichet."'";
3718  $sql .= ",'".$bac->number."'";
3719  $sql .= ",'".$bac->cle_rib."')";
3720 
3721  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3722  $resql=$this->db->query($sql);
3723  if (! $resql)
3724  {
3725  $this->error=$this->db->lasterror();
3726  dol_syslog(get_class($this).'::demandeprelevement Erreur');
3727  $error++;
3728  }
3729  }
3730  else
3731  {
3732  $this->error='WithdrawRequestErrorNilAmount';
3733  dol_syslog(get_class($this).'::demandeprelevement WithdrawRequestErrorNilAmount');
3734  $error++;
3735  }
3736 
3737  if (! $error)
3738  {
3739  // Force payment mode of invoice to withdraw
3740  $payment_mode_id = dol_getIdFromCode($this->db, 'PRE', 'c_paiement', 'code', 'id', 1);
3741  if ($payment_mode_id > 0)
3742  {
3743  $result=$this->setPaymentMethods($payment_mode_id);
3744  }
3745  }
3746 
3747  if ($error) return -1;
3748  return 1;
3749  }
3750  else
3751  {
3752  $this->error="A request already exists";
3753  dol_syslog(get_class($this).'::demandeprelevement Impossible de creer une demande, demande deja en cours');
3754  return 0;
3755  }
3756  }
3757  else
3758  {
3759  $this->error=$this->db->error();
3760  dol_syslog(get_class($this).'::demandeprelevement Erreur -2');
3761  return -2;
3762  }
3763  }
3764  else
3765  {
3766  $this->error="Status of invoice does not allow this";
3767  dol_syslog(get_class($this)."::demandeprelevement ".$this->error." $this->statut, $this->paye, $this->mode_reglement_id");
3768  return -3;
3769  }
3770  }
3771 
3772  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3780  function demande_prelevement_delete($fuser, $did)
3781  {
3782  // phpcs:enable
3783  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3784  $sql .= ' WHERE rowid = '.$did;
3785  $sql .= ' AND traite = 0';
3786  if ( $this->db->query($sql) )
3787  {
3788  return 0;
3789  }
3790  else
3791  {
3792  $this->error=$this->db->lasterror();
3793  dol_syslog(get_class($this).'::demande_prelevement_delete Error '.$this->error);
3794  return -1;
3795  }
3796  }
3797 
3798 
3799  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3806  function load_board($user)
3807  {
3808  // phpcs:enable
3809  global $conf, $langs;
3810 
3811  $clause = " WHERE";
3812 
3813  $sql = "SELECT f.rowid, f.date_lim_reglement as datefin,f.fk_statut, f.total";
3814  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3815  if (!$user->rights->societe->client->voir && !$user->societe_id)
3816  {
3817  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON f.fk_soc = sc.fk_soc";
3818  $sql.= " WHERE sc.fk_user = " .$user->id;
3819  $clause = " AND";
3820  }
3821  $sql.= $clause." f.paye=0";
3822  $sql.= " AND f.entity = ".$conf->entity;
3823  $sql.= " AND f.fk_statut = ".self::STATUS_VALIDATED;
3824  if ($user->societe_id) $sql.= " AND f.fk_soc = ".$user->societe_id;
3825 
3826  $resql=$this->db->query($sql);
3827  if ($resql)
3828  {
3829  $langs->load("bills");
3830  $now=dol_now();
3831 
3832  $response = new WorkboardResponse();
3833  $response->warning_delay=$conf->facture->client->warning_delay/60/60/24;
3834  $response->label=$langs->trans("CustomerBillsUnpaid");
3835  $response->url=DOL_URL_ROOT.'/compta/facture/list.php?search_status=1&mainmenu=billing&leftmenu=customers_bills';
3836  $response->img=img_object('',"bill");
3837 
3838  $generic_facture = new Facture($this->db);
3839 
3840  while ($obj=$this->db->fetch_object($resql))
3841  {
3842  $generic_facture->date_lim_reglement = $this->db->jdate($obj->datefin);
3843  $generic_facture->statut = $obj->fk_statut;
3844 
3845  $response->nbtodo++;
3846  $response->total += $obj->total;
3847 
3848  if ($generic_facture->hasDelay()) {
3849  $response->nbtodolate++;
3850  }
3851  }
3852 
3853  return $response;
3854  }
3855  else
3856  {
3857  dol_print_error($this->db);
3858  $this->error=$this->db->error();
3859  return -1;
3860  }
3861  }
3862 
3863 
3864  /* gestion des contacts d'une facture */
3865 
3872  {
3873  return $this->getIdContact('external','BILLING');
3874  }
3875 
3882  {
3883  return $this->getIdContact('external','SHIPPING');
3884  }
3885 
3886 
3895  function initAsSpecimen($option='')
3896  {
3897  global $langs;
3898 
3899  $now=dol_now();
3900  $arraynow=dol_getdate($now);
3901  $nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
3902 
3903  // Load array of products prodids
3904  $num_prods = 0;
3905  $prodids = array();
3906  $sql = "SELECT rowid";
3907  $sql.= " FROM ".MAIN_DB_PREFIX."product";
3908  $sql.= " WHERE entity IN (".getEntity('product').")";
3909  $resql = $this->db->query($sql);
3910  if ($resql)
3911  {
3912  $num_prods = $this->db->num_rows($resql);
3913  $i = 0;
3914  while ($i < $num_prods)
3915  {
3916  $i++;
3917  $row = $this->db->fetch_row($resql);
3918  $prodids[$i] = $row[0];
3919  }
3920  }
3921  //Avoid php warning Warning: mt_rand(): max(0) is smaller than min(1) when no product exists
3922  if (empty($num_prods)) {
3923  $num_prods=1;
3924  }
3925 
3926  // Initialize parameters
3927  $this->id=0;
3928  $this->entity = 1;
3929  $this->ref = 'SPECIMEN';
3930  $this->specimen=1;
3931  $this->socid = 1;
3932  $this->date = $nownotime;
3933  $this->date_lim_reglement = $nownotime + 3600 * 24 *30;
3934  $this->cond_reglement_id = 1;
3935  $this->cond_reglement_code = 'RECEP';
3936  $this->date_lim_reglement=$this->calculate_date_lim_reglement();
3937  $this->mode_reglement_id = 0; // Not forced to show payment mode CHQ + VIR
3938  $this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR
3939  $this->note_public='This is a comment (public)';
3940  $this->note_private='This is a comment (private)';
3941  $this->note='This is a comment (private)';
3942  $this->fk_incoterms=0;
3943  $this->location_incoterms='';
3944 
3945  if (empty($option) || $option != 'nolines')
3946  {
3947  // Lines
3948  $nbp = 5;
3949  $xnbp = 0;
3950  while ($xnbp < $nbp)
3951  {
3952  $line=new FactureLigne($this->db);
3953  $line->desc=$langs->trans("Description")." ".$xnbp;
3954  $line->qty=1;
3955  $line->subprice=100;
3956  $line->tva_tx=19.6;
3957  $line->localtax1_tx=0;
3958  $line->localtax2_tx=0;
3959  $line->remise_percent=0;
3960  if ($xnbp == 1) // Qty is negative (product line)
3961  {
3962  $prodid = mt_rand(1, $num_prods);
3963  $line->fk_product=$prodids[$prodid];
3964  $line->qty=-1;
3965  $line->total_ht=-100;
3966  $line->total_ttc=-119.6;
3967  $line->total_tva=-19.6;
3968  $line->multicurrency_total_ht=-200;
3969  $line->multicurrency_total_ttc=-239.2;
3970  $line->multicurrency_total_tva=-39.2;
3971  }
3972  else if ($xnbp == 2) // UP is negative (free line)
3973  {
3974  $line->subprice=-100;
3975  $line->total_ht=-100;
3976  $line->total_ttc=-119.6;
3977  $line->total_tva=-19.6;
3978  $line->remise_percent=0;
3979  $line->multicurrency_total_ht=-200;
3980  $line->multicurrency_total_ttc=-239.2;
3981  $line->multicurrency_total_tva=-39.2;
3982  }
3983  else if ($xnbp == 3) // Discount is 50% (product line)
3984  {
3985  $prodid = mt_rand(1, $num_prods);
3986  $line->fk_product=$prodids[$prodid];
3987  $line->total_ht=50;
3988  $line->total_ttc=59.8;
3989  $line->total_tva=9.8;
3990  $line->multicurrency_total_ht=100;
3991  $line->multicurrency_total_ttc=119.6;
3992  $line->multicurrency_total_tva=19.6;
3993  $line->remise_percent=50;
3994  }
3995  else // (product line)
3996  {
3997  $prodid = mt_rand(1, $num_prods);
3998  $line->fk_product=$prodids[$prodid];
3999  $line->total_ht=100;
4000  $line->total_ttc=119.6;
4001  $line->total_tva=19.6;
4002  $line->multicurrency_total_ht=200;
4003  $line->multicurrency_total_ttc=239.2;
4004  $line->multicurrency_total_tva=39.2;
4005  $line->remise_percent=0;
4006  }
4007 
4008  $this->lines[$xnbp]=$line;
4009 
4010 
4011  $this->total_ht += $line->total_ht;
4012  $this->total_tva += $line->total_tva;
4013  $this->total_ttc += $line->total_ttc;
4014 
4015  $this->multicurrency_total_ht += $line->multicurrency_total_ht;
4016  $this->multicurrency_total_tva += $line->multicurrency_total_tva;
4017  $this->multicurrency_total_ttc += $line->multicurrency_total_ttc;
4018 
4019  $xnbp++;
4020  }
4021  $this->revenuestamp = 0;
4022 
4023  // Add a line "offered"
4024  $line=new FactureLigne($this->db);
4025  $line->desc=$langs->trans("Description")." (offered line)";
4026  $line->qty=1;
4027  $line->subprice=100;
4028  $line->tva_tx=19.6;
4029  $line->localtax1_tx=0;
4030  $line->localtax2_tx=0;
4031  $line->remise_percent=100;
4032  $line->total_ht=0;
4033  $line->total_ttc=0; // 90 * 1.196
4034  $line->total_tva=0;
4035  $line->multicurrency_total_ht=0;
4036  $line->multicurrency_total_ttc=0;
4037  $line->multicurrency_total_tva=0;
4038  $prodid = mt_rand(1, $num_prods);
4039  $line->fk_product=$prodids[$prodid];
4040 
4041  $this->lines[$xnbp]=$line;
4042  $xnbp++;
4043  }
4044  }
4045 
4046  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4052  function load_state_board()
4053  {
4054  // phpcs:enable
4055  global $conf, $user;
4056 
4057  $this->nb=array();
4058 
4059  $clause = "WHERE";
4060 
4061  $sql = "SELECT count(f.rowid) as nb";
4062  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
4063  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
4064  if (!$user->rights->societe->client->voir && !$user->societe_id)
4065  {
4066  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4067  $sql.= " WHERE sc.fk_user = " .$user->id;
4068  $clause = "AND";
4069  }
4070  $sql.= " ".$clause." f.entity = ".$conf->entity;
4071 
4072  $resql=$this->db->query($sql);
4073  if ($resql)
4074  {
4075  while ($obj=$this->db->fetch_object($resql))
4076  {
4077  $this->nb["invoices"]=$obj->nb;
4078  }
4079  $this->db->free($resql);
4080  return 1;
4081  }
4082  else
4083  {
4084  dol_print_error($this->db);
4085  $this->error=$this->db->error();
4086  return -1;
4087  }
4088  }
4089 
4095  function getLinesArray()
4096  {
4097  return $this->fetch_lines();
4098  }
4099 
4111  public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
4112  {
4113  global $conf,$langs;
4114 
4115  $langs->load("bills");
4116 
4117  if (! dol_strlen($modele))
4118  {
4119  $modele = 'crabe';
4120  $thisTypeConfName = 'FACTURE_ADDON_PDF_'.$this->type;
4121 
4122  if ($this->modelpdf) {
4123  $modele = $this->modelpdf;
4124  } elseif (! empty($conf->global->$thisTypeConfName)) {
4125  $modele = $conf->global->$thisTypeConfName;
4126  } elseif (! empty($conf->global->FACTURE_ADDON_PDF)) {
4127  $modele = $conf->global->FACTURE_ADDON_PDF;
4128  }
4129  }
4130 
4131  $modelpath = "core/modules/facture/doc/";
4132 
4133  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4134  }
4135 
4141  function newCycle()
4142  {
4143  $sql = 'SELECT max(situation_cycle_ref) FROM ' . MAIN_DB_PREFIX . 'facture as f';
4144  $sql.= " WHERE f.entity in (".getEntity('facture', 0).")";
4145  $resql = $this->db->query($sql);
4146  if ($resql) {
4147  if ($resql->num_rows > 0)
4148  {
4149  $res = $this->db->fetch_array($resql);
4150  $ref = $res['max(situation_cycle_ref)'];
4151  $ref++;
4152  } else {
4153  $ref = 1;
4154  }
4155  $this->db->free($resql);
4156  return $ref;
4157  } else {
4158  $this->error = $this->db->lasterror();
4159  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4160  return -1;
4161  }
4162  }
4163 
4164  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4170  function is_first()
4171  {
4172  // phpcs:enable
4173  return ($this->situation_counter == 1);
4174  }
4175 
4176  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4182  function get_prev_sits()
4183  {
4184  // phpcs:enable
4185  global $conf;
4186 
4187  $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . 'facture';
4188  $sql .= ' where situation_cycle_ref = ' . $this->situation_cycle_ref;
4189  $sql .= ' and situation_counter < ' . $this->situation_counter;
4190  $sql .= ' AND entity = '. ($this->entity > 0 ? $this->entity : $conf->entity);
4191  $resql = $this->db->query($sql);
4192  $res = array();
4193  if ($resql && $resql->num_rows > 0) {
4194  while ($row = $this->db->fetch_object($resql)) {
4195  $id = $row->rowid;
4196  $situation = new Facture($this->db);
4197  $situation->fetch($id);
4198  $res[] = $situation;
4199  }
4200  } else {
4201  $this->error = $this->db->error();
4202  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4203  return -1;
4204  }
4205 
4206  return $res;
4207  }
4208 
4216  function setFinal(User $user, $notrigger=0)
4217  {
4218  $error=0;
4219 
4220  $this->db->begin();
4221 
4222  $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET situation_final = ' . $this->situation_final . ' where rowid = ' . $this->id;
4223 
4224  dol_syslog(__METHOD__, LOG_DEBUG);
4225  $resql=$this->db->query($sql);
4226  if (!$resql)
4227  {
4228  $this->errors[]=$this->db->error();
4229  $error++;
4230  }
4231 
4232  if (! $notrigger && empty($error))
4233  {
4234  // Call trigger
4235  $result=$this->call_trigger('BILL_MODIFY',$user);
4236  if ($result < 0) $error++;
4237  // End call triggers
4238  }
4239 
4240  if (! $error)
4241  {
4242  $this->db->commit();
4243  return 1;
4244  }
4245  else
4246  {
4247  foreach($this->errors as $errmsg)
4248  {
4249  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
4250  $this->error.=($this->error?', '.$errmsg:$errmsg);
4251  }
4252  $this->db->rollback();
4253  return -1*$error;
4254  }
4255  }
4256 
4257  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4264  function is_last_in_cycle()
4265  {
4266  // phpcs:enable
4267  global $conf;
4268 
4269  if (!empty($this->situation_cycle_ref)) {
4270  // No point in testing anything if we're not inside a cycle
4271  $sql = 'SELECT max(situation_counter) FROM ' . MAIN_DB_PREFIX . 'facture WHERE situation_cycle_ref = ' . $this->situation_cycle_ref . ' AND entity = ' . ($this->entity > 0 ? $this->entity : $conf->entity);
4272  $resql = $this->db->query($sql);
4273 
4274  if ($resql && $resql->num_rows > 0) {
4275  $res = $this->db->fetch_array($resql);
4276  $last = $res['max(situation_counter)'];
4277  return ($last == $this->situation_counter);
4278  } else {
4279  $this->error = $this->db->lasterror();
4280  dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR);
4281  return false;
4282  }
4283  } else {
4284  return true;
4285  }
4286  }
4287 
4296  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
4297  {
4298  $tables = array(
4299  'facture'
4300  );
4301 
4302  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
4303  }
4304 
4310  public function hasDelay()
4311  {
4312  global $conf;
4313 
4314  $now = dol_now();
4315 
4316  // Paid invoices have status STATUS_CLOSED
4317  if ($this->statut != Facture::STATUS_VALIDATED) return false;
4318 
4319  return $this->date_lim_reglement < ($now - $conf->facture->client->warning_delay);
4320  }
4321 }
4322 
4328 {
4332  public $element='facturedet';
4333 
4337  public $table_element='facturedet';
4338 
4339  public $oldline;
4340 
4343  public $fk_facture;
4349  public $label;
4351  public $desc;
4352 
4353  public $localtax1_type; // Local tax 1 type
4354  public $localtax2_type; // Local tax 2 type
4355  public $fk_remise_except; // Link to line into llx_remise_except
4356  public $rang = 0;
4357 
4358  public $fk_fournprice;
4359  public $pa_ht;
4360  public $marge_tx;
4361  public $marque_tx;
4362 
4363  public $special_code; // Liste d'options non cumulabels:
4364  // 1: frais de port
4365  // 2: ecotaxe
4366  // 3: ??
4367 
4368  public $origin;
4369  public $origin_id;
4370 
4371  public $fk_code_ventilation = 0;
4372 
4373  public $date_start;
4374  public $date_end;
4375 
4376  // Ne plus utiliser
4377  //var $price; // P.U. HT apres remise % de ligne (exemple 80)
4378  //var $remise; // Montant calcule de la remise % sur PU HT (exemple 20)
4379 
4380  // From llx_product
4385  public $ref; // Product ref (deprecated)
4386  public $product_ref; // Product ref
4391  public $libelle; // Product label (deprecated)
4392  public $product_label; // Product label
4393  public $product_desc; // Description produit
4394 
4395  public $skip_update_total; // Skip update price total for special lines
4396 
4400  public $situation_percent;
4401 
4405  public $fk_prev_id;
4406 
4407  // Multicurrency
4408  public $fk_multicurrency;
4409  public $multicurrency_code;
4410  public $multicurrency_subprice;
4411  public $multicurrency_total_ht;
4412  public $multicurrency_total_tva;
4413  public $multicurrency_total_ttc;
4414 
4421  function fetch($rowid)
4422  {
4423  $sql = 'SELECT fd.rowid, fd.fk_facture, fd.fk_parent_line, fd.fk_product, fd.product_type, fd.label as custom_label, fd.description, fd.price, fd.qty, fd.vat_src_code, fd.tva_tx,';
4424  $sql.= ' fd.localtax1_tx, fd. localtax2_tx, fd.remise, fd.remise_percent, fd.fk_remise_except, fd.subprice,';
4425  $sql.= ' fd.date_start as date_start, fd.date_end as date_end, fd.fk_product_fournisseur_price as fk_fournprice, fd.buy_price_ht as pa_ht,';
4426  $sql.= ' fd.info_bits, fd.special_code, fd.total_ht, fd.total_tva, fd.total_ttc, fd.total_localtax1, fd.total_localtax2, fd.rang,';
4427  $sql.= ' fd.fk_code_ventilation,';
4428  $sql.= ' fd.fk_unit, fd.fk_user_author, fd.fk_user_modif,';
4429  $sql.= ' fd.situation_percent, fd.fk_prev_id,';
4430  $sql.= ' fd.multicurrency_subprice,';
4431  $sql.= ' fd.multicurrency_total_ht,';
4432  $sql.= ' fd.multicurrency_total_tva,';
4433  $sql.= ' fd.multicurrency_total_ttc,';
4434  $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc';
4435  $sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet as fd';
4436  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON fd.fk_product = p.rowid';
4437  $sql.= ' WHERE fd.rowid = '.$rowid;
4438 
4439  $result = $this->db->query($sql);
4440  if ($result)
4441  {
4442  $objp = $this->db->fetch_object($result);
4443 
4444  $this->rowid = $objp->rowid;
4445  $this->id = $objp->rowid;
4446  $this->fk_facture = $objp->fk_facture;
4447  $this->fk_parent_line = $objp->fk_parent_line;
4448  $this->label = $objp->custom_label;
4449  $this->desc = $objp->description;
4450  $this->qty = $objp->qty;
4451  $this->subprice = $objp->subprice;
4452  $this->vat_src_code = $objp->vat_src_code;
4453  $this->tva_tx = $objp->tva_tx;
4454  $this->localtax1_tx = $objp->localtax1_tx;
4455  $this->localtax2_tx = $objp->localtax2_tx;
4456  $this->remise_percent = $objp->remise_percent;
4457  $this->fk_remise_except = $objp->fk_remise_except;
4458  $this->fk_product = $objp->fk_product;
4459  $this->product_type = $objp->product_type;
4460  $this->date_start = $this->db->jdate($objp->date_start);
4461  $this->date_end = $this->db->jdate($objp->date_end);
4462  $this->info_bits = $objp->info_bits;
4463  $this->tva_npr = ($objp->info_bits & 1 == 1) ? 1 : 0;
4464  $this->special_code = $objp->special_code;
4465  $this->total_ht = $objp->total_ht;
4466  $this->total_tva = $objp->total_tva;
4467  $this->total_localtax1 = $objp->total_localtax1;
4468  $this->total_localtax2 = $objp->total_localtax2;
4469  $this->total_ttc = $objp->total_ttc;
4470  $this->fk_code_ventilation = $objp->fk_code_ventilation;
4471  $this->rang = $objp->rang;
4472  $this->fk_fournprice = $objp->fk_fournprice;
4473  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4474  $this->pa_ht = $marginInfos[0];
4475  $this->marge_tx = $marginInfos[1];
4476  $this->marque_tx = $marginInfos[2];
4477 
4478  $this->ref = $objp->product_ref; // deprecated
4479  $this->product_ref = $objp->product_ref;
4480  $this->libelle = $objp->product_libelle; // deprecated
4481  $this->product_label = $objp->product_libelle;
4482  $this->product_desc = $objp->product_desc;
4483  $this->fk_unit = $objp->fk_unit;
4484  $this->fk_user_modif = $objp->fk_user_modif;
4485  $this->fk_user_author = $objp->fk_user_author;
4486 
4487  $this->situation_percent = $objp->situation_percent;
4488  $this->fk_prev_id = $objp->fk_prev_id;
4489 
4490  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4491  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4492  $this->multicurrency_total_tva= $objp->multicurrency_total_tva;
4493  $this->multicurrency_total_ttc= $objp->multicurrency_total_ttc;
4494 
4495  $this->db->free($result);
4496 
4497  return 1;
4498  }
4499  else
4500  {
4501  $this->error = $this->db->lasterror();
4502  return -1;
4503  }
4504  }
4505 
4513  function insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
4514  {
4515  global $langs,$user,$conf;
4516 
4517  $error=0;
4518 
4519  $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4520 
4521  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4522 
4523  // Clean parameters
4524  $this->desc=trim($this->desc);
4525  if (empty($this->tva_tx)) $this->tva_tx=0;
4526  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4527  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4528  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4529  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4530  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4531  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4532  if (empty($this->rang)) $this->rang=0;
4533  if (empty($this->remise_percent)) $this->remise_percent=0;
4534  if (empty($this->info_bits)) $this->info_bits=0;
4535  if (empty($this->subprice)) $this->subprice=0;
4536  if (empty($this->special_code)) $this->special_code=0;
4537  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4538  if (empty($this->fk_prev_id)) $this->fk_prev_id = 0;
4539  if (! isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100;
4540 
4541  if (empty($this->pa_ht)) $this->pa_ht=0;
4542  if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice=0;
4543  if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht=0;
4544  if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
4545  if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
4546 
4547  // if buy price not defined, define buyprice as configured in margin admin
4548  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4549  {
4550  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4551  {
4552  return $result;
4553  }
4554  else
4555  {
4556  $this->pa_ht = $result;
4557  }
4558  }
4559 
4560  // Check parameters
4561  if ($this->product_type < 0)
4562  {
4563  $this->error='ErrorProductTypeMustBe0orMore';
4564  return -1;
4565  }
4566  if (! empty($this->fk_product))
4567  {
4568  // Check product exists
4569  $result=Product::isExistingObject('product', $this->fk_product);
4570  if ($result <= 0)
4571  {
4572  $this->error='ErrorProductIdDoesNotExists';
4573  return -1;
4574  }
4575  }
4576 
4577  $this->db->begin();
4578 
4579  // Insertion dans base de la ligne
4580  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facturedet';
4581  $sql.= ' (fk_facture, fk_parent_line, label, description, qty,';
4582  $sql.= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4583  $sql.= ' fk_product, product_type, remise_percent, subprice, fk_remise_except,';
4584  $sql.= ' date_start, date_end, fk_code_ventilation, ';
4585  $sql.= ' rang, special_code, fk_product_fournisseur_price, buy_price_ht,';
4586  $sql.= ' info_bits, total_ht, total_tva, total_ttc, total_localtax1, total_localtax2,';
4587  $sql.= ' situation_percent, fk_prev_id,';
4588  $sql.= ' fk_unit, fk_user_author, fk_user_modif,';
4589  $sql.= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4590  $sql.= ')';
4591  $sql.= " VALUES (".$this->fk_facture.",";
4592  $sql.= " ".($this->fk_parent_line>0 ? $this->fk_parent_line:"null").",";
4593  $sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4594  $sql.= " '".$this->db->escape($this->desc)."',";
4595  $sql.= " ".price2num($this->qty).",";
4596  $sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4597  $sql.= " ".price2num($this->tva_tx).",";
4598  $sql.= " ".price2num($this->localtax1_tx).",";
4599  $sql.= " ".price2num($this->localtax2_tx).",";
4600  $sql.= " '".$this->db->escape($this->localtax1_type)."',";
4601  $sql.= " '".$this->db->escape($this->localtax2_type)."',";
4602  $sql.= ' '.(! empty($this->fk_product)?$this->fk_product:"null").',';
4603  $sql.= " ".$this->product_type.",";
4604  $sql.= " ".price2num($this->remise_percent).",";
4605  $sql.= " ".price2num($this->subprice).",";
4606  $sql.= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
4607  $sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").",";
4608  $sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").",";
4609  $sql.= ' '.$this->fk_code_ventilation.',';
4610  $sql.= ' '.$this->rang.',';
4611  $sql.= ' '.$this->special_code.',';
4612  $sql.= ' '.(! empty($this->fk_fournprice)?$this->fk_fournprice:"null").',';
4613  $sql.= ' '.price2num($this->pa_ht).',';
4614  $sql.= " '".$this->db->escape($this->info_bits)."',";
4615  $sql.= " ".price2num($this->total_ht).",";
4616  $sql.= " ".price2num($this->total_tva).",";
4617  $sql.= " ".price2num($this->total_ttc).",";
4618  $sql.= " ".price2num($this->total_localtax1).",";
4619  $sql.= " ".price2num($this->total_localtax2);
4620  $sql.= ", " . $this->situation_percent;
4621  $sql.= ", " . (!empty($this->fk_prev_id)?$this->fk_prev_id:"null");
4622  $sql.= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4623  $sql.= ", ".$user->id;
4624  $sql.= ", ".$user->id;
4625  $sql.= ", ".(int) $this->fk_multicurrency;
4626  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4627  $sql.= ", ".price2num($this->multicurrency_subprice);
4628  $sql.= ", ".price2num($this->multicurrency_total_ht);
4629  $sql.= ", ".price2num($this->multicurrency_total_tva);
4630  $sql.= ", ".price2num($this->multicurrency_total_ttc);
4631  $sql.= ')';
4632 
4633  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4634  $resql=$this->db->query($sql);
4635  if ($resql)
4636  {
4637  $this->id=$this->db->last_insert_id(MAIN_DB_PREFIX.'facturedet');
4638  $this->rowid=$this->id; // For backward compatibility
4639 
4640  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4641  {
4642  $result=$this->insertExtraFields();
4643  if ($result < 0)
4644  {
4645  $error++;
4646  }
4647  }
4648 
4649  // Si fk_remise_except defini, on lie la remise a la facture
4650  // ce qui la flague comme "consommee".
4651  if ($this->fk_remise_except)
4652  {
4653  $discount=new DiscountAbsolute($this->db);
4654  $result=$discount->fetch($this->fk_remise_except);
4655  if ($result >= 0)
4656  {
4657  // Check if discount was found
4658  if ($result > 0)
4659  {
4660  // Check if discount not already affected to another invoice
4661  if ($discount->fk_facture_line > 0)
4662  {
4663  if (empty($noerrorifdiscountalreadylinked))
4664  {
4665  $this->error=$langs->trans("ErrorDiscountAlreadyUsed",$discount->id);
4666  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4667  $this->db->rollback();
4668  return -3;
4669  }
4670  }
4671  else
4672  {
4673  $result=$discount->link_to_invoice($this->rowid,0);
4674  if ($result < 0)
4675  {
4676  $this->error=$discount->error;
4677  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4678  $this->db->rollback();
4679  return -3;
4680  }
4681  }
4682  }
4683  else
4684  {
4685  $this->error=$langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4686  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4687  $this->db->rollback();
4688  return -3;
4689  }
4690  }
4691  else
4692  {
4693  $this->error=$discount->error;
4694  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4695  $this->db->rollback();
4696  return -3;
4697  }
4698  }
4699 
4700  if (! $notrigger)
4701  {
4702  // Call trigger
4703  $result=$this->call_trigger('LINEBILL_INSERT',$user);
4704  if ($result < 0)
4705  {
4706  $this->db->rollback();
4707  return -2;
4708  }
4709  // End call triggers
4710  }
4711 
4712  $this->db->commit();
4713  return $this->id;
4714  }
4715  else
4716  {
4717  $this->error=$this->db->lasterror();
4718  $this->db->rollback();
4719  return -2;
4720  }
4721  }
4722 
4730  function update($user='',$notrigger=0)
4731  {
4732  global $user,$conf;
4733 
4734  $error=0;
4735 
4736  $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4737 
4738  // Clean parameters
4739  $this->desc=trim($this->desc);
4740  if (empty($this->tva_tx)) $this->tva_tx=0;
4741  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4742  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4743  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4744  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4745  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4746  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4747  if (empty($this->remise_percent)) $this->remise_percent=0;
4748  if (empty($this->info_bits)) $this->info_bits=0;
4749  if (empty($this->special_code)) $this->special_code=0;
4750  if (empty($this->product_type)) $this->product_type=0;
4751  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4752  if (! isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100;
4753  if (empty($this->pa_ht)) $this->pa_ht=0;
4754 
4755  if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice=0;
4756  if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht=0;
4757  if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
4758  if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
4759 
4760  // Check parameters
4761  if ($this->product_type < 0) return -1;
4762 
4763  // if buy price not defined, define buyprice as configured in margin admin
4764  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4765  {
4766  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4767  {
4768  return $result;
4769  }
4770  else
4771  {
4772  $this->pa_ht = $result;
4773  }
4774  }
4775 
4776  $this->db->begin();
4777 
4778  // Mise a jour ligne en base
4779  $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
4780  $sql.= " description='".$this->db->escape($this->desc)."'";
4781  $sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4782  $sql.= ", subprice=".price2num($this->subprice)."";
4783  $sql.= ", remise_percent=".price2num($this->remise_percent)."";
4784  if ($this->fk_remise_except) $sql.= ", fk_remise_except=".$this->fk_remise_except;
4785  else $sql.= ", fk_remise_except=null";
4786  $sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->db->escape($this->vat_src_code))."'";
4787  $sql.= ", tva_tx=".price2num($this->tva_tx)."";
4788  $sql.= ", localtax1_tx=".price2num($this->localtax1_tx)."";
4789  $sql.= ", localtax2_tx=".price2num($this->localtax2_tx)."";
4790  $sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4791  $sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4792  $sql.= ", qty=".price2num($this->qty);
4793  $sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4794  $sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4795  $sql.= ", product_type=".$this->product_type;
4796  $sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4797  $sql.= ", special_code='".$this->db->escape($this->special_code)."'";
4798  if (empty($this->skip_update_total))
4799  {
4800  $sql.= ", total_ht=".price2num($this->total_ht);
4801  $sql.= ", total_tva=".price2num($this->total_tva);
4802  $sql.= ", total_ttc=".price2num($this->total_ttc);
4803  $sql.= ", total_localtax1=".price2num($this->total_localtax1);
4804  $sql.= ", total_localtax2=".price2num($this->total_localtax2);
4805  }
4806  $sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4807  $sql.= ", buy_price_ht='".price2num($this->pa_ht)."'";
4808  $sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4809  if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4810  $sql.= ", situation_percent=" . $this->situation_percent;
4811  $sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4812  $sql.= ", fk_user_modif =".$user->id;
4813 
4814  // Multicurrency
4815  $sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4816  $sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4817  $sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4818  $sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4819 
4820  $sql.= " WHERE rowid = ".$this->rowid;
4821 
4822  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4823  $resql=$this->db->query($sql);
4824  if ($resql)
4825  {
4826  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4827  {
4828  $this->id=$this->rowid;
4829  $result=$this->insertExtraFields();
4830  if ($result < 0)
4831  {
4832  $error++;
4833  }
4834  }
4835 
4836  if (! $error && ! $notrigger)
4837  {
4838  // Call trigger
4839  $result=$this->call_trigger('LINEBILL_UPDATE',$user);
4840  if ($result < 0)
4841  {
4842  $this->db->rollback();
4843  return -2;
4844  }
4845  // End call triggers
4846  }
4847  $this->db->commit();
4848  return 1;
4849  }
4850  else
4851  {
4852  $this->error=$this->db->error();
4853  $this->db->rollback();
4854  return -2;
4855  }
4856  }
4857 
4864  function delete()
4865  {
4866  global $user;
4867 
4868  $this->db->begin();
4869 
4870  // Call trigger
4871  $result=$this->call_trigger('LINEBILL_DELETE',$user);
4872  if ($result < 0)
4873  {
4874  $this->db->rollback();
4875  return -1;
4876  }
4877  // End call triggers
4878 
4879 
4880  $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".$this->rowid;
4881  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4882  if ($this->db->query($sql) )
4883  {
4884  $this->db->commit();
4885  return 1;
4886  }
4887  else
4888  {
4889  $this->error=$this->db->error()." sql=".$sql;
4890  $this->db->rollback();
4891  return -1;
4892  }
4893  }
4894 
4895  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4902  function update_total()
4903  {
4904  // phpcs:enable
4905  $this->db->begin();
4906  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
4907 
4908  // Clean parameters
4909  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4910  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4911 
4912  // Mise a jour ligne en base
4913  $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
4914  $sql.= " total_ht=".price2num($this->total_ht)."";
4915  $sql.= ",total_tva=".price2num($this->total_tva)."";
4916  $sql.= ",total_localtax1=".price2num($this->total_localtax1)."";
4917  $sql.= ",total_localtax2=".price2num($this->total_localtax2)."";
4918  $sql.= ",total_ttc=".price2num($this->total_ttc)."";
4919  $sql.= " WHERE rowid = ".$this->rowid;
4920 
4921  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
4922 
4923  $resql=$this->db->query($sql);
4924  if ($resql)
4925  {
4926  $this->db->commit();
4927  return 1;
4928  }
4929  else
4930  {
4931  $this->error=$this->db->error();
4932  $this->db->rollback();
4933  return -2;
4934  }
4935  }
4936 
4937  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4946  function get_prev_progress($invoiceid, $include_credit_note=true)
4947  {
4948  // phpcs:enable
4949  if (is_null($this->fk_prev_id) || empty($this->fk_prev_id) || $this->fk_prev_id == "") {
4950  return 0;
4951  } else {
4952  // If invoice is not a situation invoice, this->fk_prev_id is used for something else
4953  $tmpinvoice=new Facture($this->db);
4954  $tmpinvoice->fetch($invoiceid);
4955  if ($tmpinvoice->type != Facture::TYPE_SITUATION) return 0;
4956 
4957  $sql = 'SELECT situation_percent FROM ' . MAIN_DB_PREFIX . 'facturedet WHERE rowid=' . $this->fk_prev_id;
4958  $resql = $this->db->query($sql);
4959  if ($resql && $resql->num_rows > 0) {
4960  $res = $this->db->fetch_array($resql);
4961 
4962  $returnPercent = floatval($res['situation_percent']);
4963 
4964  if($include_credit_note) {
4965 
4966  $sql = 'SELECT fd.situation_percent FROM ' . MAIN_DB_PREFIX . 'facturedet fd';
4967  $sql.= ' JOIN ' . MAIN_DB_PREFIX . 'facture f ON (f.rowid = fd.fk_facture) ';
4968  $sql.= ' WHERE fd.fk_prev_id =' . $this->fk_prev_id;
4969  $sql.= ' AND f.situation_cycle_ref = '.$tmpinvoice->situation_cycle_ref; // Prevent cycle outed
4970  $sql.= ' AND f.type = '.Facture::TYPE_CREDIT_NOTE;
4971 
4972  $res = $this->db->query($sql);
4973  if($res) {
4974  while($obj = $this->db->fetch_object($res)) {
4975  $returnPercent = $returnPercent + floatval($obj->situation_percent);
4976  }
4977  }
4978  }
4979 
4980  return $returnPercent;
4981  } else {
4982  $this->error = $this->db->error();
4983  dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR);
4984  $this->db->rollback();
4985  return -1;
4986  }
4987  }
4988  }
4989 }
print $object label
hash of file content (md5_file(dol_osencode($destfull))
Definition: edit.php:153
calculate_date_lim_reglement($cond_reglement=0)
Renvoi une date limite de reglement de facture en fonction des conditions de reglements de la facture...
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
Class to manage stock movements.
GETPOST($paramname, $check='none', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
set_draft($user, $idwarehouse=-1)
Set draft status.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
const TYPE_STANDARD
Standard invoice.
print
Draft customers invoices.
Definition: index.php:91
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.
if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) if(! empty($conf->don->enabled) && $user->rights->societe->lire) if(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) if(! empty($conf->facture->enabled) &&! empty($conf->commande->enabled) && $user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1053
static getIdFromCode(&$db, $code)
Get id of currency from code.
initAsSpecimen($option='')
Initialise an instance with random values.
update_percent($line, $percent)
Update invoice line with percentage.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
set_remise($user, $remise, $notrigger=0)
Set percent discount.
deleteline($rowid)
Delete line in database.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
Delete all links between an object $this.
createFromOrder($object, User $user)
Load an object from an order and create a new invoice into database.
getDirectExternalLink($withpicto=0)
Return link to download file from a direct external access.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only). ...
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -> yes (1) I...
fetch($rowid)
Load invoice line from database.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm=false, $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
createFromCurrent(User $user, $invertdetail=0)
Create a new invoice in database from current invoice.
getLinesArray()
Create an array of invoice lines.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $date_start='', $date_end='', $ventil=0, $info_bits=0, $fk_remise_except='', $price_base_type='HT', $pu_ttc=0, $type=self::TYPE_STANDARD, $rang=-1, $special_code=0, $origin='', $origin_id=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $situation_percent=100, $fk_prev_id=0, $fk_unit=null, $pu_ht_devise=0)
Add an invoice line into database (linked to product/service or not).
if(! empty($search_group)) natural_search(array("g.nom" g note
Definition: list.php:123
$close_note
Commentaire si mis a paye sans paiement complet.
Class to manage products or services.
fetch_lines()
Load all detailed lines into this->lines.
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1324
Class to manage invoice templates.
insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
Insert line into database.
Class to manage Dolibarr users.
Definition: user.class.php:41
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0)
Return an id or code from a code or id.
const TYPE_REPLACEMENT
Replacement invoice.
Class to manage Dolibarr database access.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
Class to manage bank accounts description of third parties.
insert_discount($idremise)
Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume...
set_unpaid($user)
Tag la facture comme non payee completement + appel trigger BILL_UNPAYED Fonction utilisee quand un p...
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
fetchPreviousNextSituationInvoice()
Fetch previous and next situations invoices.
dol_concatdesc($text1, $text2, $forxml=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
setFinal(User $user, $notrigger=0)
Sets the invoice as a final situation.
get_prev_sits()
Returns an array containing the previous situations as Facture objects.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
const TYPE_PROFORMA
Proforma invoice (should not be used.
set_paid($user, $close_code='', $close_note='')
Tag la facture comme paye completement (si close_code non renseigne) => this->fk_statut=2, this->paye=1 ou partiellement (si close_code renseigne) + appel trigger BILL_PAYED => this->fk_statut=2, this->paye stay 0.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
Parent class of all other business classes for details of elements (invoices, contracts, proposals, orders, ...)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
load_state_board()
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
const TYPE_SITUATION
Situation invoice.
$desc
Description ligne.
const TYPE_CREDIT_NOTE
Credit note invoice.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
updatePriceNextInvoice(&$langs)
Update price of next invoice.
$table_ref_field
{}
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
type
Definition: viewcat.php:284
Class to manage third parties objects (customers, suppliers, prospects...)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
demande_prelevement_delete($fuser, $did)
Supprime une demande de prelevement.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
checkProgressLine($idline, $situation_percent)
Check if the percent edited is lower of next invoice line.
const STATUS_VALIDATED
Validated (need to be paid)
getSommePaiement($multicurrency=0)
Return amount of payments already done.
create(User $user, $notrigger=0, $forceduedate=0)
Create invoice in database.
update($user='', $notrigger=0)
Update line into database.
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...
Classe permettant la generation de composants html autre Only common components are here...
validate($user, $force_number='', $idwarehouse=0, $notrigger=0)
Tag invoice as validated + call trigger BILL_VALIDATE Object must have lines loaded with fetch_lines...
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)...
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $addlinktonotes=0, $save_lastsearch_value=-1)
Return clicable link of object (with eventually picto)
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts.
getSumCreditNotesUsed($multicurrency=0)
Return amount (with tax) of all credit notes invoices + excess received used by invoice.
$pos_source
key of pos source (&#39;0&#39;, &#39;1&#39;, ...)
Class to manage shipments.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
list_replacable_invoices($socid=0)
Return list of invoices qualified to be replaced by another invoice.
const STATUS_DRAFT
Draft status.
set_remise_absolue($user, $remise, $notrigger=0)
Set absolute discount.
update(User $user, $notrigger=0)
Update database.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1273
$module_source
key of module source when invoice generated from a dedicated module (&#39;cashdesk&#39;, &#39;takepos&#39;, ...)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1)
Remove a file or several files with a mask.
Definition: files.lib.php:1139
deleteExtraFields()
Delete all extra fields values for the current object.
Class to manage translations.
createFromClone($socid=0)
Load an object from its id and create a new one in database.
if(GETPOST('cancel', 'alpha')) if(! GETPOST( 'confirmmassaction', 'alpha') &&$massaction !='presend' &&$massaction !='confirm_presend')
Draft customers invoices.
Definition: list.php:156
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:59
const STATUS_CLOSED
Classified paid.
dol_now($mode='gmt')
Return date for now.
newCycle()
Gets the smallest reference available for a new cycle.
$fk_fac_rec_source
id of template invoice when generated from a template invoice
set_canceled($user, $close_code='', $close_note='')
Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never rece...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0)
Clean a string from all HTML tags and entities.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
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)
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:85
Superclass for invoices classes.
const STATUS_ABANDONED
Classified abandoned and no payment done.
setPaymentMethods($id)
Change the payments methods.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
dol_print_date($time, $format='', $tzoutput='tzserver', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
getIdBillingContact()
Retourne id des contacts clients de facturation.
const TYPE_DEPOSIT
Deposit invoice.
hasDelay()
Is the customer invoice delayed?
getSumDepositsUsed($multicurrency=0)
Return amount (with tax) of all deposits invoices used by invoice.
demande_prelevement($fuser, $amount=0)
Create a withdrawal request for a standing order.
getIdShippingContact()
Retourne id des contacts clients de livraison.
$close_code
Fermeture apres paiement partiel: discount_vat, badcustomer, abandon Fermeture alors que aucun paieme...
liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='f.datef, f.rowid', $sortorder='DESC')
Return list of invoices (eventually filtered on a user) into an array.
getNextNumRef($soc, $mode='next')
Return next reference of customer invoice not already used (or last reference) according to numbering...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
fetch($rowid, $ref='', $ref_ext='', $ref_int='', $fetch_situation=false)
Get object and lines from database.
Class to manage absolute discounts.
info($id)
Load miscellaneous information for tab "Info".
$fk_facture_source
id of source invoice if replacement invoice or credit note
$fk_parent_line
Id parent line.
$paye
1 if invoice paid COMPLETELY, 0 otherwise (do not use it anymore, use statut and close_code) ...
make_substitutions($text, $substitutionarray, $outputlangs=null)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
Class to manage invoices.
dol_getdate($timestamp, $fast=false)
Return an array with locale date info.
call_trigger($trigger_name, $user)
Call trigger based on this instance.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $txtva, $txlocaltax1=0, $txlocaltax2=0, $price_base_type='HT', $info_bits=0, $type=self::TYPE_STANDARD, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $situation_percent=100, $fk_unit=null, $pu_ht_devise=0, $notrigger=0)
Update a detail line.
list_qualified_avoir_invoices($socid=0)
Return list of invoices qualified to be corrected by a credit note.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
is_last_in_cycle()
Checks if the invoice is the last in its cycle.
is_first()
Checks if the invoice is the first of a cycle.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
dol_time_plus_duree($time, $duration_value, $duration_unit)
Add a delay to a date.
Definition: date.lib.php:116
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it&#39;s its name (generic function)
$fk_facture
From llx_facturedet Id facture.
Class to manage ECM files.
update_total()
Update DB line fields total_xxx Used by migration.
set_ref_client($ref_client, $notrigger=0)
Set customer ref.
price2num($amount, $rounding='', $alreadysqlnb=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
__construct($db)
Constructor.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
get_prev_progress($invoiceid, $include_credit_note=true)
Returns situation_percent of the previous line.
Class to manage invoice lines.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
static getIdAndTxFromCode(&$db, $code, $date_document='')
Get id and rate of currency from code.