dolibarr  7.0.0-beta
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@capnetworks.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  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  */
32 
39 include_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
40 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/societe/class/client.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
44 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
45 
49 class Facture extends CommonInvoice
50 {
51  public $element='facture';
52  public $table_element='facture';
53  public $table_element_line = 'facturedet';
54  public $fk_element = 'fk_facture';
55  public $picto='bill';
60  public $ismultientitymanaged = 1;
65  public $restrictiononfksoc = 1;
66 
70  protected $table_ref_field = 'facnumber';
71 
72  public $socid;
73 
74  public $author;
75  public $fk_user_author;
76  public $fk_user_valid;
77  public $date; // Date invoice
78  public $date_creation; // Creation date
79  public $date_validation; // Validation date
80  public $datem;
81  public $ref_client;
82  public $ref_int;
83  //Check constants for types
84  public $type = self::TYPE_STANDARD;
85 
86  //var $amount;
87  public $remise_absolue;
88  public $remise_percent;
89  public $total_ht=0;
90  public $total_tva=0;
91  public $total_localtax1=0;
92  public $total_localtax2=0;
93  public $total_ttc=0;
94  public $revenuestamp;
95 
98  public $close_code;
100  public $close_note;
102  public $paye;
105  public $linked_objects=array();
106  public $date_lim_reglement;
107  public $cond_reglement_code; // Code in llx_c_paiement
108  public $mode_reglement_code; // Code in llx_c_paiement
109  public $fk_bank; // Field to store bank id to use when payment mode is withdraw
113  public $products=array();
117  public $lines=array();
118  public $line;
119  public $extraparams=array();
120  public $specimen;
121 
122  public $fac_rec;
123 
124  // Multicurrency
125  public $fk_multicurrency;
126  public $multicurrency_code;
127  public $multicurrency_tx;
128  public $multicurrency_total_ht;
129  public $multicurrency_total_tva;
130  public $multicurrency_total_ttc;
131 
135  public $situation_cycle_ref;
136 
140  public $situation_counter;
141 
145  public $situation_final;
146 
150  public $tab_previous_situation_invoice=array();
151 
155  public $tab_next_situation_invoice=array();
156 
157  public $oldcopy;
158 
162  const TYPE_STANDARD = 0;
163 
167  const TYPE_REPLACEMENT = 1;
168 
172  const TYPE_CREDIT_NOTE = 2;
173 
177  const TYPE_DEPOSIT = 3;
178 
182  const TYPE_PROFORMA = 4;
183 
187  const TYPE_SITUATION = 5;
188 
192  const STATUS_DRAFT = 0;
193 
197  const STATUS_VALIDATED = 1;
198 
206  const STATUS_CLOSED = 2;
207 
215  const STATUS_ABANDONED = 3;
216 
217  const CLOSECODE_DISCOUNTVAT = 'discount_vat'; // Abandonned remain - escompte
218  const CLOSECODE_BADDEBT = 'badcustomer'; // Abandonned - bad
219  const CLOSECODE_ABANDONED = 'abandon'; // Abandonned - other
220  const CLOSECODE_REPLACED = 'replaced'; // Closed after doing a replacement invoice
221 
227  function __construct($db)
228  {
229  $this->db = $db;
230  }
231 
242  function create($user,$notrigger=0,$forceduedate=0)
243  {
244  global $langs,$conf,$mysoc,$hookmanager;
245  $error=0;
246 
247  // Clean parameters
248  if (empty($this->type)) $this->type = self::TYPE_STANDARD;
249  $this->ref_client=trim($this->ref_client);
250  $this->note=(isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
251  $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note_private));
252  $this->note_public=trim($this->note_public);
253  if (! $this->cond_reglement_id) $this->cond_reglement_id = 0;
254  if (! $this->mode_reglement_id) $this->mode_reglement_id = 0;
255  $this->brouillon = 1;
256  if (empty($this->entity)) $this->entity = $conf->entity;
257 
258  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
259  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
260  else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
261  if (empty($this->fk_multicurrency))
262  {
263  $this->multicurrency_code = $conf->currency;
264  $this->fk_multicurrency = 0;
265  $this->multicurrency_tx = 1;
266  }
267 
268  dol_syslog(get_class($this)."::create user=".$user->id);
269 
270  // Check parameters
271  if (empty($this->date) || empty($user->id))
272  {
273  $this->error="ErrorBadParameter";
274  dol_syslog(get_class($this)."::create Try to create an invoice with an empty parameter (user, date, ...)", LOG_ERR);
275  return -3;
276  }
277  $soc = new Societe($this->db);
278  $result=$soc->fetch($this->socid);
279  if ($result < 0)
280  {
281  $this->error="Failed to fetch company: ".$soc->error;
282  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
283  return -2;
284  }
285 
286  $now=dol_now();
287 
288  $this->db->begin();
289 
290  // Create invoice from a template invoice
291  if ($this->fac_rec > 0)
292  {
293  $this->fk_fac_rec_source = $this->fac_rec;
294 
295  require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
296  $_facrec = new FactureRec($this->db);
297  $result=$_facrec->fetch($this->fac_rec);
298  $result=$_facrec->fetchObjectLinked(); // This load $_facrec->linkedObjectsIds
299 
300  $originaldatewhen = $_facrec->date_when;
301 
302  $this->socid = $_facrec->socid; // Invoice created on same thirdparty than template
303  $this->entity = $_facrec->entity; // Invoice created in same entity than template
304 
305  // 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
306  $this->fk_project = GETPOST('projectid','int') > 0 ? GETPOST('projectid','int') : $_facrec->fk_project;
307  $this->note_public = GETPOST('note_public','none') ? GETPOST('note_public','none') : $_facrec->note_public;
308  $this->note_private = GETPOST('note_private','none') ? GETPOST('note_private','none') : $_facrec->note_private;
309  $this->modelpdf = GETPOST('model') ? GETPOST('model') : $_facrec->modelpdf;
310  $this->cond_reglement_id = GETPOST('cond_reglement_id') > 0 ? GETPOST('cond_reglement_id') : $_facrec->cond_reglement_id;
311  $this->mode_reglement_id = GETPOST('mode_reglement_id') > 0 ? GETPOST('mode_reglement_id') : $_facrec->mode_reglement_id;
312  $this->fk_account = GETPOST('fk_account') > 0 ? GETPOST('fk_account') : $_facrec->fk_account;
313 
314  // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
315  $this->total_ht = $_facrec->total_ht;
316  $this->total_ttc = $_facrec->total_ttc;
317 
318  // Fields always coming from template
319  $this->remise_absolue = $_facrec->remise_absolue;
320  $this->remise_percent = $_facrec->remise_percent;
321  $this->fk_incoterms = $_facrec->fk_incoterms;
322  $this->location_incoterms= $_facrec->location_incoterms;
323 
324  // Clean parameters
325  if (! $this->type) $this->type = self::TYPE_STANDARD;
326  $this->ref_client=trim($this->ref_client);
327  $this->note_public=trim($this->note_public);
328  $this->note_private=trim($this->note_private);
329  $this->note_private=dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->ref));
330 
331  $this->array_options=$_facrec->array_options;
332 
333  //if (! $this->remise) $this->remise = 0;
334  if (! $this->mode_reglement_id) $this->mode_reglement_id = 0;
335  $this->brouillon = 1;
336 
337  $this->linked_objects = $_facrec->linkedObjectsIds;
338 
339  $forceduedate = $this->calculate_date_lim_reglement();
340 
341  // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
342  if ($_facrec->frequency > 0)
343  {
344  dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
345  if (empty($_facrec->date_when)) $_facrec->date_when = $now;
346  $next_date = $_facrec->getNextDate(); // Calculate next date
347  $result = $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date', '', $user, '');
348  //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
349  $result = $_facrec->setNextDate($next_date,1);
350  }
351 
352  // Define lang of customer
353  $outputlangs = $langs;
354  $newlang='';
355 
356  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->thirdparty->default_lang)) $newlang=$this->thirdparty->default_lang; // for proposal, order, invoice, ...
357  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($this->default_lang)) $newlang=$this->default_lang; // for thirdparty
358  if (! empty($newlang))
359  {
360  $outputlangs = new Translate("",$conf);
361  $outputlangs->setDefaultLang($newlang);
362  }
363 
364  // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
365  $substitutionarray=getCommonSubstitutionArray($outputlangs, 0, null, $this);
366  $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
367  $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
368  $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
369  $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
370  $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
371  $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
372  $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
373  $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
374  $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y');
375  // Only for tempalte invoice
376  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour');
377  $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date(dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency), 'dayhour');
378 
379  //var_dump($substitutionarray);exit;
380 
381  $substitutionisok=true;
382  complete_substitutions_array($substitutionarray, $outputlangs);
383 
384  $this->note_public=make_substitutions($this->note_public,$substitutionarray);
385  $this->note_private=make_substitutions($this->note_private,$substitutionarray);
386  }
387 
388  // Define due date if not already defined
389  $datelim=(empty($forceduedate)?$this->calculate_date_lim_reglement():$forceduedate);
390 
391  // Insert into database
392  $socid = $this->socid;
393 
394  $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture (";
395  $sql.= " facnumber";
396  $sql.= ", entity";
397  $sql.= ", ref_ext";
398  $sql.= ", type";
399  $sql.= ", fk_soc";
400  $sql.= ", datec";
401  $sql.= ", remise_absolue";
402  $sql.= ", remise_percent";
403  $sql.= ", datef";
404  $sql.= ", date_pointoftax";
405  $sql.= ", note_private";
406  $sql.= ", note_public";
407  $sql.= ", ref_client, ref_int";
408  $sql.= ", fk_account";
409  $sql.= ", fk_fac_rec_source, fk_facture_source, fk_user_author, fk_projet";
410  $sql.= ", fk_cond_reglement, fk_mode_reglement, date_lim_reglement, model_pdf";
411  $sql.= ", situation_cycle_ref, situation_counter, situation_final";
412  $sql.= ", fk_incoterms, location_incoterms";
413  $sql.= ", fk_multicurrency";
414  $sql.= ", multicurrency_code";
415  $sql.= ", multicurrency_tx";
416  $sql.= ")";
417  $sql.= " VALUES (";
418  $sql.= "'(PROV)'";
419  $sql.= ", ".$this->entity;
420  $sql.= ", ".($this->ref_ext?"'".$this->db->escape($this->ref_ext)."'":"null");
421  $sql.= ", '".$this->db->escape($this->type)."'";
422  $sql.= ", '".$socid."'";
423  $sql.= ", '".$this->db->idate($now)."'";
424  $sql.= ", ".($this->remise_absolue>0?$this->remise_absolue:'NULL');
425  $sql.= ", ".($this->remise_percent>0?$this->remise_percent:'NULL');
426  $sql.= ", '".$this->db->idate($this->date)."'";
427  $sql.= ", ".(strval($this->date_pointoftax)!='' ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null');
428  $sql.= ", ".($this->note_private?"'".$this->db->escape($this->note_private)."'":"null");
429  $sql.= ", ".($this->note_public?"'".$this->db->escape($this->note_public)."'":"null");
430  $sql.= ", ".($this->ref_client?"'".$this->db->escape($this->ref_client)."'":"null");
431  $sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
432  $sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
433  $sql.= ", ".($this->fk_fac_rec_source?"'".$this->db->escape($this->fk_fac_rec_source)."'":"null");
434  $sql.= ", ".($this->fk_facture_source?"'".$this->db->escape($this->fk_facture_source)."'":"null");
435  $sql.= ", ".($user->id > 0 ? "'".$user->id."'":"null");
436  $sql.= ", ".($this->fk_project?$this->fk_project:"null");
437  $sql.= ", ".$this->cond_reglement_id;
438  $sql.= ", ".$this->mode_reglement_id;
439  $sql.= ", '".$this->db->idate($datelim)."', '".$this->db->escape($this->modelpdf)."'";
440  $sql.= ", ".($this->situation_cycle_ref?"'".$this->db->escape($this->situation_cycle_ref)."'":"null");
441  $sql.= ", ".($this->situation_counter?"'".$this->db->escape($this->situation_counter)."'":"null");
442  $sql.= ", ".($this->situation_final?$this->situation_final:0);
443  $sql.= ", ".(int) $this->fk_incoterms;
444  $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
445  $sql.= ", ".(int) $this->fk_multicurrency;
446  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
447  $sql.= ", ".(double) $this->multicurrency_tx;
448  $sql.=")";
449 
450  $resql=$this->db->query($sql);
451  if ($resql)
452  {
453  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture');
454 
455  // Update ref with new one
456  $this->ref='(PROV'.$this->id.')';
457  $sql = 'UPDATE '.MAIN_DB_PREFIX."facture SET facnumber='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
458 
459  $resql=$this->db->query($sql);
460  if (! $resql) $error++;
461 
462  if (! empty($this->linkedObjectsIds) && empty($this->linked_objects)) // To use new linkedObjectsIds instead of old linked_objects
463  {
464  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
465  }
466 
467  // Add object linked
468  if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
469  {
470  foreach($this->linked_objects as $origin => $tmp_origin_id)
471  {
472  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, ...))
473  {
474  foreach($tmp_origin_id as $origin_id)
475  {
476  $ret = $this->add_object_linked($origin, $origin_id);
477  if (! $ret)
478  {
479  $this->error=$this->db->lasterror();
480  $error++;
481  }
482  }
483  }
484  else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
485  {
486  $origin_id = $tmp_origin_id;
487  $ret = $this->add_object_linked($origin, $origin_id);
488  if (! $ret)
489  {
490  $this->error=$this->db->lasterror();
491  $error++;
492  }
493  }
494  }
495  }
496 
497  if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id)) // Get contact from origin object
498  {
499  $originforcontact = $this->origin;
500  $originidforcontact = $this->origin_id;
501  if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
502  {
503  require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
504  $exp = new Expedition($this->db);
505  $exp->fetch($this->origin_id);
506  $exp->fetchObjectLinked();
507  if (count($exp->linkedObjectsIds['commande']) > 0)
508  {
509  foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
510  {
511  $originforcontact = 'commande';
512  if (is_object($value)) $originidforcontact = $value->id;
513  else $originidforcontact = $value;
514  break; // We take first one
515  }
516  }
517  }
518 
519  $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";
520  $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
521 
522  $resqlcontact = $this->db->query($sqlcontact);
523  if ($resqlcontact)
524  {
525  while($objcontact = $this->db->fetch_object($resqlcontact))
526  {
527  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
528  $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
529  }
530  }
531  else dol_print_error($resqlcontact);
532  }
533 
534  /*
535  * Insert lines of invoices into database
536  */
537  if (count($this->lines) && is_object($this->lines[0])) // If this->lines is array of InvoiceLines (preferred mode)
538  {
539  $fk_parent_line = 0;
540 
541  dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
542  foreach ($this->lines as $i => $val)
543  {
544  $newinvoiceline=$this->lines[$i];
545  $newinvoiceline->fk_facture=$this->id;
546 
547  // TODO This seems not used. Here we put origin 'facture' but after, we put an id of object !
548  $newinvoiceline->origin = $this->element;
549  $newinvoiceline->origin_id = $this->lines[$i]->id;
550 
551  if ($result >= 0)
552  {
553  // Reset fk_parent_line for no child products and special product
554  if (($newinvoiceline->product_type != 9 && empty($newinvoiceline->fk_parent_line)) || $newinvoiceline->product_type == 9) {
555  $fk_parent_line = 0;
556  }
557 
558  $newinvoiceline->fk_parent_line=$fk_parent_line;
559  $result=$newinvoiceline->insert();
560 
561  // Defined the new fk_parent_line
562  if ($result > 0 && $newinvoiceline->product_type == 9) {
563  $fk_parent_line = $result;
564  }
565  }
566  if ($result < 0)
567  {
568  $this->error=$newinvoiceline->error;
569  $error++;
570  break;
571  }
572  }
573  }
574  else // If this->lines is an array of invoice line arrays
575  {
576  $fk_parent_line = 0;
577 
578  dol_syslog("There is ".count($this->lines)." lines that are array lines");
579 
580  foreach ($this->lines as $i => $val)
581  {
582  $line = $this->lines[$i];
583 
584  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
585  //if (! is_object($line)) $line=json_decode(json_encode($line), FALSE); // convert recursively array into object.
586  if (! is_object($line)) $line = (object) $line;
587 
588  if ($result >= 0)
589  {
590  // Reset fk_parent_line for no child products and special product
591  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
592  $fk_parent_line = 0;
593  }
594 
595  // Complete vat rate with code
596  $vatrate = $line->tva_tx;
597  if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
598 
599  $result = $this->addline(
600  $line->desc,
601  $line->subprice,
602  $line->qty,
603  $vatrate,
604  $line->localtax1_tx,
605  $line->localtax2_tx,
606  $line->fk_product,
607  $line->remise_percent,
608  $line->date_start,
609  $line->date_end,
610  $line->fk_code_ventilation,
611  $line->info_bits,
612  $line->fk_remise_except,
613  'HT',
614  0,
615  $line->product_type,
616  $line->rang,
617  $line->special_code,
618  $this->element,
619  $line->id,
620  $fk_parent_line,
621  $line->fk_fournprice,
622  $line->pa_ht,
623  $line->label,
624  $line->array_options,
625  $line->situation_percent,
626  $line->fk_prev_id,
627  $line->fk_unit,
628  $line->pu_ht_devise
629  );
630  if ($result < 0)
631  {
632  $this->error=$this->db->lasterror();
633  dol_print_error($this->db);
634  $this->db->rollback();
635  return -1;
636  }
637 
638  // Defined the new fk_parent_line
639  if ($result > 0 && $line->product_type == 9) {
640  $fk_parent_line = $result;
641  }
642  }
643  }
644  }
645 
646  /*
647  * Insert lines of predefined invoices
648  */
649  if (! $error && $this->fac_rec > 0)
650  {
651  foreach ($_facrec->lines as $i => $val)
652  {
653  if ($_facrec->lines[$i]->fk_product)
654  {
655  $prod = new Product($this->db);
656  $res=$prod->fetch($_facrec->lines[$i]->fk_product);
657  }
658  $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
659  $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
660  if (empty($tva_tx)) $tva_npr=0;
661  $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
662  $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
663 
664  $result_insert = $this->addline(
665  $_facrec->lines[$i]->desc,
666  $_facrec->lines[$i]->subprice,
667  $_facrec->lines[$i]->qty,
668  $tva_tx,
669  $localtax1_tx,
670  $localtax2_tx,
671  $_facrec->lines[$i]->fk_product,
672  $_facrec->lines[$i]->remise_percent,
673  '','',0,$tva_npr,'','HT',0,
674  $_facrec->lines[$i]->product_type,
675  $_facrec->lines[$i]->rang,
676  $_facrec->lines[$i]->special_code,
677  '',
678  0,
679  0,
680  null,
681  0,
682  $_facrec->lines[$i]->label,
683  empty($_facrec->lines[$i]->array_options)?null:$_facrec->lines[$i]->array_options,
684  $_facrec->lines[$i]->situation_percent,
685  '',
686  $_facrec->lines[$i]->fk_unit,
687  $_facrec->lines[$i]->pu_ht_devise
688  );
689 
690  if ( $result_insert < 0)
691  {
692  $error++;
693  $this->error=$this->db->error();
694  break;
695  }
696  }
697  }
698 
699  if (! $error)
700  {
701 
702  $result=$this->update_price(1);
703  if ($result > 0)
704  {
705  $action='create';
706 
707  // Actions on extra fields (by external module or standard code)
708  // TODO le hook fait double emploi avec le trigger !!
709  /*
710  $hookmanager->initHooks(array('invoicedao'));
711  $parameters=array('invoiceid'=>$this->id);
712  $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
713  if (empty($reshook))
714  {
715  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
716  {*/
717  if (! $error)
718  {
719  $result=$this->insertExtraFields();
720  if ($result < 0) $error++;
721  }
722  /*}
723  }
724  else if ($reshook < 0) $error++;*/
725 
726  // Call trigger
727  $result=$this->call_trigger('BILL_CREATE',$user);
728  if ($result < 0) $error++;
729  // End call triggers
730 
731  if (! $error)
732  {
733  $this->db->commit();
734  return $this->id;
735  }
736  else
737  {
738  $this->db->rollback();
739  return -4;
740  }
741  }
742  else
743  {
744  $this->error=$langs->trans('FailedToUpdatePrice');
745  $this->db->rollback();
746  return -3;
747  }
748  }
749  else
750  {
751  dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
752  $this->db->rollback();
753  return -2;
754  }
755  }
756  else
757  {
758  $this->error=$this->db->error();
759  $this->db->rollback();
760  return -1;
761  }
762  }
763 
764 
772  function createFromCurrent(User $user, $invertdetail=0)
773  {
774  global $conf;
775 
776  // Charge facture source
777  $facture=new Facture($this->db);
778 
779  $this->fetch_optionals();
780  if(!empty($this->array_options)){
781  $facture->array_options = $this->array_options;
782  }
783 
784  foreach($this->lines as &$line){
785  $line->fetch_optionals();//fetch extrafields
786  }
787 
788  $facture->fk_facture_source = $this->fk_facture_source;
789  $facture->type = $this->type;
790  $facture->socid = $this->socid;
791  $facture->date = $this->date;
792  $facture->date_pointoftax = $this->date_pointoftax;
793  $facture->note_public = $this->note_public;
794  $facture->note_private = $this->note_private;
795  $facture->ref_client = $this->ref_client;
796  $facture->modelpdf = $this->modelpdf;
797  $facture->fk_project = $this->fk_project;
798  $facture->cond_reglement_id = $this->cond_reglement_id;
799  $facture->mode_reglement_id = $this->mode_reglement_id;
800  $facture->remise_absolue = $this->remise_absolue;
801  $facture->remise_percent = $this->remise_percent;
802 
803  $facture->origin = $this->origin;
804  $facture->origin_id = $this->origin_id;
805 
806  $facture->lines = $this->lines; // Tableau des lignes de factures
807  $facture->products = $this->lines; // Tant que products encore utilise
808  $facture->situation_counter = $this->situation_counter;
809  $facture->situation_cycle_ref=$this->situation_cycle_ref;
810  $facture->situation_final = $this->situation_final;
811 
812  // Loop on each line of new invoice
813  foreach($facture->lines as $i => $tmpline)
814  {
815  $facture->lines[$i]->fk_prev_id = $this->lines[$i]->rowid;
816  if ($invertdetail)
817  {
818  $facture->lines[$i]->subprice = -$facture->lines[$i]->subprice;
819  $facture->lines[$i]->total_ht = -$facture->lines[$i]->total_ht;
820  $facture->lines[$i]->total_tva = -$facture->lines[$i]->total_tva;
821  $facture->lines[$i]->total_localtax1 = -$facture->lines[$i]->total_localtax1;
822  $facture->lines[$i]->total_localtax2 = -$facture->lines[$i]->total_localtax2;
823  $facture->lines[$i]->total_ttc = -$facture->lines[$i]->total_ttc;
824  }
825  }
826 
827  dol_syslog(get_class($this)."::createFromCurrent invertdetail=".$invertdetail." socid=".$this->socid." nboflines=".count($facture->lines));
828 
829  $facid = $facture->create($user);
830  if ($facid <= 0)
831  {
832  $this->error=$facture->error;
833  $this->errors=$facture->errors;
834  }
835  elseif ($this->type == self::TYPE_SITUATION && !empty($conf->global->INVOICE_USE_SITUATION))
836  {
837  $this->fetchObjectLinked('', '', $object->id, 'facture');
838 
839  foreach ($this->linkedObjectsIds as $typeObject => $Tfk_object)
840  {
841  foreach ($Tfk_object as $fk_object)
842  {
843  $facture->add_object_linked($typeObject, $fk_object);
844  }
845  }
846 
847  $facture->add_object_linked('facture', $this->fk_facture_source);
848  }
849 
850  return $facid;
851  }
852 
853 
860  function createFromClone($socid=0)
861  {
862  global $user,$hookmanager;
863 
864  $error=0;
865 
866  $this->context['createfromclone'] = 'createfromclone';
867 
868  $this->db->begin();
869 
870  // get extrafields so they will be clone
871  foreach($this->lines as $line)
872  $line->fetch_optionals($line->rowid);
873 
874  // Load source object
875  $objFrom = clone $this;
876 
877 
878 
879  // Change socid if needed
880  if (! empty($socid) && $socid != $this->socid)
881  {
882  $objsoc = new Societe($this->db);
883 
884  if ($objsoc->fetch($socid)>0)
885  {
886  $this->socid = $objsoc->id;
887  $this->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
888  $this->mode_reglement_id = (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
889  $this->fk_project = '';
890  $this->fk_delivery_address = '';
891  }
892 
893  // TODO Change product price if multi-prices
894  }
895 
896  $this->id=0;
897  $this->statut= self::STATUS_DRAFT;
898 
899  // Clear fields
900  $this->date = dol_now(); // Date of invoice is set to current date when cloning. // TODO Best is to ask date into confirm box
901  $this->user_author = $user->id;
902  $this->user_valid = '';
903  $this->fk_facture_source = 0;
904  $this->date_creation = '';
905  $this->date_validation = '';
906  $this->ref_client = '';
907  $this->close_code = '';
908  $this->close_note = '';
909  $this->products = $this->lines; // Tant que products encore utilise
910 
911  // Loop on each line of new invoice
912  foreach($this->lines as $i => $line)
913  {
914  if (($this->lines[$i]->info_bits & 0x02) == 0x02) // We do not clone line of discounts
915  {
916  unset($this->lines[$i]);
917  unset($this->products[$i]); // Tant que products encore utilise
918  }
919  }
920 
921  // Create clone
922  $result=$this->create($user);
923  if ($result < 0) $error++;
924  else {
925  // copy internal contacts
926  if ($this->copy_linked_contact($objFrom, 'internal') < 0)
927  $error++;
928 
929  // copy external contacts if same company
930  elseif ($objFrom->socid == $this->socid)
931  {
932  if ($this->copy_linked_contact($objFrom, 'external') < 0)
933  $error++;
934  }
935  }
936 
937  if (! $error)
938  {
939  // Hook of thirdparty module
940  if (is_object($hookmanager))
941  {
942  $parameters=array('objFrom'=>$objFrom);
943  $action='';
944  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
945  if ($reshook < 0) $error++;
946  }
947 
948  // Call trigger
949  $result=$this->call_trigger('BILL_CLONE',$user);
950  if ($result < 0) $error++;
951  // End call triggers
952  }
953 
954  unset($this->context['createfromclone']);
955 
956  // End
957  if (! $error)
958  {
959  $this->db->commit();
960  return $this->id;
961  }
962  else
963  {
964  $this->db->rollback();
965  return -1;
966  }
967  }
968 
976  function createFromOrder($object, User $user)
977  {
978  global $hookmanager;
979 
980  $error=0;
981 
982  // Closed order
983  $this->date = dol_now();
984  $this->source = 0;
985 
986  $num=count($object->lines);
987  for ($i = 0; $i < $num; $i++)
988  {
989  $line = new FactureLigne($this->db);
990 
991  $line->libelle = $object->lines[$i]->libelle;
992  $line->label = $object->lines[$i]->label;
993  $line->desc = $object->lines[$i]->desc;
994  $line->subprice = $object->lines[$i]->subprice;
995  $line->total_ht = $object->lines[$i]->total_ht;
996  $line->total_tva = $object->lines[$i]->total_tva;
997  $line->total_localtax1 = $object->lines[$i]->total_localtax1;
998  $line->total_localtax2 = $object->lines[$i]->total_localtax2;
999  $line->total_ttc = $object->lines[$i]->total_ttc;
1000  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1001  $line->tva_tx = $object->lines[$i]->tva_tx;
1002  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1003  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1004  $line->qty = $object->lines[$i]->qty;
1005  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1006  $line->remise_percent = $object->lines[$i]->remise_percent;
1007  $line->fk_product = $object->lines[$i]->fk_product;
1008  $line->info_bits = $object->lines[$i]->info_bits;
1009  $line->product_type = $object->lines[$i]->product_type;
1010  $line->rang = $object->lines[$i]->rang;
1011  $line->special_code = $object->lines[$i]->special_code;
1012  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1013  $line->fk_unit = $object->lines[$i]->fk_unit;
1014  $line->date_start = $object->lines[$i]->date_start;
1015  $line->date_end = $object->lines[$i]->date_end;
1016 
1017  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1018  $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);
1019  $line->pa_ht = $marginInfos[0];
1020 
1021  // get extrafields from original line
1022  $object->lines[$i]->fetch_optionals($object->lines[$i]->rowid);
1023  foreach($object->lines[$i]->array_options as $options_key => $value)
1024  $line->array_options[$options_key] = $value;
1025 
1026  $this->lines[$i] = $line;
1027  }
1028 
1029  $this->socid = $object->socid;
1030  $this->fk_project = $object->fk_project;
1031  $this->cond_reglement_id = $object->cond_reglement_id;
1032  $this->mode_reglement_id = $object->mode_reglement_id;
1033  $this->availability_id = $object->availability_id;
1034  $this->demand_reason_id = $object->demand_reason_id;
1035  $this->date_livraison = $object->date_livraison;
1036  $this->fk_delivery_address = $object->fk_delivery_address;
1037  $this->contact_id = $object->contactid;
1038  $this->ref_client = $object->ref_client;
1039  $this->note_private = $object->note_private;
1040  $this->note_public = $object->note_public;
1041 
1042  $this->origin = $object->element;
1043  $this->origin_id = $object->id;
1044 
1045  // get extrafields from original line
1046  $object->fetch_optionals($object->id);
1047  foreach($object->array_options as $options_key => $value)
1048  $this->array_options[$options_key] = $value;
1049 
1050  // Possibility to add external linked objects with hooks
1051  $this->linked_objects[$this->origin] = $this->origin_id;
1052  if (! empty($object->other_linked_objects) && is_array($object->other_linked_objects))
1053  {
1054  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1055  }
1056 
1057  $ret = $this->create($user);
1058 
1059  if ($ret > 0)
1060  {
1061  // Actions hooked (by external module)
1062  $hookmanager->initHooks(array('invoicedao'));
1063 
1064  $parameters=array('objFrom'=>$object);
1065  $action='';
1066  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1067  if ($reshook < 0) $error++;
1068 
1069  if (! $error)
1070  {
1071  return 1;
1072  }
1073  else return -1;
1074  }
1075  else return -1;
1076  }
1077 
1084  function getDirectExternalLink($withpicto=0)
1085  {
1086  global $dolibarr_main_url_root;
1087 
1088  // Define $urlwithroot
1089  $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
1090  $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1091  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1092 
1093  // TODO Read into ecmfile table to get entry and hash exists (PS: If not found, add it)
1094  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1095  $ecmfile=new EcmFiles($this->db);
1096  //$result = $ecmfile->get();
1097 
1098  $hashp='todo';
1099  return '<a href="'.$urlwithroot.'/document.php?modulepart=invoice&hashp='.$hashp.'" target="_download" rel="noindex, nofollow">'.$this->ref.'</a>';
1100  }
1101 
1115  function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $addlinktonotes=0, $save_lastsearch_value=-1)
1116  {
1117  global $langs, $conf, $user, $form;
1118 
1119  if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
1120 
1121  $result='';
1122 
1123  if ($option == 'withdraw') $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id;
1124  else $url = DOL_URL_ROOT.'/compta/facture/card.php?facid='.$this->id;
1125 
1126  if ($short) return $url;
1127 
1128  if ($option !== 'nolink')
1129  {
1130  // Add param to save lastsearch_values or not
1131  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1132  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1133  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1134  }
1135 
1136  $picto='bill';
1137  if ($this->type == self::TYPE_REPLACEMENT) $picto.='r'; // Replacement invoice
1138  if ($this->type == self::TYPE_CREDIT_NOTE) $picto.='a'; // Credit note
1139  if ($this->type == self::TYPE_DEPOSIT) $picto.='d'; // Deposit invoice
1140  $label='';
1141 
1142  if ($user->rights->facture->lire) {
1143  $label = '<u>' . $langs->trans("ShowInvoice") . '</u>';
1144  if (! empty($this->ref))
1145  $label .= '<br><b>'.$langs->trans('Ref') . ':</b> ' . $this->ref;
1146  if (! empty($this->ref_client))
1147  $label .= '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_client;
1148  if (! empty($this->total_ht))
1149  $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1150  if (! empty($this->total_tva))
1151  $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1152  if (! empty($this->total_tva))
1153  $label.= '<br><b>' . $langs->trans('LT1') . ':</b> ' . price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
1154  if (! empty($this->total_tva))
1155  $label.= '<br><b>' . $langs->trans('LT2') . ':</b> ' . price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
1156  if (! empty($this->total_ttc))
1157  $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1158  if ($this->type == self::TYPE_REPLACEMENT) $label=$langs->transnoentitiesnoconv("ShowInvoiceReplace").': '.$this->ref;
1159  if ($this->type == self::TYPE_CREDIT_NOTE) $label=$langs->transnoentitiesnoconv("ShowInvoiceAvoir").': '.$this->ref;
1160  if ($this->type == self::TYPE_DEPOSIT) $label=$langs->transnoentitiesnoconv("ShowInvoiceDeposit").': '.$this->ref;
1161  if ($this->type == self::TYPE_SITUATION) $label=$langs->transnoentitiesnoconv("ShowInvoiceSituation").': '.$this->ref;
1162  if ($moretitle) $label.=' - '.$moretitle;
1163  }
1164 
1165  $linkclose='';
1166  if (empty($notooltip) && $user->rights->facture->lire)
1167  {
1168  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1169  {
1170  $label=$langs->trans("ShowInvoice");
1171  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1172  }
1173  $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1174  $linkclose.=' class="classfortooltip"';
1175  }
1176 
1177  $linkstart='<a href="'.$url.'"';
1178  $linkstart.=$linkclose.'>';
1179  $linkend='</a>';
1180 
1181  $result .= $linkstart;
1182  if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1183  if ($withpicto != 2) $result.= ($max?dol_trunc($this->ref,$max):$this->ref);
1184  $result .= $linkend;
1185 
1186  if ($addlinktonotes)
1187  {
1188  $txttoshow=($user->societe_id>0?$this->note_public:$this->note_private);
1189  if ($txttoshow)
1190  {
1191  $notetoshow=$langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow,1);
1192  $result.=' <span class="note inline-block">';
1193  $result.='<a href="'.DOL_URL_ROOT.'/compta/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">'.img_picto('','object_generic').'</a>';
1194  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1195  //$result.='</a>';
1196  $result.='</span>';
1197  }
1198  }
1199 
1200  return $result;
1201  }
1202 
1213  function fetch($rowid, $ref='', $ref_ext='', $ref_int='', $fetch_situation=false)
1214  {
1215  global $conf;
1216 
1217  if (empty($rowid) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
1218 
1219  $sql = 'SELECT f.rowid,f.facnumber,f.ref_client,f.ref_ext,f.ref_int,f.type,f.fk_soc,f.amount';
1220  $sql.= ', f.tva, f.localtax1, f.localtax2, f.total, f.total_ttc, f.revenuestamp';
1221  $sql.= ', f.remise_percent, f.remise_absolue, f.remise';
1222  $sql.= ', f.datef as df, f.date_pointoftax';
1223  $sql.= ', f.date_lim_reglement as dlr';
1224  $sql.= ', f.datec as datec';
1225  $sql.= ', f.date_valid as datev';
1226  $sql.= ', f.tms as datem';
1227  $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';
1228  $sql.= ', f.fk_facture_source';
1229  $sql.= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet, f.extraparams';
1230  $sql.= ', f.situation_cycle_ref, f.situation_counter, f.situation_final';
1231  $sql.= ', f.fk_account';
1232  $sql.= ", f.fk_multicurrency, f.multicurrency_code, f.multicurrency_tx, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc";
1233  $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1234  $sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
1235  $sql.= ', f.fk_incoterms, f.location_incoterms';
1236  $sql.= ", i.libelle as libelle_incoterms";
1237  $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as f';
1238  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid AND c.entity IN (' . getEntity('c_payment_term').')';
1239  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')';
1240  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON f.fk_incoterms = i.rowid';
1241  $sql.= ' WHERE f.entity IN ('.getEntity('facture').')';
1242  if ($rowid) $sql.= " AND f.rowid=".$rowid;
1243  if ($ref) $sql.= " AND f.facnumber='".$this->db->escape($ref)."'";
1244  if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
1245  if ($ref_int) $sql.= " AND f.ref_int='".$this->db->escape($ref_int)."'";
1246 
1247  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1248  $result = $this->db->query($sql);
1249  if ($result)
1250  {
1251  if ($this->db->num_rows($result))
1252  {
1253  $obj = $this->db->fetch_object($result);
1254 
1255  $this->id = $obj->rowid;
1256  $this->ref = $obj->facnumber;
1257  $this->ref_client = $obj->ref_client;
1258  $this->ref_ext = $obj->ref_ext;
1259  $this->ref_int = $obj->ref_int;
1260  $this->type = $obj->type;
1261  $this->date = $this->db->jdate($obj->df);
1262  $this->date_pointoftax = $this->db->jdate($obj->date_pointoftax);
1263  $this->date_creation = $this->db->jdate($obj->datec);
1264  $this->date_validation = $this->db->jdate($obj->datev);
1265  $this->datem = $this->db->jdate($obj->datem);
1266  $this->remise_percent = $obj->remise_percent;
1267  $this->remise_absolue = $obj->remise_absolue;
1268  $this->total_ht = $obj->total;
1269  $this->total_tva = $obj->tva;
1270  $this->total_localtax1 = $obj->localtax1;
1271  $this->total_localtax2 = $obj->localtax2;
1272  $this->total_ttc = $obj->total_ttc;
1273  $this->revenuestamp = $obj->revenuestamp;
1274  $this->paye = $obj->paye;
1275  $this->close_code = $obj->close_code;
1276  $this->close_note = $obj->close_note;
1277  $this->socid = $obj->fk_soc;
1278  $this->statut = $obj->fk_statut;
1279  $this->date_lim_reglement = $this->db->jdate($obj->dlr);
1280  $this->mode_reglement_id = $obj->fk_mode_reglement;
1281  $this->mode_reglement_code = $obj->mode_reglement_code;
1282  $this->mode_reglement = $obj->mode_reglement_libelle;
1283  $this->cond_reglement_id = $obj->fk_cond_reglement;
1284  $this->cond_reglement_code = $obj->cond_reglement_code;
1285  $this->cond_reglement = $obj->cond_reglement_libelle;
1286  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1287  $this->fk_account = ($obj->fk_account>0)?$obj->fk_account:null;
1288  $this->fk_project = $obj->fk_projet;
1289  $this->fk_facture_source = $obj->fk_facture_source;
1290  $this->note = $obj->note_private; // deprecated
1291  $this->note_private = $obj->note_private;
1292  $this->note_public = $obj->note_public;
1293  $this->user_author = $obj->fk_user_author;
1294  $this->user_valid = $obj->fk_user_valid;
1295  $this->modelpdf = $obj->model_pdf;
1296  $this->last_main_doc = $obj->last_main_doc;
1297  $this->situation_cycle_ref = $obj->situation_cycle_ref;
1298  $this->situation_counter = $obj->situation_counter;
1299  $this->situation_final = $obj->situation_final;
1300  $this->extraparams = (array) json_decode($obj->extraparams, true);
1301 
1302  //Incoterms
1303  $this->fk_incoterms = $obj->fk_incoterms;
1304  $this->location_incoterms = $obj->location_incoterms;
1305  $this->libelle_incoterms = $obj->libelle_incoterms;
1306 
1307  // Multicurrency
1308  $this->fk_multicurrency = $obj->fk_multicurrency;
1309  $this->multicurrency_code = $obj->multicurrency_code;
1310  $this->multicurrency_tx = $obj->multicurrency_tx;
1311  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1312  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1313  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1314 
1315  if ($this->type == self::TYPE_SITUATION && $fetch_situation)
1316  {
1318  }
1319 
1320  if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1321 
1322  // Retrieve all extrafield for invoice
1323  // fetch optionals attributes and labels
1324  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1325  $extrafields=new ExtraFields($this->db);
1326  $extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
1327  $this->fetch_optionals($this->id,$extralabels);
1328 
1329  /*
1330  * Lines
1331  */
1332 
1333  $this->lines = array();
1334 
1335  $result=$this->fetch_lines();
1336  if ($result < 0)
1337  {
1338  $this->error=$this->db->error();
1339  return -3;
1340  }
1341  return 1;
1342  }
1343  else
1344  {
1345  $this->error='Bill with id='.$rowid.' or ref='.$ref.' or ref_ext='.$ref_ext.' not found';
1346  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
1347  return 0;
1348  }
1349  }
1350  else
1351  {
1352  $this->error=$this->db->error();
1353  return -1;
1354  }
1355  }
1356 
1357 
1363  function fetch_lines()
1364  {
1365  $this->lines=array();
1366 
1367  $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,';
1368  $sql.= ' l.situation_percent, l.fk_prev_id,';
1369  $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,';
1370  $sql.= ' l.rang, l.special_code,';
1371  $sql.= ' l.date_start as date_start, l.date_end as date_end,';
1372  $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,';
1373  $sql.= ' l.fk_unit,';
1374  $sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1375  $sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
1376  $sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet as l';
1377  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
1378  $sql.= ' WHERE l.fk_facture = '.$this->id;
1379  $sql.= ' ORDER BY l.rang, l.rowid';
1380 
1381  dol_syslog(get_class($this).'::fetch_lines', LOG_DEBUG);
1382  $result = $this->db->query($sql);
1383  if ($result)
1384  {
1385  $num = $this->db->num_rows($result);
1386  $i = 0;
1387  while ($i < $num)
1388  {
1389  $objp = $this->db->fetch_object($result);
1390  $line = new FactureLigne($this->db);
1391 
1392  $line->id = $objp->rowid;
1393  $line->rowid = $objp->rowid; // deprecated
1394  $line->fk_facture = $objp->fk_facture;
1395  $line->label = $objp->custom_label; // deprecated
1396  $line->desc = $objp->description; // Description line
1397  $line->description = $objp->description; // Description line
1398  $line->product_type = $objp->product_type; // Type of line
1399  $line->ref = $objp->product_ref; // Ref product
1400  $line->product_ref = $objp->product_ref; // Ref product
1401  $line->libelle = $objp->product_label; // TODO deprecated
1402  $line->product_label = $objp->product_label; // Label product
1403  $line->product_desc = $objp->product_desc; // Description product
1404  $line->fk_product_type = $objp->fk_product_type; // Type of product
1405  $line->qty = $objp->qty;
1406  $line->subprice = $objp->subprice;
1407 
1408  $line->vat_src_code = $objp->vat_src_code;
1409  $line->tva_tx = $objp->tva_tx;
1410  $line->localtax1_tx = $objp->localtax1_tx;
1411  $line->localtax2_tx = $objp->localtax2_tx;
1412  $line->localtax1_type = $objp->localtax1_type;
1413  $line->localtax2_type = $objp->localtax2_type;
1414  $line->remise_percent = $objp->remise_percent;
1415  $line->fk_remise_except = $objp->fk_remise_except;
1416  $line->fk_product = $objp->fk_product;
1417  $line->date_start = $this->db->jdate($objp->date_start);
1418  $line->date_end = $this->db->jdate($objp->date_end);
1419  $line->date_start = $this->db->jdate($objp->date_start);
1420  $line->date_end = $this->db->jdate($objp->date_end);
1421  $line->info_bits = $objp->info_bits;
1422  $line->total_ht = $objp->total_ht;
1423  $line->total_tva = $objp->total_tva;
1424  $line->total_localtax1 = $objp->total_localtax1;
1425  $line->total_localtax2 = $objp->total_localtax2;
1426  $line->total_ttc = $objp->total_ttc;
1427  $line->code_ventilation = $objp->fk_code_ventilation;
1428  $line->fk_fournprice = $objp->fk_fournprice;
1429  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1430  $line->pa_ht = $marginInfos[0];
1431  $line->marge_tx = $marginInfos[1];
1432  $line->marque_tx = $marginInfos[2];
1433  $line->rang = $objp->rang;
1434  $line->special_code = $objp->special_code;
1435  $line->fk_parent_line = $objp->fk_parent_line;
1436  $line->situation_percent= $objp->situation_percent;
1437  $line->fk_prev_id = $objp->fk_prev_id;
1438  $line->fk_unit = $objp->fk_unit;
1439 
1440  // Multicurrency
1441  $line->fk_multicurrency = $objp->fk_multicurrency;
1442  $line->multicurrency_code = $objp->multicurrency_code;
1443  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1444  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1445  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1446  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1447 
1448  // TODO Fetch optional like done in fetch line of facture_rec ?
1449 
1450  $this->lines[$i] = $line;
1451 
1452  $i++;
1453  }
1454  $this->db->free($result);
1455  return 1;
1456  }
1457  else
1458  {
1459  $this->error=$this->db->error();
1460  return -3;
1461  }
1462  }
1463 
1470  {
1471  global $conf;
1472 
1473  $this->tab_previous_situation_invoice = array();
1474  $this->tab_next_situation_invoice = array();
1475 
1476  $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';
1477 
1478  dol_syslog(get_class($this).'::fetchPreviousNextSituationInvoice ', LOG_DEBUG);
1479  $result = $this->db->query($sql);
1480  if ($result && $this->db->num_rows($result) > 0)
1481  {
1482  while ($objp = $this->db->fetch_object($result))
1483  {
1484  $invoice = new Facture($this->db);
1485  if ($invoice->fetch($objp->rowid) > 0)
1486  {
1487  if ($objp->situation_counter < $this->situation_counter) $this->tab_previous_situation_invoice[] = $invoice;
1488  else $this->tab_next_situation_invoice[] = $invoice;
1489  }
1490  }
1491  }
1492 
1493  }
1494 
1502  function update($user=null, $notrigger=0)
1503  {
1504  $error=0;
1505 
1506  // Clean parameters
1507  if (empty($this->type)) $this->type= self::TYPE_STANDARD;
1508  if (isset($this->facnumber)) $this->facnumber=trim($this->ref);
1509  if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1510  if (isset($this->increment)) $this->increment=trim($this->increment);
1511  if (isset($this->close_code)) $this->close_code=trim($this->close_code);
1512  if (isset($this->close_note)) $this->close_note=trim($this->close_note);
1513  if (isset($this->note) || isset($this->note_private)) $this->note=(isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
1514  if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1515  if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1516  if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1517  if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1518  if (empty($this->situation_cycle_ref)) {
1519  $this->situation_cycle_ref = 'null';
1520  }
1521 
1522  if (empty($this->situation_counter)) {
1523  $this->situation_counter = 'null';
1524  }
1525 
1526  if (empty($this->situation_final)) {
1527  $this->situation_final = '0';
1528  }
1529 
1530  // Check parameters
1531  // Put here code to add control on parameters values
1532 
1533  // Update request
1534  $sql = "UPDATE ".MAIN_DB_PREFIX."facture SET";
1535 
1536  $sql.= " facnumber=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1537  $sql.= " type=".(isset($this->type)?$this->type:"null").",";
1538  $sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1539  $sql.= " increment=".(isset($this->increment)?"'".$this->db->escape($this->increment)."'":"null").",";
1540  $sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1541  $sql.= " datec=".(strval($this->date_creation)!='' ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1542  $sql.= " datef=".(strval($this->date)!='' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1543  $sql.= " date_pointoftax=".(strval($this->date_pointoftax)!='' ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null').",";
1544  $sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1545  $sql.= " paye=".(isset($this->paye)?$this->paye:"null").",";
1546  $sql.= " remise_percent=".(isset($this->remise_percent)?$this->remise_percent:"null").",";
1547  $sql.= " remise_absolue=".(isset($this->remise_absolue)?$this->remise_absolue:"null").",";
1548  $sql.= " close_code=".(isset($this->close_code)?"'".$this->db->escape($this->close_code)."'":"null").",";
1549  $sql.= " close_note=".(isset($this->close_note)?"'".$this->db->escape($this->close_note)."'":"null").",";
1550  $sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1551  $sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1552  $sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1553  $sql.= " total=".(isset($this->total_ht)?$this->total_ht:"null").",";
1554  $sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1555  $sql.= " revenuestamp=".((isset($this->revenuestamp) && $this->revenuestamp != '')?$this->revenuestamp:"null").",";
1556  $sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1557  $sql.= " fk_user_author=".(isset($this->user_author)?$this->user_author:"null").",";
1558  $sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->fk_user_valid:"null").",";
1559  $sql.= " fk_facture_source=".(isset($this->fk_facture_source)?$this->fk_facture_source:"null").",";
1560  $sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1561  $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
1562  $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
1563  $sql.= " date_lim_reglement=".(strval($this->date_lim_reglement)!='' ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'null').",";
1564  $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1565  $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1566  $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1567  $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null");
1568  $sql.= ", situation_cycle_ref=".$this->situation_cycle_ref;
1569  $sql.= ", situation_counter=".$this->situation_counter;
1570  $sql.= ", situation_final=".$this->situation_final;
1571 
1572  $sql.= " WHERE rowid=".$this->id;
1573 
1574  $this->db->begin();
1575 
1576  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1577  $resql = $this->db->query($sql);
1578  if (! $resql) {
1579  $error++; $this->errors[]="Error ".$this->db->lasterror();
1580  }
1581 
1582  if (! $error)
1583  {
1584  if (! $notrigger)
1585  {
1586  // Call trigger
1587  $result=$this->call_trigger('BILL_MODIFY',$user);
1588  if ($result < 0) $error++;
1589  // End call triggers
1590  }
1591  }
1592 
1593  // Commit or rollback
1594  if ($error)
1595  {
1596  foreach($this->errors as $errmsg)
1597  {
1598  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1599  $this->error.=($this->error?', '.$errmsg:$errmsg);
1600  }
1601  $this->db->rollback();
1602  return -1*$error;
1603  }
1604  else
1605  {
1606  $this->db->commit();
1607  return 1;
1608  }
1609  }
1610 
1611 
1618  function insert_discount($idremise)
1619  {
1620  global $langs;
1621 
1622  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1623  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1624 
1625  $this->db->begin();
1626 
1627  $remise=new DiscountAbsolute($this->db);
1628  $result=$remise->fetch($idremise);
1629 
1630  if ($result > 0)
1631  {
1632  if ($remise->fk_facture) // Protection against multiple submission
1633  {
1634  $this->error=$langs->trans("ErrorDiscountAlreadyUsed");
1635  $this->db->rollback();
1636  return -5;
1637  }
1638 
1639  $facligne=new FactureLigne($this->db);
1640  $facligne->fk_facture=$this->id;
1641  $facligne->fk_remise_except=$remise->id;
1642  $facligne->desc=$remise->description; // Description ligne
1643  $facligne->vat_src_code=$remise->vat_src_code;
1644  $facligne->tva_tx=$remise->tva_tx;
1645  $facligne->subprice = -$remise->amount_ht;
1646  $facligne->fk_product=0; // Id produit predefini
1647  $facligne->qty=1;
1648  $facligne->remise_percent=0;
1649  $facligne->rang=-1;
1650  $facligne->info_bits=2;
1651 
1652  // Get buy/cost price of invoice that is source of discount
1653  if ($remise->fk_facture_source > 0)
1654  {
1655  $srcinvoice=new Facture($this->db);
1656  $srcinvoice->fetch($remise->fk_facture_source);
1657  $totalcostpriceofinvoice=0;
1658  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1659  $formmargin=new FormMargin($this->db);
1660  $arraytmp=$formmargin->getMarginInfosArray($srcinvoice, false);
1661  $facligne->pa_ht = $arraytmp['pa_total'];
1662  }
1663 
1664  $facligne->total_ht = -$remise->amount_ht;
1665  $facligne->total_tva = -$remise->amount_tva;
1666  $facligne->total_ttc = -$remise->amount_ttc;
1667 
1668  $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1669  $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1670  $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1671  $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1672 
1673  $lineid=$facligne->insert();
1674  if ($lineid > 0)
1675  {
1676  $result=$this->update_price(1);
1677  if ($result > 0)
1678  {
1679  // Create link between discount and invoice line
1680  $result=$remise->link_to_invoice($lineid,0);
1681  if ($result < 0)
1682  {
1683  $this->error=$remise->error;
1684  $this->db->rollback();
1685  return -4;
1686  }
1687 
1688  $this->db->commit();
1689  return 1;
1690  }
1691  else
1692  {
1693  $this->error=$facligne->error;
1694  $this->db->rollback();
1695  return -1;
1696  }
1697  }
1698  else
1699  {
1700  $this->error=$facligne->error;
1701  $this->db->rollback();
1702  return -2;
1703  }
1704  }
1705  else
1706  {
1707  $this->db->rollback();
1708  return -3;
1709  }
1710  }
1711 
1719  function set_ref_client($ref_client, $notrigger=0)
1720  {
1721  global $user;
1722 
1723  $error=0;
1724 
1725  $this->db->begin();
1726 
1727  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
1728  if (empty($ref_client))
1729  $sql .= ' SET ref_client = NULL';
1730  else
1731  $sql .= ' SET ref_client = \''.$this->db->escape($ref_client).'\'';
1732  $sql .= ' WHERE rowid = '.$this->id;
1733 
1734  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
1735  $resql=$this->db->query($sql);
1736  if (!$resql)
1737  {
1738  $this->errors[]=$this->db->error();
1739  $error++;
1740  }
1741 
1742  if (! $error)
1743  {
1744  $this->ref_client = $ref_client;
1745  }
1746 
1747  if (! $notrigger && empty($error))
1748  {
1749  // Call trigger
1750  $result=$this->call_trigger('BILL_MODIFY',$user);
1751  if ($result < 0) $error++;
1752  // End call triggers
1753  }
1754 
1755  if (! $error)
1756  {
1757 
1758  $this->ref_client = $ref_client;
1759 
1760  $this->db->commit();
1761  return 1;
1762  }
1763  else
1764  {
1765  foreach($this->errors as $errmsg)
1766  {
1767  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1768  $this->error.=($this->error?', '.$errmsg:$errmsg);
1769  }
1770  $this->db->rollback();
1771  return -1*$error;
1772  }
1773  }
1774 
1783  function delete($user, $notrigger=0, $idwarehouse=-1)
1784  {
1785  global $langs,$conf;
1786  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1787 
1788  $rowid=$this->id;
1789 
1790  dol_syslog(get_class($this)."::delete rowid=".$rowid.", ref=".$this->ref.", thirdparty=".$this->thirdparty->name, LOG_DEBUG);
1791 
1792  // Test to avoid invoice deletion (allowed if draft)
1793  $test = $this->is_erasable();
1794 
1795  if ($test <= 0) return 0;
1796 
1797  $error=0;
1798 
1799  $this->db->begin();
1800 
1801  if (! $error && ! $notrigger)
1802  {
1803  // Call trigger
1804  $result=$this->call_trigger('BILL_DELETE',$user);
1805  if ($result < 0) $error++;
1806  // End call triggers
1807  }
1808 
1809  // Removed extrafields
1810  if (! $error) {
1811  $result=$this->deleteExtraFields();
1812  if ($result < 0)
1813  {
1814  $error++;
1815  dol_syslog(get_class($this)."::delete error deleteExtraFields ".$this->error, LOG_ERR);
1816  }
1817  }
1818 
1819  if (! $error)
1820  {
1821  // Delete linked object
1822  $res = $this->deleteObjectLinked();
1823  if ($res < 0) $error++;
1824  }
1825 
1826  if (! $error)
1827  {
1828  // If invoice was converted into a discount not yet consumed, we remove discount
1829  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1830  $sql.= ' WHERE fk_facture_source = '.$rowid;
1831  $sql.= ' AND fk_facture_line IS NULL';
1832  $resql=$this->db->query($sql);
1833 
1834  // If invoice has consumned discounts
1835  $this->fetch_lines();
1836  $list_rowid_det=array();
1837  foreach($this->lines as $key => $invoiceline)
1838  {
1839  $list_rowid_det[]=$invoiceline->rowid;
1840  }
1841 
1842  // Consumned discounts are freed
1843  if (count($list_rowid_det))
1844  {
1845  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1846  $sql.= ' SET fk_facture = NULL, fk_facture_line = NULL';
1847  $sql.= ' WHERE fk_facture_line IN ('.join(',',$list_rowid_det).')';
1848 
1849  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1850  if (! $this->db->query($sql))
1851  {
1852  $this->error=$this->db->error()." sql=".$sql;
1853  $this->db->rollback();
1854  return -5;
1855  }
1856  }
1857 
1858  // If we decrement stock on invoice validation, we increment
1859  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL) && $idwarehouse!=-1)
1860  {
1861  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1862  $langs->load("agenda");
1863 
1864  $num=count($this->lines);
1865  for ($i = 0; $i < $num; $i++)
1866  {
1867  if ($this->lines[$i]->fk_product > 0)
1868  {
1869  $mouvP = new MouvementStock($this->db);
1870  $mouvP->origin = &$this;
1871  // We decrease stock for product
1872  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));
1873  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
1874  }
1875  }
1876  }
1877 
1878 
1879  // Delete invoice line
1880  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facturedet WHERE fk_facture = '.$rowid;
1881 
1882  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1883 
1884  if ($this->db->query($sql) && $this->delete_linked_contact())
1885  {
1886  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture WHERE rowid = '.$rowid;
1887 
1888  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1889 
1890  $resql=$this->db->query($sql);
1891  if ($resql)
1892  {
1893  // On efface le repertoire de pdf provisoire
1894  $ref = dol_sanitizeFileName($this->ref);
1895  if ($conf->facture->dir_output && !empty($this->ref))
1896  {
1897  $dir = $conf->facture->dir_output . "/" . $ref;
1898  $file = $conf->facture->dir_output . "/" . $ref . "/" . $ref . ".pdf";
1899  if (file_exists($file)) // We must delete all files before deleting directory
1900  {
1901  $ret=dol_delete_preview($this);
1902 
1903  if (! dol_delete_file($file,0,0,0,$this)) // For triggers
1904  {
1905  $this->error=$langs->trans("ErrorCanNotDeleteFile",$file);
1906  $this->db->rollback();
1907  return 0;
1908  }
1909  }
1910  if (file_exists($dir))
1911  {
1912  if (! dol_delete_dir_recursive($dir)) // For remove dir and meta
1913  {
1914  $this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
1915  $this->db->rollback();
1916  return 0;
1917  }
1918  }
1919  }
1920 
1921  $this->db->commit();
1922  return 1;
1923  }
1924  else
1925  {
1926  $this->error=$this->db->lasterror()." sql=".$sql;
1927  $this->db->rollback();
1928  return -6;
1929  }
1930  }
1931  else
1932  {
1933  $this->error=$this->db->lasterror()." sql=".$sql;
1934  $this->db->rollback();
1935  return -4;
1936  }
1937  }
1938  else
1939  {
1940  $this->db->rollback();
1941  return -2;
1942  }
1943  }
1944 
1954  function set_paid($user, $close_code='', $close_note='')
1955  {
1956  $error=0;
1957 
1958  if ($this->paye != 1)
1959  {
1960  $this->db->begin();
1961 
1962  dol_syslog(get_class($this)."::set_paid rowid=".$this->id, LOG_DEBUG);
1963  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture SET';
1964  $sql.= ' fk_statut='.self::STATUS_CLOSED;
1965  if (! $close_code) $sql.= ', paye=1';
1966  if ($close_code) $sql.= ", close_code='".$this->db->escape($close_code)."'";
1967  if ($close_note) $sql.= ", close_note='".$this->db->escape($close_note)."'";
1968  $sql.= ' WHERE rowid = '.$this->id;
1969 
1970  dol_syslog(get_class($this)."::set_paid", LOG_DEBUG);
1971  $resql = $this->db->query($sql);
1972  if ($resql)
1973  {
1974  // Call trigger
1975  $result=$this->call_trigger('BILL_PAYED',$user);
1976  if ($result < 0) $error++;
1977  // End call triggers
1978  }
1979  else
1980  {
1981  $error++;
1982  $this->error=$this->db->lasterror();
1983  }
1984 
1985  if (! $error)
1986  {
1987  $this->db->commit();
1988  return 1;
1989  }
1990  else
1991  {
1992  $this->db->rollback();
1993  return -1;
1994  }
1995  }
1996  else
1997  {
1998  return 0;
1999  }
2000  }
2001 
2002 
2011  function set_unpaid($user)
2012  {
2013  $error=0;
2014 
2015  $this->db->begin();
2016 
2017  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
2018  $sql.= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null';
2019  $sql.= ' WHERE rowid = '.$this->id;
2020 
2021  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
2022  $resql = $this->db->query($sql);
2023  if ($resql)
2024  {
2025  // Call trigger
2026  $result=$this->call_trigger('BILL_UNPAYED',$user);
2027  if ($result < 0) $error++;
2028  // End call triggers
2029  }
2030  else
2031  {
2032  $error++;
2033  $this->error=$this->db->error();
2034  dol_print_error($this->db);
2035  }
2036 
2037  if (! $error)
2038  {
2039  $this->db->commit();
2040  return 1;
2041  }
2042  else
2043  {
2044  $this->db->rollback();
2045  return -1;
2046  }
2047  }
2048 
2049 
2060  function set_canceled($user, $close_code='', $close_note='')
2061  {
2062 
2063  dol_syslog(get_class($this)."::set_canceled rowid=".$this->id, LOG_DEBUG);
2064 
2065  $this->db->begin();
2066 
2067  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture SET';
2068  $sql.= ' fk_statut='.self::STATUS_ABANDONED;
2069  if ($close_code) $sql.= ", close_code='".$this->db->escape($close_code)."'";
2070  if ($close_note) $sql.= ", close_note='".$this->db->escape($close_note)."'";
2071  $sql.= ' WHERE rowid = '.$this->id;
2072 
2073  $resql = $this->db->query($sql);
2074  if ($resql)
2075  {
2076  // On desaffecte de la facture les remises liees
2077  // car elles n'ont pas ete utilisees vu que la facture est abandonnee.
2078  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2079  $sql.= ' SET fk_facture = NULL';
2080  $sql.= ' WHERE fk_facture = '.$this->id;
2081 
2082  $resql=$this->db->query($sql);
2083  if ($resql)
2084  {
2085  // Call trigger
2086  $result=$this->call_trigger('BILL_CANCEL',$user);
2087  if ($result < 0)
2088  {
2089  $this->db->rollback();
2090  return -1;
2091  }
2092  // End call triggers
2093 
2094  $this->db->commit();
2095  return 1;
2096  }
2097  else
2098  {
2099  $this->error=$this->db->error()." sql=".$sql;
2100  $this->db->rollback();
2101  return -1;
2102  }
2103  }
2104  else
2105  {
2106  $this->error=$this->db->error()." sql=".$sql;
2107  $this->db->rollback();
2108  return -2;
2109  }
2110  }
2111 
2122  function validate($user, $force_number='', $idwarehouse=0, $notrigger=0)
2123  {
2124  global $conf,$langs;
2125  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2126 
2127  $now=dol_now();
2128 
2129  $error=0;
2130  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
2131 
2132  // Force to have object complete for checks
2133  $this->fetch_thirdparty();
2134  $this->fetch_lines();
2135 
2136  // Check parameters
2137  if (! $this->brouillon)
2138  {
2139  dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
2140  return 0;
2141  }
2142  if (count($this->lines) <= 0)
2143  {
2144  $langs->load("errors");
2145  $this->error=$langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
2146  return -1;
2147  }
2148  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->facture->creer))
2149  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->facture->invoice_advance->validate)))
2150  {
2151  $this->error='Permission denied';
2152  dol_syslog(get_class($this)."::validate ".$this->error.' MAIN_USE_ADVANCED_PERMS='.$conf->global->MAIN_USE_ADVANCED_PERMS, LOG_ERR);
2153  return -1;
2154  }
2155 
2156  $this->db->begin();
2157 
2158  // Check parameters
2159  if ($this->type == self::TYPE_REPLACEMENT) // si facture de remplacement
2160  {
2161  // Controle que facture source connue
2162  if ($this->fk_facture_source <= 0)
2163  {
2164  $this->error=$langs->trans("ErrorFieldRequired",$langs->trans("InvoiceReplacement"));
2165  $this->db->rollback();
2166  return -10;
2167  }
2168 
2169  // Charge la facture source a remplacer
2170  $facreplaced=new Facture($this->db);
2171  $result=$facreplaced->fetch($this->fk_facture_source);
2172  if ($result <= 0)
2173  {
2174  $this->error=$langs->trans("ErrorBadInvoice");
2175  $this->db->rollback();
2176  return -11;
2177  }
2178 
2179  // Controle que facture source non deja remplacee par une autre
2180  $idreplacement=$facreplaced->getIdReplacingInvoice('validated');
2181  if ($idreplacement && $idreplacement != $this->id)
2182  {
2183  $facreplacement=new Facture($this->db);
2184  $facreplacement->fetch($idreplacement);
2185  $this->error=$langs->trans("ErrorInvoiceAlreadyReplaced",$facreplaced->ref,$facreplacement->ref);
2186  $this->db->rollback();
2187  return -12;
2188  }
2189 
2190  $result=$facreplaced->set_canceled($user, self::CLOSECODE_REPLACED, '');
2191  if ($result < 0)
2192  {
2193  $this->error=$facreplaced->error;
2194  $this->db->rollback();
2195  return -13;
2196  }
2197  }
2198 
2199  // Define new ref
2200  if ($force_number)
2201  {
2202  $num = $force_number;
2203  }
2204  else if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) // empty should not happened, but when it occurs, the test save life
2205  {
2206  if (! empty($conf->global->FAC_FORCE_DATE_VALIDATION)) // If option enabled, we force invoice date
2207  {
2208  $this->date=dol_now();
2209  $this->date_lim_reglement=$this->calculate_date_lim_reglement();
2210  }
2211  $num = $this->getNextNumRef($this->thirdparty);
2212  }
2213  else
2214  {
2215  $num = $this->ref;
2216  }
2217  $this->newref = $num;
2218 
2219  if ($num)
2220  {
2221  $this->update_price(1);
2222 
2223  // Validate
2224  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
2225  $sql.= " SET facnumber='".$num."', fk_statut = ".self::STATUS_VALIDATED.", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
2226  if (! empty($conf->global->FAC_FORCE_DATE_VALIDATION)) // If option enabled, we force invoice date
2227  {
2228  $sql.= ", datef='".$this->db->idate($this->date)."'";
2229  $sql.= ", date_lim_reglement='".$this->db->idate($this->date_lim_reglement)."'";
2230  }
2231  $sql.= ' WHERE rowid = '.$this->id;
2232 
2233  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
2234  $resql=$this->db->query($sql);
2235  if (! $resql)
2236  {
2237  dol_print_error($this->db);
2238  $error++;
2239  }
2240 
2241  // On verifie si la facture etait une provisoire
2242  if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref)))
2243  {
2244  // La verif qu'une remise n'est pas utilisee 2 fois est faite au moment de l'insertion de ligne
2245  }
2246 
2247  if (! $error)
2248  {
2249  // Define third party as a customer
2250  $result=$this->thirdparty->set_as_client();
2251 
2252  // Si active on decremente le produit principal et ses composants a la validation de facture
2253  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL) && $idwarehouse > 0)
2254  {
2255  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2256  $langs->load("agenda");
2257 
2258  // Loop on each line
2259  $cpt=count($this->lines);
2260  for ($i = 0; $i < $cpt; $i++)
2261  {
2262  if ($this->lines[$i]->fk_product > 0)
2263  {
2264  $mouvP = new MouvementStock($this->db);
2265  $mouvP->origin = &$this;
2266  // We decrease stock for product
2267  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));
2268  else $result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceValidatedInDolibarr",$num));
2269  if ($result < 0) {
2270  $error++;
2271  $this->error = $mouvP->error;
2272  }
2273  }
2274  }
2275  }
2276  }
2277 
2278  // Trigger calls
2279  if (! $error && ! $notrigger)
2280  {
2281  // Call trigger
2282  $result=$this->call_trigger('BILL_VALIDATE',$user);
2283  if ($result < 0) $error++;
2284  // End call triggers
2285  }
2286 
2287  if (! $error)
2288  {
2289  $this->oldref = $this->ref;
2290 
2291  // Rename directory if dir was a temporary ref
2292  if (preg_match('/^[\(]?PROV/i', $this->ref))
2293  {
2294  // Rename of object directory ($this->ref = old ref, $num = new ref)
2295  // to not lose the linked files
2296  $oldref = dol_sanitizeFileName($this->ref);
2297  $newref = dol_sanitizeFileName($num);
2298  $dirsource = $conf->facture->dir_output.'/'.$oldref;
2299  $dirdest = $conf->facture->dir_output.'/'.$newref;
2300  if (file_exists($dirsource))
2301  {
2302  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2303 
2304  if (@rename($dirsource, $dirdest))
2305  {
2306  dol_syslog("Rename ok");
2307  // Rename docs starting with $oldref with $newref
2308  $listoffiles=dol_dir_list($conf->facture->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
2309  foreach($listoffiles as $fileentry)
2310  {
2311  $dirsource=$fileentry['name'];
2312  $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
2313  $dirsource=$fileentry['path'].'/'.$dirsource;
2314  $dirdest=$fileentry['path'].'/'.$dirdest;
2315  @rename($dirsource, $dirdest);
2316  }
2317  }
2318  }
2319  }
2320  }
2321 
2322  if (! $error && !$this->is_last_in_cycle())
2323  {
2324  if (! $this->updatePriceNextInvoice($langs))
2325  {
2326  $error++;
2327  }
2328  }
2329 
2330  // Set new ref and define current statut
2331  if (! $error)
2332  {
2333  $this->ref = $num;
2334  $this->facnumber=$num;
2335  $this->statut= self::STATUS_VALIDATED;
2336  $this->brouillon=0;
2337  $this->date_validation=$now;
2338  $i = 0;
2339 
2340  if (!empty($conf->global->INVOICE_USE_SITUATION))
2341  {
2342  $final = True;
2343  $nboflines = count($this->lines);
2344  while (($i < $nboflines) && $final) {
2345  $final = ($this->lines[$i]->situation_percent == 100);
2346  $i++;
2347  }
2348  if ($final) {
2349  $this->setFinal($user);
2350  }
2351  }
2352  }
2353  }
2354  else
2355  {
2356  $error++;
2357  }
2358 
2359  if (! $error)
2360  {
2361  $this->db->commit();
2362  return 1;
2363  }
2364  else
2365  {
2366  $this->db->rollback();
2367  return -1;
2368  }
2369  }
2370 
2377  function updatePriceNextInvoice(&$langs)
2378  {
2379  foreach ($this->tab_next_situation_invoice as $next_invoice)
2380  {
2381  $is_last = $next_invoice->is_last_in_cycle();
2382 
2383  if ($next_invoice->brouillon && $is_last != 1)
2384  {
2385  $this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
2386  return false;
2387  }
2388 
2389  $next_invoice->brouillon = 1;
2390  foreach ($next_invoice->lines as $line)
2391  {
2392  $result = $next_invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent,
2393  $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type,
2394  $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent,
2395  $line->fk_unit);
2396 
2397  if ($result < 0)
2398  {
2399  $this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
2400  return false;
2401  }
2402  }
2403 
2404  break; // Only the next invoice and not each next invoice
2405  }
2406 
2407  return true;
2408  }
2409 
2417  function set_draft($user,$idwarehouse=-1)
2418  {
2419  global $conf,$langs;
2420 
2421  $error=0;
2422 
2423  if ($this->statut == self::STATUS_DRAFT)
2424  {
2425  dol_syslog(get_class($this)."::set_draft already draft status", LOG_WARNING);
2426  return 0;
2427  }
2428 
2429  $this->db->begin();
2430 
2431  $sql = "UPDATE ".MAIN_DB_PREFIX."facture";
2432  $sql.= " SET fk_statut = ".self::STATUS_DRAFT;
2433  $sql.= " WHERE rowid = ".$this->id;
2434 
2435  dol_syslog(get_class($this)."::set_draft", LOG_DEBUG);
2436  $result=$this->db->query($sql);
2437  if ($result)
2438  {
2439  // Si on decremente le produit principal et ses composants a la validation de facture, on réincrement
2440  if ($this->type != self::TYPE_DEPOSIT && $result >= 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL))
2441  {
2442  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2443  $langs->load("agenda");
2444 
2445  $num=count($this->lines);
2446  for ($i = 0; $i < $num; $i++)
2447  {
2448  if ($this->lines[$i]->fk_product > 0)
2449  {
2450  $mouvP = new MouvementStock($this->db);
2451  $mouvP->origin = &$this;
2452  // We decrease stock for product
2453  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));
2454  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
2455  }
2456  }
2457  }
2458 
2459  if ($error == 0)
2460  {
2461  $old_statut=$this->statut;
2462  $this->brouillon = 1;
2463  $this->statut = self::STATUS_DRAFT;
2464  // Call trigger
2465  $result=$this->call_trigger('BILL_UNVALIDATE',$user);
2466  if ($result < 0)
2467  {
2468  $error++;
2469  $this->statut=$old_statut;
2470  $this->brouillon=0;
2471  }
2472  // End call triggers
2473  } else {
2474  $this->db->rollback();
2475  return -1;
2476  }
2477 
2478  if ($error == 0)
2479  {
2480  $this->db->commit();
2481  return 1;
2482  }
2483  else
2484  {
2485  $this->db->rollback();
2486  return -1;
2487  }
2488  }
2489  else
2490  {
2491  $this->error=$this->db->error();
2492  $this->db->rollback();
2493  return -1;
2494  }
2495  }
2496 
2497 
2536  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='', $fk_unit = null, $pu_ht_devise = 0)
2537  {
2538  // Deprecation warning
2539  if ($label) {
2540  dol_syslog(__METHOD__ . ": using line label is deprecated", LOG_WARNING);
2541  }
2542 
2543  global $mysoc, $conf, $langs;
2544 
2545  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);
2546  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2547 
2548  // Clean parameters
2549  if (empty($remise_percent)) $remise_percent=0;
2550  if (empty($qty)) $qty=0;
2551  if (empty($info_bits)) $info_bits=0;
2552  if (empty($rang)) $rang=0;
2553  if (empty($ventil)) $ventil=0;
2554  if (empty($txtva)) $txtva=0;
2555  if (empty($txlocaltax1)) $txlocaltax1=0;
2556  if (empty($txlocaltax2)) $txlocaltax2=0;
2557  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
2558  if (empty($fk_prev_id)) $fk_prev_id = 'null';
2559  if (! isset($situation_percent) || $situation_percent > 100 || (string) $situation_percent == '') $situation_percent = 100;
2560 
2561  $localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
2562 
2563  // Clean vat code
2564  $vat_src_code='';
2565  if (preg_match('/\((.*)\)/', $txtva, $reg))
2566  {
2567  $vat_src_code = $reg[1];
2568  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2569  }
2570 
2571  $remise_percent=price2num($remise_percent);
2572  $qty=price2num($qty);
2573  $pu_ht=price2num($pu_ht);
2574  $pu_ttc=price2num($pu_ttc);
2575  $pa_ht=price2num($pa_ht);
2576  $txtva=price2num($txtva);
2577  $txlocaltax1=price2num($txlocaltax1);
2578  $txlocaltax2=price2num($txlocaltax2);
2579 
2580  if ($price_base_type=='HT')
2581  {
2582  $pu=$pu_ht;
2583  }
2584  else
2585  {
2586  $pu=$pu_ttc;
2587  }
2588 
2589  // Check parameters
2590  if ($type < 0) return -1;
2591 
2592  if (! empty($this->brouillon))
2593  {
2594  $this->db->begin();
2595 
2596  $product_type=$type;
2597  if (!empty($fk_product))
2598  {
2599  $product=new Product($this->db);
2600  $result=$product->fetch($fk_product);
2601  $product_type=$product->type;
2602 
2603  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE) && $product_type == 0 && $product->stock_reel < $qty) {
2604  $langs->load("errors");
2605  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
2606  $this->db->rollback();
2607  return -3;
2608  }
2609  }
2610 
2611  // Calcul du total TTC et de la TVA pour la ligne a partir de
2612  // qty, pu, remise_percent et txtva
2613  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2614  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2615 
2616  $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);
2617 
2618  $total_ht = $tabprice[0];
2619  $total_tva = $tabprice[1];
2620  $total_ttc = $tabprice[2];
2621  $total_localtax1 = $tabprice[9];
2622  $total_localtax2 = $tabprice[10];
2623  $pu_ht = $tabprice[3];
2624 
2625  // MultiCurrency
2626  $multicurrency_total_ht = $tabprice[16];
2627  $multicurrency_total_tva = $tabprice[17];
2628  $multicurrency_total_ttc = $tabprice[18];
2629  $pu_ht_devise = $tabprice[19];
2630 
2631  // Rank to use
2632  $rangtouse = $rang;
2633  if ($rangtouse == -1)
2634  {
2635  $rangmax = $this->line_max($fk_parent_line);
2636  $rangtouse = $rangmax + 1;
2637  }
2638 
2639  // Insert line
2640  $this->line=new FactureLigne($this->db);
2641 
2642  $this->line->context = $this->context;
2643 
2644  $this->line->fk_facture=$this->id;
2645  $this->line->label=$label; // deprecated
2646  $this->line->desc=$desc;
2647 
2648  $this->line->qty= ($this->type==self::TYPE_CREDIT_NOTE?abs($qty):$qty); // For credit note, quantity is always positive and unit price negative
2649  $this->line->subprice= ($this->type==self::TYPE_CREDIT_NOTE?-abs($pu_ht):$pu_ht); // For credit note, unit price always negative, always positive otherwise
2650 
2651  $this->line->vat_src_code=$vat_src_code;
2652  $this->line->tva_tx=$txtva;
2653  $this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
2654  $this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
2655  $this->line->localtax1_type = $localtaxes_type[0];
2656  $this->line->localtax2_type = $localtaxes_type[2];
2657 
2658  $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
2659  $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
2660  $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
2661  $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
2662  $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
2663 
2664  $this->line->fk_product=$fk_product;
2665  $this->line->product_type=$product_type;
2666  $this->line->remise_percent=$remise_percent;
2667  $this->line->date_start=$date_start;
2668  $this->line->date_end=$date_end;
2669  $this->line->ventil=$ventil;
2670  $this->line->rang=$rangtouse;
2671  $this->line->info_bits=$info_bits;
2672  $this->line->fk_remise_except=$fk_remise_except;
2673 
2674  $this->line->special_code=$special_code;
2675  $this->line->fk_parent_line=$fk_parent_line;
2676  $this->line->origin=$origin;
2677  $this->line->origin_id=$origin_id;
2678  $this->line->situation_percent = $situation_percent;
2679  $this->line->fk_prev_id = $fk_prev_id;
2680  $this->line->fk_unit=$fk_unit;
2681 
2682  // infos marge
2683  $this->line->fk_fournprice = $fk_fournprice;
2684  $this->line->pa_ht = $pa_ht;
2685 
2686  // Multicurrency
2687  $this->line->fk_multicurrency = $this->fk_multicurrency;
2688  $this->line->multicurrency_code = $this->multicurrency_code;
2689  $this->line->multicurrency_subprice = $pu_ht_devise;
2690  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2691  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2692  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2693 
2694  if (is_array($array_options) && count($array_options)>0) {
2695  $this->line->array_options=$array_options;
2696  }
2697 
2698  $result=$this->line->insert();
2699  if ($result > 0)
2700  {
2701  // Reorder if child line
2702  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
2703 
2704  // Mise a jour informations denormalisees au niveau de la facture meme
2705  $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.
2706  if ($result > 0)
2707  {
2708  $this->db->commit();
2709  return $this->line->rowid;
2710  }
2711  else
2712  {
2713  $this->error=$this->db->error();
2714  $this->db->rollback();
2715  return -1;
2716  }
2717  }
2718  else
2719  {
2720  $this->error=$this->line->error;
2721  $this->db->rollback();
2722  return -2;
2723  }
2724  }
2725  else
2726  {
2727  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
2728  return -3;
2729  }
2730  }
2731 
2761  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)
2762  {
2763  global $conf,$user;
2764  // Deprecation warning
2765  if ($label) {
2766  dol_syslog(__METHOD__ . ": using line label is deprecated", LOG_WARNING);
2767  }
2768 
2769  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2770 
2771  global $mysoc,$langs;
2772 
2773  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);
2774 
2775  if ($this->brouillon)
2776  {
2777  if (!$this->is_last_in_cycle() && empty($this->error))
2778  {
2779  if (!$this->checkProgressLine($rowid, $situation_percent))
2780  {
2781  if (!$this->error) $this->error=$langs->trans('invoiceLineProgressError');
2782  return -3;
2783  }
2784  }
2785 
2786  $this->db->begin();
2787 
2788  // Clean parameters
2789  if (empty($qty)) $qty=0;
2790  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
2791  if (empty($special_code) || $special_code == 3) $special_code=0;
2792  if (! isset($situation_percent) || $situation_percent > 100 || (string) $situation_percent == '') $situation_percent = 100;
2793 
2794  $remise_percent = price2num($remise_percent);
2795  $qty = price2num($qty);
2796  $pu = price2num($pu);
2797  $pa_ht = price2num($pa_ht);
2798  $txtva = price2num($txtva);
2799  $txlocaltax1 = price2num($txlocaltax1);
2800  $txlocaltax2 = price2num($txlocaltax2);
2801 
2802  // Check parameters
2803  if ($type < 0) return -1;
2804 
2805  // Calculate total with, without tax and tax from qty, pu, remise_percent and txtva
2806  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2807  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2808 
2809  $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty, $mysoc);
2810 
2811  // Clean vat code
2812  $vat_src_code='';
2813  if (preg_match('/\((.*)\)/', $txtva, $reg))
2814  {
2815  $vat_src_code = $reg[1];
2816  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2817  }
2818 
2819  $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);
2820 
2821  $total_ht = $tabprice[0];
2822  $total_tva = $tabprice[1];
2823  $total_ttc = $tabprice[2];
2824  $total_localtax1=$tabprice[9];
2825  $total_localtax2=$tabprice[10];
2826  $pu_ht = $tabprice[3];
2827  $pu_tva = $tabprice[4];
2828  $pu_ttc = $tabprice[5];
2829 
2830  // MultiCurrency
2831  $multicurrency_total_ht = $tabprice[16];
2832  $multicurrency_total_tva = $tabprice[17];
2833  $multicurrency_total_ttc = $tabprice[18];
2834  $pu_ht_devise = $tabprice[19];
2835 
2836  // Old properties: $price, $remise (deprecated)
2837  $price = $pu;
2838  $remise = 0;
2839  if ($remise_percent > 0)
2840  {
2841  $remise = round(($pu * $remise_percent / 100),2);
2842  $price = ($pu - $remise);
2843  }
2844  $price = price2num($price);
2845 
2846  //Fetch current line from the database and then clone the object and set it in $oldline property
2847  $line = new FactureLigne($this->db);
2848  $line->fetch($rowid);
2849 
2850  if (!empty($line->fk_product))
2851  {
2852  $product=new Product($this->db);
2853  $result=$product->fetch($line->fk_product);
2854  $product_type=$product->type;
2855 
2856  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE) && $product_type == 0 && $product->stock_reel < $qty) {
2857  $langs->load("errors");
2858  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
2859  $this->db->rollback();
2860  return -3;
2861  }
2862  }
2863 
2864  $staticline = clone $line;
2865 
2866  $line->oldline = $staticline;
2867  $this->line = $line;
2868  $this->line->context = $this->context;
2869 
2870  // Reorder if fk_parent_line change
2871  if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
2872  {
2873  $rangmax = $this->line_max($fk_parent_line);
2874  $this->line->rang = $rangmax + 1;
2875  }
2876 
2877  $this->line->rowid = $rowid;
2878  $this->line->label = $label;
2879  $this->line->desc = $desc;
2880  $this->line->qty = ($this->type==self::TYPE_CREDIT_NOTE?abs($qty):$qty); // For credit note, quantity is always positive and unit price negative
2881 
2882  $this->line->vat_src_code = $vat_src_code;
2883  $this->line->tva_tx = $txtva;
2884  $this->line->localtax1_tx = $txlocaltax1;
2885  $this->line->localtax2_tx = $txlocaltax2;
2886  $this->line->localtax1_type = $localtaxes_type[0];
2887  $this->line->localtax2_type = $localtaxes_type[2];
2888 
2889  $this->line->remise_percent = $remise_percent;
2890  $this->line->subprice = ($this->type==2?-abs($pu_ht):$pu_ht); // For credit note, unit price always negative, always positive otherwise
2891  $this->line->date_start = $date_start;
2892  $this->line->date_end = $date_end;
2893  $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
2894  $this->line->total_tva = (($this->type==self::TYPE_CREDIT_NOTE||$qty<0)?-abs($total_tva):$total_tva);
2895  $this->line->total_localtax1 = $total_localtax1;
2896  $this->line->total_localtax2 = $total_localtax2;
2897  $this->line->total_ttc = (($this->type==self::TYPE_CREDIT_NOTE||$qty<0)?-abs($total_ttc):$total_ttc);
2898  $this->line->info_bits = $info_bits;
2899  $this->line->special_code = $special_code;
2900  $this->line->product_type = $type;
2901  $this->line->fk_parent_line = $fk_parent_line;
2902  $this->line->skip_update_total = $skip_update_total;
2903  $this->line->situation_percent = $situation_percent;
2904  $this->line->fk_unit = $fk_unit;
2905 
2906  $this->line->fk_fournprice = $fk_fournprice;
2907  $this->line->pa_ht = $pa_ht;
2908 
2909  // Multicurrency
2910  $this->line->multicurrency_subprice = $pu_ht_devise;
2911  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2912  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2913  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2914 
2915  if (is_array($array_options) && count($array_options)>0) {
2916  $this->line->array_options=$array_options;
2917  }
2918 
2919  $result=$this->line->update($user, $notrigger);
2920  if ($result > 0)
2921  {
2922  // Reorder if child line
2923  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
2924 
2925  // Mise a jour info denormalisees au niveau facture
2926  $this->update_price(1);
2927  $this->db->commit();
2928  return $result;
2929  }
2930  else
2931  {
2932  $this->error=$this->line->error;
2933  $this->db->rollback();
2934  return -1;
2935  }
2936  }
2937  else
2938  {
2939  $this->error="Invoice statut makes operation forbidden";
2940  return -2;
2941  }
2942  }
2943 
2951  function checkProgressLine($idline, $situation_percent)
2952  {
2953  $sql = 'SELECT fd.situation_percent FROM '.MAIN_DB_PREFIX.'facturedet fd
2954  INNER JOIN '.MAIN_DB_PREFIX.'facture f ON (fd.fk_facture = f.rowid)
2955  WHERE fd.fk_prev_id = '.$idline.'
2956  AND f.fk_statut <> 0';
2957 
2958  $result = $this->db->query($sql);
2959  if (! $result)
2960  {
2961  $this->error=$this->db->error();
2962  return false;
2963  }
2964 
2965  $obj = $this->db->fetch_object($result);
2966 
2967  if ($obj === null) return true;
2968  else return $situation_percent < $obj->situation_percent;
2969  }
2970 
2978  function update_percent($line, $percent)
2979  {
2980  global $mysoc,$user;
2981 
2982  include_once(DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php');
2983 
2984  // Cap percentages to 100
2985  if ($percent > 100) $percent = 100;
2986  $line->situation_percent = $percent;
2987  $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);
2988  $line->total_ht = $tabprice[0];
2989  $line->total_tva = $tabprice[1];
2990  $line->total_ttc = $tabprice[2];
2991  $line->total_localtax1 = $tabprice[9];
2992  $line->total_localtax2 = $tabprice[10];
2993  $line->update($user);
2994  $this->update_price(1);
2995  $this->db->commit();
2996  }
2997 
3004  function deleteline($rowid)
3005  {
3006  global $user;
3007 
3008  dol_syslog(get_class($this)."::deleteline rowid=".$rowid, LOG_DEBUG);
3009 
3010  if (! $this->brouillon)
3011  {
3012  $this->error='ErrorDeleteLineNotAllowedByObjectStatus';
3013  return -1;
3014  }
3015 
3016  $this->db->begin();
3017 
3018  // Libere remise liee a ligne de facture
3019  $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
3020  $sql.= ' SET fk_facture_line = NULL';
3021  $sql.= ' WHERE fk_facture_line = '.$rowid;
3022 
3023  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
3024  $result = $this->db->query($sql);
3025  if (! $result)
3026  {
3027  $this->error=$this->db->error();
3028  $this->db->rollback();
3029  return -1;
3030  }
3031 
3032  $line=new FactureLigne($this->db);
3033 
3034  $line->context = $this->context;
3035 
3036  // For triggers
3037  $result = $line->fetch($rowid);
3038  if (! ($result > 0)) dol_print_error($db, $line->error, $line->errors);
3039 
3040  if ($line->delete($user) > 0)
3041  {
3042  $result=$this->update_price(1);
3043 
3044  if ($result > 0)
3045  {
3046  $this->db->commit();
3047  return 1;
3048  }
3049  else
3050  {
3051  $this->db->rollback();
3052  $this->error=$this->db->lasterror();
3053  return -1;
3054  }
3055  }
3056  else
3057  {
3058  $this->db->rollback();
3059  $this->error=$line->error;
3060  return -1;
3061  }
3062  }
3063 
3072  function set_remise($user, $remise, $notrigger=0)
3073  {
3074  // Clean parameters
3075  if (empty($remise)) $remise=0;
3076 
3077  if ($user->rights->facture->creer)
3078  {
3079  $remise=price2num($remise);
3080 
3081  $error=0;
3082 
3083  $this->db->begin();
3084 
3085  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3086  $sql.= ' SET remise_percent = '.$remise;
3087  $sql.= ' WHERE rowid = '.$this->id;
3088  $sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
3089 
3090  dol_syslog(__METHOD__, LOG_DEBUG);
3091  $resql=$this->db->query($sql);
3092  if (!$resql)
3093  {
3094  $this->errors[]=$this->db->error();
3095  $error++;
3096  }
3097 
3098  if (! $notrigger && empty($error))
3099  {
3100  // Call trigger
3101  $result=$this->call_trigger('BILL_MODIFY',$user);
3102  if ($result < 0) $error++;
3103  // End call triggers
3104  }
3105 
3106  if (! $error)
3107  {
3108  $this->remise_percent = $remise;
3109  $this->update_price(1);
3110 
3111  $this->db->commit();
3112  return 1;
3113  }
3114  else
3115  {
3116  foreach($this->errors as $errmsg)
3117  {
3118  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3119  $this->error.=($this->error?', '.$errmsg:$errmsg);
3120  }
3121  $this->db->rollback();
3122  return -1*$error;
3123  }
3124  }
3125  }
3126 
3127 
3136  function set_remise_absolue($user, $remise, $notrigger=0)
3137  {
3138  if (empty($remise)) $remise=0;
3139 
3140  if ($user->rights->facture->creer)
3141  {
3142  $error=0;
3143 
3144  $this->db->begin();
3145 
3146  $remise=price2num($remise);
3147 
3148  $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3149  $sql.= ' SET remise_absolue = '.$remise;
3150  $sql.= ' WHERE rowid = '.$this->id;
3151  $sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
3152 
3153  dol_syslog(__METHOD__, LOG_DEBUG);
3154  $resql=$this->db->query($sql);
3155  if (!$resql)
3156  {
3157  $this->errors[]=$this->db->error();
3158  $error++;
3159  }
3160 
3161  if (! $error)
3162  {
3163  $this->oldcopy= clone $this;
3164  $this->remise_absolue = $remise;
3165  $this->update_price(1);
3166  }
3167 
3168  if (! $notrigger && empty($error))
3169  {
3170  // Call trigger
3171  $result=$this->call_trigger('BILL_MODIFY',$user);
3172  if ($result < 0) $error++;
3173  // End call triggers
3174  }
3175 
3176  if (! $error)
3177  {
3178  $this->db->commit();
3179  return 1;
3180  }
3181  else
3182  {
3183  foreach($this->errors as $errmsg)
3184  {
3185  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3186  $this->error.=($this->error?', '.$errmsg:$errmsg);
3187  }
3188  $this->db->rollback();
3189  return -1*$error;
3190  }
3191  }
3192  }
3193 
3200  function getListOfPayments($filtertype='')
3201  {
3202  $retarray=array();
3203 
3204  $table='paiement_facture';
3205  $table2='paiement';
3206  $field='fk_facture';
3207  $field2='fk_paiement';
3208  $sharedentity='facture';
3209  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
3210  {
3211  $table='paiementfourn_facturefourn';
3212  $table2='paiementfourn';
3213  $field='fk_facturefourn';
3214  $field2='fk_paiementfourn';
3215  $sharedentity='facture_fourn';
3216  }
3217 
3218  $sql = 'SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code';
3219  $sql.= ' FROM '.MAIN_DB_PREFIX.$table.' as pf, '.MAIN_DB_PREFIX.$table2.' as p, '.MAIN_DB_PREFIX.'c_paiement as t';
3220  $sql.= ' WHERE pf.'.$field.' = '.$this->id;
3221  //$sql.= ' WHERE pf.'.$field.' = 1';
3222  $sql.= ' AND pf.'.$field2.' = p.rowid';
3223  $sql.= ' AND p.fk_paiement = t.id';
3224  $sql.= ' AND p.entity IN (' . getEntity($sharedentity).')';
3225  if ($filtertype) $sql.=" AND t.code='PRE'";
3226 
3227  dol_syslog(get_class($this)."::getListOfPayments", LOG_DEBUG);
3228  $resql=$this->db->query($sql);
3229  if ($resql)
3230  {
3231  $num = $this->db->num_rows($resql);
3232  $i=0;
3233  while ($i < $num)
3234  {
3235  $obj = $this->db->fetch_object($resql);
3236  $retarray[]=array('amount'=>$obj->amount,'type'=>$obj->code, 'date'=>$obj->datep, 'num'=>$obj->num, 'ref'=>$obj->ref);
3237  $i++;
3238  }
3239  $this->db->free($resql);
3240  return $retarray;
3241  }
3242  else
3243  {
3244  $this->error=$this->db->lasterror();
3245  dol_print_error($this->db);
3246  return array();
3247  }
3248  }
3249 
3250 
3259  function getNextNumRef($soc,$mode='next')
3260  {
3261  global $conf, $langs;
3262  $langs->load("bills");
3263 
3264  // Clean parameters (if not defined or using deprecated value)
3265  if (empty($conf->global->FACTURE_ADDON)) $conf->global->FACTURE_ADDON='mod_facture_terre';
3266  else if ($conf->global->FACTURE_ADDON=='terre') $conf->global->FACTURE_ADDON='mod_facture_terre';
3267  else if ($conf->global->FACTURE_ADDON=='mercure') $conf->global->FACTURE_ADDON='mod_facture_mercure';
3268 
3269  if (! empty($conf->global->FACTURE_ADDON))
3270  {
3271  dol_syslog("Call getNextNumRef with FACTURE_ADDON = ".$conf->global->FACTURE_ADDON.", thirdparty=".$soc->nom.", type=".$soc->typent_code, LOG_DEBUG);
3272 
3273  $mybool=false;
3274 
3275  $file = $conf->global->FACTURE_ADDON.".php";
3276  $classname = $conf->global->FACTURE_ADDON;
3277 
3278  // Include file with class
3279  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3280 
3281  foreach ($dirmodels as $reldir) {
3282 
3283  $dir = dol_buildpath($reldir."core/modules/facture/");
3284 
3285  // Load file with numbering class (if found)
3286  if (is_file($dir.$file) && is_readable($dir.$file))
3287  {
3288  $mybool |= include_once $dir . $file;
3289  }
3290  }
3291 
3292  // For compatibility
3293  if (! $mybool)
3294  {
3295  $file = $conf->global->FACTURE_ADDON."/".$conf->global->FACTURE_ADDON.".modules.php";
3296  $classname = "mod_facture_".$conf->global->FACTURE_ADDON;
3297  $classname = preg_replace('/\-.*$/','',$classname);
3298  // Include file with class
3299  foreach ($conf->file->dol_document_root as $dirroot)
3300  {
3301  $dir = $dirroot."/core/modules/facture/";
3302 
3303  // Load file with numbering class (if found)
3304  if (is_file($dir.$file) && is_readable($dir.$file)) {
3305  $mybool |= include_once $dir . $file;
3306  }
3307  }
3308  }
3309 
3310  if (! $mybool)
3311  {
3312  dol_print_error('',"Failed to include file ".$file);
3313  return '';
3314  }
3315 
3316  $obj = new $classname();
3317  $numref = "";
3318  $numref = $obj->getNextValue($soc,$this,$mode);
3319 
3324  if ($mode != 'last' && !$numref) {
3325  $this->error=$obj->error;
3326  //dol_print_error($this->db,"Facture::getNextNumRef ".$obj->error);
3327  return "";
3328  }
3329 
3330  return $numref;
3331  }
3332  else
3333  {
3334  $langs->load("errors");
3335  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3336  return "";
3337  }
3338  }
3339 
3346  function info($id)
3347  {
3348  $sql = 'SELECT c.rowid, datec, date_valid as datev, tms as datem,';
3349  $sql.= ' fk_user_author, fk_user_valid';
3350  $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as c';
3351  $sql.= ' WHERE c.rowid = '.$id;
3352 
3353  $result=$this->db->query($sql);
3354  if ($result)
3355  {
3356  if ($this->db->num_rows($result))
3357  {
3358  $obj = $this->db->fetch_object($result);
3359  $this->id = $obj->rowid;
3360  if ($obj->fk_user_author)
3361  {
3362  $cuser = new User($this->db);
3363  $cuser->fetch($obj->fk_user_author);
3364  $this->user_creation = $cuser;
3365  }
3366  if ($obj->fk_user_valid)
3367  {
3368  $vuser = new User($this->db);
3369  $vuser->fetch($obj->fk_user_valid);
3370  $this->user_validation = $vuser;
3371  }
3372  $this->date_creation = $this->db->jdate($obj->datec);
3373  $this->date_modification = $this->db->jdate($obj->datem);
3374  $this->date_validation = $this->db->jdate($obj->datev); // Should be in log table
3375  }
3376  $this->db->free($result);
3377  }
3378  else
3379  {
3380  dol_print_error($this->db);
3381  }
3382  }
3383 
3384 
3398  function liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='f.datef,f.rowid', $sortorder='DESC')
3399  {
3400  global $conf,$user;
3401 
3402  $ga = array();
3403 
3404  $sql = "SELECT s.rowid, s.nom as name, s.client,";
3405  $sql.= " f.rowid as fid, f.facnumber as ref, f.datef as df";
3406  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
3407  $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture as f";
3408  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
3409  $sql.= " WHERE f.entity = ".$conf->entity;
3410  $sql.= " AND f.fk_soc = s.rowid";
3411  if (! $user->rights->societe->client->voir && ! $socid) //restriction
3412  {
3413  $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
3414  }
3415  if ($socid) $sql.= " AND s.rowid = ".$socid;
3416  if ($draft) $sql.= " AND f.fk_statut = ".self::STATUS_DRAFT;
3417  if (is_object($excluser)) $sql.= " AND f.fk_user_author <> ".$excluser->id;
3418  $sql.= $this->db->order($sortfield,$sortorder);
3419  $sql.= $this->db->plimit($limit,$offset);
3420 
3421  $result=$this->db->query($sql);
3422  if ($result)
3423  {
3424  $numc = $this->db->num_rows($result);
3425  if ($numc)
3426  {
3427  $i = 0;
3428  while ($i < $numc)
3429  {
3430  $obj = $this->db->fetch_object($result);
3431 
3432  if ($shortlist == 1)
3433  {
3434  $ga[$obj->fid] = $obj->ref;
3435  }
3436  else if ($shortlist == 2)
3437  {
3438  $ga[$obj->fid] = $obj->ref.' ('.$obj->name.')';
3439  }
3440  else
3441  {
3442  $ga[$i]['id'] = $obj->fid;
3443  $ga[$i]['ref'] = $obj->ref;
3444  $ga[$i]['name'] = $obj->name;
3445  }
3446  $i++;
3447  }
3448  }
3449  return $ga;
3450  }
3451  else
3452  {
3453  dol_print_error($this->db);
3454  return -1;
3455  }
3456  }
3457 
3458 
3467  function list_replacable_invoices($socid=0)
3468  {
3469  global $conf;
3470 
3471  $return = array();
3472 
3473  $sql = "SELECT f.rowid as rowid, f.facnumber, f.fk_statut,";
3474  $sql.= " ff.rowid as rowidnext";
3475  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3476  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture";
3477  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as ff ON f.rowid = ff.fk_facture_source";
3478  $sql.= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
3479  $sql.= " AND f.entity = ".$conf->entity;
3480  $sql.= " AND f.paye = 0"; // Pas classee payee completement
3481  $sql.= " AND pf.fk_paiement IS NULL"; // Aucun paiement deja fait
3482  $sql.= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
3483  if ($socid > 0) $sql.=" AND f.fk_soc = ".$socid;
3484  $sql.= " ORDER BY f.facnumber";
3485 
3486  dol_syslog(get_class($this)."::list_replacable_invoices", LOG_DEBUG);
3487  $resql=$this->db->query($sql);
3488  if ($resql)
3489  {
3490  while ($obj=$this->db->fetch_object($resql))
3491  {
3492  $return[$obj->rowid]=array( 'id' => $obj->rowid,
3493  'ref' => $obj->facnumber,
3494  'status' => $obj->fk_statut);
3495  }
3496  //print_r($return);
3497  return $return;
3498  }
3499  else
3500  {
3501  $this->error=$this->db->error();
3502  return -1;
3503  }
3504  }
3505 
3506 
3516  {
3517  global $conf;
3518 
3519  $return = array();
3520 
3521  $sql = "SELECT f.rowid as rowid, f.facnumber, f.fk_statut, f.type, f.paye, pf.fk_paiement";
3522  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3523  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture";
3524  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as ff ON (f.rowid = ff.fk_facture_source AND ff.type=".self::TYPE_REPLACEMENT.")";
3525  $sql.= " WHERE f.entity = ".$conf->entity;
3526  $sql.= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
3527  // $sql.= " WHERE f.fk_statut >= 1";
3528  // $sql.= " AND (f.paye = 1"; // Classee payee completement
3529  // $sql.= " OR f.close_code IS NOT NULL)"; // Classee payee partiellement
3530  $sql.= " AND ff.type IS NULL"; // Renvoi vrai si pas facture de remplacement
3531  $sql.= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
3532  if ($socid > 0) $sql.=" AND f.fk_soc = ".$socid;
3533  $sql.= " ORDER BY f.facnumber";
3534 
3535  dol_syslog(get_class($this)."::list_qualified_avoir_invoices", LOG_DEBUG);
3536  $resql=$this->db->query($sql);
3537  if ($resql)
3538  {
3539  while ($obj=$this->db->fetch_object($resql))
3540  {
3541  $qualified=0;
3542  if ($obj->fk_statut == self::STATUS_VALIDATED) $qualified=1;
3543  if ($obj->fk_statut == self::STATUS_CLOSED) $qualified=1;
3544  if ($qualified)
3545  {
3546  //$ref=$obj->facnumber;
3547  $paymentornot=($obj->fk_paiement?1:0);
3548  $return[$obj->rowid]=array('ref'=>$obj->facnumber,'status'=>$obj->fk_statut,'type'=>$obj->type,'paye'=>$obj->paye,'paymentornot'=>$paymentornot);
3549  }
3550  }
3551 
3552  return $return;
3553  }
3554  else
3555  {
3556  $this->error=$this->db->error();
3557  return -1;
3558  }
3559  }
3560 
3561 
3570  function demande_prelevement($fuser, $amount=0)
3571  {
3572 
3573  $error=0;
3574 
3575  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3576 
3577  if ($this->statut > self::STATUS_DRAFT && $this->paye == 0)
3578  {
3579  require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
3580  $bac = new CompanyBankAccount($this->db);
3581  $bac->fetch(0,$this->socid);
3582 
3583  $sql = 'SELECT count(*)';
3584  $sql.= ' FROM '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3585  $sql.= ' WHERE fk_facture = '.$this->id;
3586  $sql.= ' AND traite = 0';
3587 
3588  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3589  $resql=$this->db->query($sql);
3590  if ($resql)
3591  {
3592  $row = $this->db->fetch_row($resql);
3593  if ($row[0] == 0)
3594  {
3595  $now=dol_now();
3596 
3597  $totalpaye = $this->getSommePaiement();
3598  $totalcreditnotes = $this->getSumCreditNotesUsed();
3599  $totaldeposits = $this->getSumDepositsUsed();
3600  //print "totalpaye=".$totalpaye." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits;
3601 
3602  // We can also use bcadd to avoid pb with floating points
3603  // For example print 239.2 - 229.3 - 9.9; does not return 0.
3604  //$resteapayer=bcadd($this->total_ttc,$totalpaye,$conf->global->MAIN_MAX_DECIMALS_TOT);
3605  //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
3606  if (empty($amount)) $amount = price2num($this->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits,'MT');
3607 
3608  if (is_numeric($amount) && $amount != 0)
3609  {
3610  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3611  $sql .= ' (fk_facture, amount, date_demande, fk_user_demande, code_banque, code_guichet, number, cle_rib)';
3612  $sql .= ' VALUES ('.$this->id;
3613  $sql .= ",'".price2num($amount)."'";
3614  $sql .= ",'".$this->db->idate($now)."'";
3615  $sql .= ",".$fuser->id;
3616  $sql .= ",'".$bac->code_banque."'";
3617  $sql .= ",'".$bac->code_guichet."'";
3618  $sql .= ",'".$bac->number."'";
3619  $sql .= ",'".$bac->cle_rib."')";
3620 
3621  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
3622  $resql=$this->db->query($sql);
3623  if (! $resql)
3624  {
3625  $this->error=$this->db->lasterror();
3626  dol_syslog(get_class($this).'::demandeprelevement Erreur');
3627  $error++;
3628  }
3629  }
3630  else
3631  {
3632  $this->error='WithdrawRequestErrorNilAmount';
3633  dol_syslog(get_class($this).'::demandeprelevement WithdrawRequestErrorNilAmount');
3634  $error++;
3635  }
3636 
3637  if (! $error)
3638  {
3639  // Force payment mode of invoice to withdraw
3640  $payment_mode_id = dol_getIdFromCode($this->db, 'PRE', 'c_paiement', 'code', 'id', 1);
3641  if ($payment_mode_id > 0)
3642  {
3643  $result=$this->setPaymentMethods($payment_mode_id);
3644  }
3645  }
3646 
3647  if ($error) return -1;
3648  return 1;
3649  }
3650  else
3651  {
3652  $this->error="A request already exists";
3653  dol_syslog(get_class($this).'::demandeprelevement Impossible de creer une demande, demande deja en cours');
3654  return 0;
3655  }
3656  }
3657  else
3658  {
3659  $this->error=$this->db->error();
3660  dol_syslog(get_class($this).'::demandeprelevement Erreur -2');
3661  return -2;
3662  }
3663  }
3664  else
3665  {
3666  $this->error="Status of invoice does not allow this";
3667  dol_syslog(get_class($this)."::demandeprelevement ".$this->error." $this->statut, $this->paye, $this->mode_reglement_id");
3668  return -3;
3669  }
3670  }
3671 
3679  function demande_prelevement_delete($fuser, $did)
3680  {
3681  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'prelevement_facture_demande';
3682  $sql .= ' WHERE rowid = '.$did;
3683  $sql .= ' AND traite = 0';
3684  if ( $this->db->query($sql) )
3685  {
3686  return 0;
3687  }
3688  else
3689  {
3690  $this->error=$this->db->lasterror();
3691  dol_syslog(get_class($this).'::demande_prelevement_delete Error '.$this->error);
3692  return -1;
3693  }
3694  }
3695 
3696 
3703  function load_board($user)
3704  {
3705  global $conf, $langs;
3706 
3707  $clause = " WHERE";
3708 
3709  $sql = "SELECT f.rowid, f.date_lim_reglement as datefin,f.fk_statut";
3710  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3711  if (!$user->rights->societe->client->voir && !$user->societe_id)
3712  {
3713  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON f.fk_soc = sc.fk_soc";
3714  $sql.= " WHERE sc.fk_user = " .$user->id;
3715  $clause = " AND";
3716  }
3717  $sql.= $clause." f.paye=0";
3718  $sql.= " AND f.entity = ".$conf->entity;
3719  $sql.= " AND f.fk_statut = ".self::STATUS_VALIDATED;
3720  if ($user->societe_id) $sql.= " AND f.fk_soc = ".$user->societe_id;
3721 
3722  $resql=$this->db->query($sql);
3723  if ($resql)
3724  {
3725  $langs->load("bills");
3726  $now=dol_now();
3727 
3728  $response = new WorkboardResponse();
3729  $response->warning_delay=$conf->facture->client->warning_delay/60/60/24;
3730  $response->label=$langs->trans("CustomerBillsUnpaid");
3731  $response->url=DOL_URL_ROOT.'/compta/facture/list.php?search_status=1&mainmenu=accountancy&leftmenu=customers_bills';
3732  $response->img=img_object('',"bill");
3733 
3734  $generic_facture = new Facture($this->db);
3735 
3736  while ($obj=$this->db->fetch_object($resql))
3737  {
3738  $generic_facture->date_lim_reglement = $this->db->jdate($obj->datefin);
3739  $generic_facture->statut = $obj->fk_statut;
3740 
3741  $response->nbtodo++;
3742 
3743  if ($generic_facture->hasDelay()) {
3744  $response->nbtodolate++;
3745  }
3746  }
3747 
3748  return $response;
3749  }
3750  else
3751  {
3752  dol_print_error($this->db);
3753  $this->error=$this->db->error();
3754  return -1;
3755  }
3756  }
3757 
3758 
3759  /* gestion des contacts d'une facture */
3760 
3767  {
3768  return $this->getIdContact('external','BILLING');
3769  }
3770 
3777  {
3778  return $this->getIdContact('external','SHIPPING');
3779  }
3780 
3781 
3790  function initAsSpecimen($option='')
3791  {
3792  global $langs;
3793 
3794  $now=dol_now();
3795  $arraynow=dol_getdate($now);
3796  $nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
3797 
3798  // Load array of products prodids
3799  $num_prods = 0;
3800  $prodids = array();
3801  $sql = "SELECT rowid";
3802  $sql.= " FROM ".MAIN_DB_PREFIX."product";
3803  $sql.= " WHERE entity IN (".getEntity('product').")";
3804  $resql = $this->db->query($sql);
3805  if ($resql)
3806  {
3807  $num_prods = $this->db->num_rows($resql);
3808  $i = 0;
3809  while ($i < $num_prods)
3810  {
3811  $i++;
3812  $row = $this->db->fetch_row($resql);
3813  $prodids[$i] = $row[0];
3814  }
3815  }
3816  //Avoid php warning Warning: mt_rand(): max(0) is smaller than min(1) when no product exists
3817  if (empty($num_prods)) {
3818  $num_prods=1;
3819  }
3820 
3821  // Initialize parameters
3822  $this->id=0;
3823  $this->ref = 'SPECIMEN';
3824  $this->specimen=1;
3825  $this->socid = 1;
3826  $this->date = $nownotime;
3827  $this->date_lim_reglement = $nownotime + 3600 * 24 *30;
3828  $this->cond_reglement_id = 1;
3829  $this->cond_reglement_code = 'RECEP';
3830  $this->date_lim_reglement=$this->calculate_date_lim_reglement();
3831  $this->mode_reglement_id = 0; // Not forced to show payment mode CHQ + VIR
3832  $this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR
3833  $this->note_public='This is a comment (public)';
3834  $this->note_private='This is a comment (private)';
3835  $this->note='This is a comment (private)';
3836  $this->fk_incoterms=0;
3837  $this->location_incoterms='';
3838 
3839  if (empty($option) || $option != 'nolines')
3840  {
3841  // Lines
3842  $nbp = 5;
3843  $xnbp = 0;
3844  while ($xnbp < $nbp)
3845  {
3846  $line=new FactureLigne($this->db);
3847  $line->desc=$langs->trans("Description")." ".$xnbp;
3848  $line->qty=1;
3849  $line->subprice=100;
3850  $line->tva_tx=19.6;
3851  $line->localtax1_tx=0;
3852  $line->localtax2_tx=0;
3853  $line->remise_percent=0;
3854  if ($xnbp == 1) // Qty is negative (product line)
3855  {
3856  $prodid = mt_rand(1, $num_prods);
3857  $line->fk_product=$prodids[$prodid];
3858  $line->qty=-1;
3859  $line->total_ht=-100;
3860  $line->total_ttc=-119.6;
3861  $line->total_tva=-19.6;
3862  $line->multicurrency_total_ht=-200;
3863  $line->multicurrency_total_ttc=-239.2;
3864  $line->multicurrency_total_tva=-39.2;
3865  }
3866  else if ($xnbp == 2) // UP is negative (free line)
3867  {
3868  $line->subprice=-100;
3869  $line->total_ht=-100;
3870  $line->total_ttc=-119.6;
3871  $line->total_tva=-19.6;
3872  $line->remise_percent=0;
3873  $line->multicurrency_total_ht=-200;
3874  $line->multicurrency_total_ttc=-239.2;
3875  $line->multicurrency_total_tva=-39.2;
3876  }
3877  else if ($xnbp == 3) // Discount is 50% (product line)
3878  {
3879  $prodid = mt_rand(1, $num_prods);
3880  $line->fk_product=$prodids[$prodid];
3881  $line->total_ht=50;
3882  $line->total_ttc=59.8;
3883  $line->total_tva=9.8;
3884  $line->multicurrency_total_ht=100;
3885  $line->multicurrency_total_ttc=119.6;
3886  $line->multicurrency_total_tva=19.6;
3887  $line->remise_percent=50;
3888  }
3889  else // (product line)
3890  {
3891  $prodid = mt_rand(1, $num_prods);
3892  $line->fk_product=$prodids[$prodid];
3893  $line->total_ht=100;
3894  $line->total_ttc=119.6;
3895  $line->total_tva=19.6;
3896  $line->multicurrency_total_ht=200;
3897  $line->multicurrency_total_ttc=239.2;
3898  $line->multicurrency_total_tva=39.2;
3899  $line->remise_percent=0;
3900  }
3901 
3902  $this->lines[$xnbp]=$line;
3903 
3904 
3905  $this->total_ht += $line->total_ht;
3906  $this->total_tva += $line->total_tva;
3907  $this->total_ttc += $line->total_ttc;
3908 
3909  $this->multicurrency_total_ht += $line->multicurrency_total_ht;
3910  $this->multicurrency_total_tva += $line->multicurrency_total_tva;
3911  $this->multicurrency_total_ttc += $line->multicurrency_total_ttc;
3912 
3913  $xnbp++;
3914  }
3915  $this->revenuestamp = 0;
3916 
3917  // Add a line "offered"
3918  $line=new FactureLigne($this->db);
3919  $line->desc=$langs->trans("Description")." (offered line)";
3920  $line->qty=1;
3921  $line->subprice=100;
3922  $line->tva_tx=19.6;
3923  $line->localtax1_tx=0;
3924  $line->localtax2_tx=0;
3925  $line->remise_percent=100;
3926  $line->total_ht=0;
3927  $line->total_ttc=0; // 90 * 1.196
3928  $line->total_tva=0;
3929  $line->multicurrency_total_ht=0;
3930  $line->multicurrency_total_ttc=0;
3931  $line->multicurrency_total_tva=0;
3932  $prodid = mt_rand(1, $num_prods);
3933  $line->fk_product=$prodids[$prodid];
3934 
3935  $this->lines[$xnbp]=$line;
3936  $xnbp++;
3937  }
3938  }
3939 
3945  function load_state_board()
3946  {
3947  global $conf, $user;
3948 
3949  $this->nb=array();
3950 
3951  $clause = "WHERE";
3952 
3953  $sql = "SELECT count(f.rowid) as nb";
3954  $sql.= " FROM ".MAIN_DB_PREFIX."facture as f";
3955  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3956  if (!$user->rights->societe->client->voir && !$user->societe_id)
3957  {
3958  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3959  $sql.= " WHERE sc.fk_user = " .$user->id;
3960  $clause = "AND";
3961  }
3962  $sql.= " ".$clause." f.entity = ".$conf->entity;
3963 
3964  $resql=$this->db->query($sql);
3965  if ($resql)
3966  {
3967  while ($obj=$this->db->fetch_object($resql))
3968  {
3969  $this->nb["invoices"]=$obj->nb;
3970  }
3971  $this->db->free($resql);
3972  return 1;
3973  }
3974  else
3975  {
3976  dol_print_error($this->db);
3977  $this->error=$this->db->error();
3978  return -1;
3979  }
3980  }
3981 
3987  function getLinesArray()
3988  {
3989  return $this->fetch_lines();
3990  }
3991 
4002  public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
4003  {
4004  global $conf,$langs;
4005 
4006  $langs->load("bills");
4007 
4008  if (! dol_strlen($modele)) {
4009 
4010  $modele = 'crabe';
4011 
4012  if ($this->modelpdf) {
4013  $modele = $this->modelpdf;
4014  } elseif (! empty($conf->global->FACTURE_ADDON_PDF)) {
4015  $modele = $conf->global->FACTURE_ADDON_PDF;
4016  }
4017  }
4018 
4019  $modelpath = "core/modules/facture/doc/";
4020 
4021  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
4022  }
4023 
4029  function newCycle()
4030  {
4031  $sql = 'SELECT max(situation_cycle_ref) FROM ' . MAIN_DB_PREFIX . 'facture as f';
4032  $sql.= " WHERE f.entity in (".getEntity('facture', 0).")";
4033  $resql = $this->db->query($sql);
4034  if ($resql) {
4035  if ($resql->num_rows > 0)
4036  {
4037  $res = $this->db->fetch_array($resql);
4038  $ref = $res['max(situation_cycle_ref)'];
4039  $ref++;
4040  } else {
4041  $ref = 1;
4042  }
4043  $this->db->free($resql);
4044  return $ref;
4045  } else {
4046  $this->error = $this->db->lasterror();
4047  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4048  return -1;
4049  }
4050  }
4051 
4057  function is_first()
4058  {
4059  return ($this->situation_counter == 1);
4060  }
4061 
4067  function get_prev_sits()
4068  {
4069  global $conf;
4070 
4071  $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . 'facture';
4072  $sql .= ' where situation_cycle_ref = ' . $this->situation_cycle_ref;
4073  $sql .= ' and situation_counter < ' . $this->situation_counter;
4074  $sql .= ' AND entity = '. ($this->entity > 0 ? $this->entity : $conf->entity);
4075  $resql = $this->db->query($sql);
4076  $res = array();
4077  if ($resql && $resql->num_rows > 0) {
4078  while ($row = $this->db->fetch_object($resql)) {
4079  $id = $row->rowid;
4080  $situation = new Facture($this->db);
4081  $situation->fetch($id);
4082  $res[] = $situation;
4083  }
4084  } else {
4085  $this->error = $this->db->error();
4086  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4087  return -1;
4088  }
4089 
4090  return $res;
4091  }
4092 
4100  function setFinal(User $user, $notrigger=0)
4101  {
4102  $error=0;
4103 
4104  $this->db->begin();
4105 
4106  $this->situation_final = 1;
4107  $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET situation_final = ' . $this->situation_final . ' where rowid = ' . $this->id;
4108 
4109  dol_syslog(__METHOD__, LOG_DEBUG);
4110  $resql=$this->db->query($sql);
4111  if (!$resql)
4112  {
4113  $this->errors[]=$this->db->error();
4114  $error++;
4115  }
4116 
4117  if (! $notrigger && empty($error))
4118  {
4119  // Call trigger
4120  $result=$this->call_trigger('BILL_MODIFY',$user);
4121  if ($result < 0) $error++;
4122  // End call triggers
4123  }
4124 
4125  if (! $error)
4126  {
4127  $this->db->commit();
4128  return 1;
4129  }
4130  else
4131  {
4132  foreach($this->errors as $errmsg)
4133  {
4134  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
4135  $this->error.=($this->error?', '.$errmsg:$errmsg);
4136  }
4137  $this->db->rollback();
4138  return -1*$error;
4139  }
4140  }
4141 
4148  function is_last_in_cycle()
4149  {
4150  global $conf;
4151 
4152  if (!empty($this->situation_cycle_ref)) {
4153  // No point in testing anything if we're not inside a cycle
4154  $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);
4155  $resql = $this->db->query($sql);
4156 
4157  if ($resql && $resql->num_rows > 0) {
4158  $res = $this->db->fetch_array($resql);
4159  $last = $res['max(situation_counter)'];
4160  return ($last == $this->situation_counter);
4161  } else {
4162  $this->error = $this->db->lasterror();
4163  dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR);
4164  return false;
4165  }
4166  } else {
4167  return true;
4168  }
4169  }
4170 
4179  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
4180  {
4181  $tables = array(
4182  'facture'
4183  );
4184 
4185  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
4186  }
4187 
4193  public function hasDelay()
4194  {
4195  global $conf;
4196 
4197  $now = dol_now();
4198 
4199  // Paid invoices have status STATUS_CLOSED
4200  if ($this->statut != Facture::STATUS_VALIDATED) return false;
4201 
4202  return $this->date_lim_reglement < ($now - $conf->facture->client->warning_delay);
4203  }
4204 }
4205 
4211 {
4212  public $element='facturedet';
4213  public $table_element='facturedet';
4214 
4215  var $oldline;
4216 
4225  var $label;
4227  var $desc;
4228 
4229  var $localtax1_type; // Local tax 1 type
4230  var $localtax2_type; // Local tax 2 type
4231  var $fk_remise_except; // Link to line into llx_remise_except
4232  var $rang = 0;
4233 
4234  var $fk_fournprice;
4235  var $pa_ht;
4236  var $marge_tx;
4237  var $marque_tx;
4238 
4239  var $special_code; // Liste d'options non cumulabels:
4240  // 1: frais de port
4241  // 2: ecotaxe
4242  // 3: ??
4243 
4244  var $origin;
4245  var $origin_id;
4246 
4247  var $fk_code_ventilation = 0;
4248 
4249  var $date_start;
4250  var $date_end;
4251 
4252  // Ne plus utiliser
4253  //var $price; // P.U. HT apres remise % de ligne (exemple 80)
4254  //var $remise; // Montant calcule de la remise % sur PU HT (exemple 20)
4255 
4256  // From llx_product
4261  var $ref; // Product ref (deprecated)
4262  var $product_ref; // Product ref
4267  var $libelle; // Product label (deprecated)
4268  var $product_label; // Product label
4269  var $product_desc; // Description produit
4270 
4271  var $skip_update_total; // Skip update price total for special lines
4272 
4276  public $situation_percent;
4277 
4281  public $fk_prev_id;
4282 
4283  // Multicurrency
4284  var $fk_multicurrency;
4285  var $multicurrency_code;
4286  var $multicurrency_subprice;
4287  var $multicurrency_total_ht;
4288  var $multicurrency_total_tva;
4289  var $multicurrency_total_ttc;
4290 
4297  function fetch($rowid)
4298  {
4299  $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,';
4300  $sql.= ' fd.localtax1_tx, fd. localtax2_tx, fd.remise, fd.remise_percent, fd.fk_remise_except, fd.subprice,';
4301  $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,';
4302  $sql.= ' fd.info_bits, fd.special_code, fd.total_ht, fd.total_tva, fd.total_ttc, fd.total_localtax1, fd.total_localtax2, fd.rang,';
4303  $sql.= ' fd.fk_code_ventilation,';
4304  $sql.= ' fd.fk_unit, fd.fk_user_author, fd.fk_user_modif,';
4305  $sql.= ' fd.situation_percent, fd.fk_prev_id,';
4306  $sql.= ' fd.multicurrency_subprice,';
4307  $sql.= ' fd.multicurrency_total_ht,';
4308  $sql.= ' fd.multicurrency_total_tva,';
4309  $sql.= ' fd.multicurrency_total_ttc,';
4310  $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc';
4311  $sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet as fd';
4312  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON fd.fk_product = p.rowid';
4313  $sql.= ' WHERE fd.rowid = '.$rowid;
4314 
4315  $result = $this->db->query($sql);
4316  if ($result)
4317  {
4318  $objp = $this->db->fetch_object($result);
4319 
4320  $this->rowid = $objp->rowid;
4321  $this->fk_facture = $objp->fk_facture;
4322  $this->fk_parent_line = $objp->fk_parent_line;
4323  $this->label = $objp->custom_label;
4324  $this->desc = $objp->description;
4325  $this->qty = $objp->qty;
4326  $this->subprice = $objp->subprice;
4327  $this->vat_src_code = $objp->vat_src_code;
4328  $this->tva_tx = $objp->tva_tx;
4329  $this->localtax1_tx = $objp->localtax1_tx;
4330  $this->localtax2_tx = $objp->localtax2_tx;
4331  $this->remise_percent = $objp->remise_percent;
4332  $this->fk_remise_except = $objp->fk_remise_except;
4333  $this->fk_product = $objp->fk_product;
4334  $this->product_type = $objp->product_type;
4335  $this->date_start = $this->db->jdate($objp->date_start);
4336  $this->date_end = $this->db->jdate($objp->date_end);
4337  $this->info_bits = $objp->info_bits;
4338  $this->tva_npr = ($objp->info_bits & 1 == 1) ? 1 : 0;
4339  $this->special_code = $objp->special_code;
4340  $this->total_ht = $objp->total_ht;
4341  $this->total_tva = $objp->total_tva;
4342  $this->total_localtax1 = $objp->total_localtax1;
4343  $this->total_localtax2 = $objp->total_localtax2;
4344  $this->total_ttc = $objp->total_ttc;
4345  $this->fk_code_ventilation = $objp->fk_code_ventilation;
4346  $this->rang = $objp->rang;
4347  $this->fk_fournprice = $objp->fk_fournprice;
4348  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4349  $this->pa_ht = $marginInfos[0];
4350  $this->marge_tx = $marginInfos[1];
4351  $this->marque_tx = $marginInfos[2];
4352 
4353  $this->ref = $objp->product_ref; // deprecated
4354  $this->product_ref = $objp->product_ref;
4355  $this->libelle = $objp->product_libelle; // deprecated
4356  $this->product_label = $objp->product_libelle;
4357  $this->product_desc = $objp->product_desc;
4358  $this->fk_unit = $objp->fk_unit;
4359  $this->fk_user_modif = $objp->fk_user_modif;
4360  $this->fk_user_author = $objp->fk_user_author;
4361 
4362  $this->situation_percent = $objp->situation_percent;
4363  $this->fk_prev_id = $objp->fk_prev_id;
4364 
4365  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4366  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4367  $this->multicurrency_total_tva= $objp->multicurrency_total_tva;
4368  $this->multicurrency_total_ttc= $objp->multicurrency_total_ttc;
4369 
4370  $this->db->free($result);
4371 
4372  return 1;
4373  }
4374  else
4375  {
4376  $this->error = $this->db->lasterror();
4377  return -1;
4378  }
4379  }
4380 
4388  function insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
4389  {
4390  global $langs,$user,$conf;
4391 
4392  $error=0;
4393 
4394  $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'.
4395 
4396  dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4397 
4398  // Clean parameters
4399  $this->desc=trim($this->desc);
4400  if (empty($this->tva_tx)) $this->tva_tx=0;
4401  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4402  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4403  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4404  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4405  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4406  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4407  if (empty($this->rang)) $this->rang=0;
4408  if (empty($this->remise_percent)) $this->remise_percent=0;
4409  if (empty($this->info_bits)) $this->info_bits=0;
4410  if (empty($this->subprice)) $this->subprice=0;
4411  if (empty($this->special_code)) $this->special_code=0;
4412  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4413  if (empty($this->fk_prev_id)) $this->fk_prev_id = 'null';
4414  if (! isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100;
4415 
4416  if (empty($this->pa_ht)) $this->pa_ht=0;
4417  if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice=0;
4418  if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht=0;
4419  if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
4420  if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
4421 
4422  // if buy price not defined, define buyprice as configured in margin admin
4423  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4424  {
4425  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4426  {
4427  return $result;
4428  }
4429  else
4430  {
4431  $this->pa_ht = $result;
4432  }
4433  }
4434 
4435  // Check parameters
4436  if ($this->product_type < 0)
4437  {
4438  $this->error='ErrorProductTypeMustBe0orMore';
4439  return -1;
4440  }
4441  if (! empty($this->fk_product))
4442  {
4443  // Check product exists
4444  $result=Product::isExistingObject('product', $this->fk_product);
4445  if ($result <= 0)
4446  {
4447  $this->error='ErrorProductIdDoesNotExists';
4448  return -1;
4449  }
4450  }
4451 
4452  $this->db->begin();
4453 
4454  // Insertion dans base de la ligne
4455  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facturedet';
4456  $sql.= ' (fk_facture, fk_parent_line, label, description, qty,';
4457  $sql.= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4458  $sql.= ' fk_product, product_type, remise_percent, subprice, fk_remise_except,';
4459  $sql.= ' date_start, date_end, fk_code_ventilation, ';
4460  $sql.= ' rang, special_code, fk_product_fournisseur_price, buy_price_ht,';
4461  $sql.= ' info_bits, total_ht, total_tva, total_ttc, total_localtax1, total_localtax2,';
4462  $sql.= ' situation_percent, fk_prev_id,';
4463  $sql.= ' fk_unit, fk_user_author, fk_user_modif,';
4464  $sql.= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4465  $sql.= ')';
4466  $sql.= " VALUES (".$this->fk_facture.",";
4467  $sql.= " ".($this->fk_parent_line>0 ? $this->fk_parent_line:"null").",";
4468  $sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4469  $sql.= " '".$this->db->escape($this->desc)."',";
4470  $sql.= " ".price2num($this->qty).",";
4471  $sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4472  $sql.= " ".price2num($this->tva_tx).",";
4473  $sql.= " ".price2num($this->localtax1_tx).",";
4474  $sql.= " ".price2num($this->localtax2_tx).",";
4475  $sql.= " '".$this->db->escape($this->localtax1_type)."',";
4476  $sql.= " '".$this->db->escape($this->localtax2_type)."',";
4477  $sql.= ' '.(! empty($this->fk_product)?$this->fk_product:"null").',';
4478  $sql.= " ".$this->product_type.",";
4479  $sql.= " ".price2num($this->remise_percent).",";
4480  $sql.= " ".price2num($this->subprice).",";
4481  $sql.= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
4482  $sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").",";
4483  $sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").",";
4484  $sql.= ' '.$this->fk_code_ventilation.',';
4485  $sql.= ' '.$this->rang.',';
4486  $sql.= ' '.$this->special_code.',';
4487  $sql.= ' '.(! empty($this->fk_fournprice)?$this->fk_fournprice:"null").',';
4488  $sql.= ' '.price2num($this->pa_ht).',';
4489  $sql.= " '".$this->db->escape($this->info_bits)."',";
4490  $sql.= " ".price2num($this->total_ht).",";
4491  $sql.= " ".price2num($this->total_tva).",";
4492  $sql.= " ".price2num($this->total_ttc).",";
4493  $sql.= " ".price2num($this->total_localtax1).",";
4494  $sql.= " ".price2num($this->total_localtax2);
4495  $sql.= ", " . $this->situation_percent;
4496  $sql.= ", " . $this->fk_prev_id;
4497  $sql.= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4498  $sql.= ", ".$user->id;
4499  $sql.= ", ".$user->id;
4500  $sql.= ", ".(int) $this->fk_multicurrency;
4501  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4502  $sql.= ", ".price2num($this->multicurrency_subprice);
4503  $sql.= ", ".price2num($this->multicurrency_total_ht);
4504  $sql.= ", ".price2num($this->multicurrency_total_tva);
4505  $sql.= ", ".price2num($this->multicurrency_total_ttc);
4506  $sql.= ')';
4507 
4508  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4509  $resql=$this->db->query($sql);
4510  if ($resql)
4511  {
4512  $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'facturedet');
4513 
4514  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4515  {
4516  $this->id=$this->rowid;
4517  $result=$this->insertExtraFields();
4518  if ($result < 0)
4519  {
4520  $error++;
4521  }
4522  }
4523 
4524  // Si fk_remise_except defini, on lie la remise a la facture
4525  // ce qui la flague comme "consommee".
4526  if ($this->fk_remise_except)
4527  {
4528  $discount=new DiscountAbsolute($this->db);
4529  $result=$discount->fetch($this->fk_remise_except);
4530  if ($result >= 0)
4531  {
4532  // Check if discount was found
4533  if ($result > 0)
4534  {
4535  // Check if discount not already affected to another invoice
4536  if ($discount->fk_facture_line > 0)
4537  {
4538  if (empty($noerrorifdiscountalreadylinked))
4539  {
4540  $this->error=$langs->trans("ErrorDiscountAlreadyUsed",$discount->id);
4541  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4542  $this->db->rollback();
4543  return -3;
4544  }
4545  }
4546  else
4547  {
4548  $result=$discount->link_to_invoice($this->rowid,0);
4549  if ($result < 0)
4550  {
4551  $this->error=$discount->error;
4552  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4553  $this->db->rollback();
4554  return -3;
4555  }
4556  }
4557  }
4558  else
4559  {
4560  $this->error=$langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4561  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4562  $this->db->rollback();
4563  return -3;
4564  }
4565  }
4566  else
4567  {
4568  $this->error=$discount->error;
4569  dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4570  $this->db->rollback();
4571  return -3;
4572  }
4573  }
4574 
4575  if (! $notrigger)
4576  {
4577  // Call trigger
4578  $result=$this->call_trigger('LINEBILL_INSERT',$user);
4579  if ($result < 0)
4580  {
4581  $this->db->rollback();
4582  return -2;
4583  }
4584  // End call triggers
4585  }
4586 
4587  $this->db->commit();
4588  return $this->rowid;
4589 
4590  }
4591  else
4592  {
4593  $this->error=$this->db->error();
4594  $this->db->rollback();
4595  return -2;
4596  }
4597  }
4598 
4606  function update($user='',$notrigger=0)
4607  {
4608  global $user,$conf;
4609 
4610  $error=0;
4611 
4612  $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'.
4613 
4614  // Clean parameters
4615  $this->desc=trim($this->desc);
4616  if (empty($this->tva_tx)) $this->tva_tx=0;
4617  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4618  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4619  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4620  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4621  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4622  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4623  if (empty($this->remise_percent)) $this->remise_percent=0;
4624  if (empty($this->info_bits)) $this->info_bits=0;
4625  if (empty($this->special_code)) $this->special_code=0;
4626  if (empty($this->product_type)) $this->product_type=0;
4627  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4628  if (! isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100;
4629  if (empty($this->pa_ht)) $this->pa_ht=0;
4630 
4631  if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice=0;
4632  if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht=0;
4633  if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
4634  if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
4635 
4636  // Check parameters
4637  if ($this->product_type < 0) return -1;
4638 
4639  // if buy price not defined, define buyprice as configured in margin admin
4640  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4641  {
4642  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4643  {
4644  return $result;
4645  }
4646  else
4647  {
4648  $this->pa_ht = $result;
4649  }
4650  }
4651 
4652  $this->db->begin();
4653 
4654  // Mise a jour ligne en base
4655  $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
4656  $sql.= " description='".$this->db->escape($this->desc)."'";
4657  $sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4658  $sql.= ", subprice=".price2num($this->subprice)."";
4659  $sql.= ", remise_percent=".price2num($this->remise_percent)."";
4660  if ($this->fk_remise_except) $sql.= ", fk_remise_except=".$this->fk_remise_except;
4661  else $sql.= ", fk_remise_except=null";
4662  $sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->db->escape($this->vat_src_code))."'";
4663  $sql.= ", tva_tx=".price2num($this->tva_tx)."";
4664  $sql.= ", localtax1_tx=".price2num($this->localtax1_tx)."";
4665  $sql.= ", localtax2_tx=".price2num($this->localtax2_tx)."";
4666  $sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4667  $sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4668  $sql.= ", qty=".price2num($this->qty);
4669  $sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4670  $sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4671  $sql.= ", product_type=".$this->product_type;
4672  $sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4673  $sql.= ", special_code='".$this->db->escape($this->special_code)."'";
4674  if (empty($this->skip_update_total))
4675  {
4676  $sql.= ", total_ht=".price2num($this->total_ht)."";
4677  $sql.= ", total_tva=".price2num($this->total_tva)."";
4678  $sql.= ", total_ttc=".price2num($this->total_ttc)."";
4679  $sql.= ", total_localtax1=".price2num($this->total_localtax1)."";
4680  $sql.= ", total_localtax2=".price2num($this->total_localtax2)."";
4681  }
4682  $sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4683  $sql.= ", buy_price_ht='".price2num($this->pa_ht)."'";
4684  $sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4685  if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4686  $sql.= ", situation_percent=" . $this->situation_percent;
4687  $sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4688  $sql.= ", fk_user_modif =".$user->id;
4689 
4690  // Multicurrency
4691  $sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4692  $sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4693  $sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4694  $sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4695 
4696  $sql.= " WHERE rowid = ".$this->rowid;
4697 
4698  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4699  $resql=$this->db->query($sql);
4700  if ($resql)
4701  {
4702  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4703  {
4704  $this->id=$this->rowid;
4705  $result=$this->insertExtraFields();
4706  if ($result < 0)
4707  {
4708  $error++;
4709  }
4710  }
4711 
4712  if (! $notrigger)
4713  {
4714  // Call trigger
4715  $result=$this->call_trigger('LINEBILL_UPDATE',$user);
4716  if ($result < 0)
4717  {
4718  $this->db->rollback();
4719  return -2;
4720  }
4721  // End call triggers
4722  }
4723  $this->db->commit();
4724  return 1;
4725  }
4726  else
4727  {
4728  $this->error=$this->db->error();
4729  $this->db->rollback();
4730  return -2;
4731  }
4732  }
4733 
4740  function delete()
4741  {
4742  global $user;
4743 
4744  $this->db->begin();
4745 
4746  // Call trigger
4747  $result=$this->call_trigger('LINEBILL_DELETE',$user);
4748  if ($result < 0)
4749  {
4750  $this->db->rollback();
4751  return -1;
4752  }
4753  // End call triggers
4754 
4755 
4756  $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".$this->rowid;
4757  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4758  if ($this->db->query($sql) )
4759  {
4760  $this->db->commit();
4761  return 1;
4762  }
4763  else
4764  {
4765  $this->error=$this->db->error()." sql=".$sql;
4766  $this->db->rollback();
4767  return -1;
4768  }
4769  }
4770 
4776  function update_total()
4777  {
4778  $this->db->begin();
4779  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
4780 
4781  // Clean parameters
4782  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4783  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4784 
4785  // Mise a jour ligne en base
4786  $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
4787  $sql.= " total_ht=".price2num($this->total_ht)."";
4788  $sql.= ",total_tva=".price2num($this->total_tva)."";
4789  $sql.= ",total_localtax1=".price2num($this->total_localtax1)."";
4790  $sql.= ",total_localtax2=".price2num($this->total_localtax2)."";
4791  $sql.= ",total_ttc=".price2num($this->total_ttc)."";
4792  $sql.= " WHERE rowid = ".$this->rowid;
4793 
4794  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
4795 
4796  $resql=$this->db->query($sql);
4797  if ($resql)
4798  {
4799  $this->db->commit();
4800  return 1;
4801  }
4802  else
4803  {
4804  $this->error=$this->db->error();
4805  $this->db->rollback();
4806  return -2;
4807  }
4808  }
4809 
4817  function get_prev_progress($invoiceid)
4818  {
4819  if (is_null($this->fk_prev_id) || empty($this->fk_prev_id) || $this->fk_prev_id == "") {
4820  return 0;
4821  } else {
4822  // If invoice is a not a situation invoice, this->fk_prev_id is used for something else
4823  $tmpinvoice=new Facture($this->db);
4824  $tmpinvoice->fetch($invoiceid);
4825  if ($tmpinvoice->type != Facture::TYPE_SITUATION) return 0;
4826 
4827  $sql = 'SELECT situation_percent FROM ' . MAIN_DB_PREFIX . 'facturedet WHERE rowid=' . $this->fk_prev_id;
4828  $resql = $this->db->query($sql);
4829  if ($resql && $resql->num_rows > 0) {
4830  $res = $this->db->fetch_array($resql);
4831  return $res['situation_percent'];
4832  } else {
4833  $this->error = $this->db->error();
4834  dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR);
4835  $this->db->rollback();
4836  return -1;
4837  }
4838  }
4839  }
4840 }
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.
set_draft($user, $idwarehouse=-1)
Set draft status.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it's its name (generic function)
const TYPE_STANDARD
Standard invoice.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
static getIdFromCode(&$db, $code)
Get id of currency from code.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1)
Fetch array of objects linked to current object.
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.
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -> yes If hi...
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.
$close_note
Commentaire si mis a paye sans paiement complet.
getListOfPayments($filtertype='')
Return list of payments.
Class to manage products or services.
update($user=null, $notrigger=0)
Update database.
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:1283
Class to manage invoice templates.
insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
Insert line into database.
Class to manage Dolibarr users.
Definition: user.class.php:39
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0)
Return an id or code from a code or id.
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='', $fk_unit=null, $pu_ht_devise=0)
Add an invoice line into database (linked to product/service or not).
const TYPE_REPLACEMENT
Replacement invoice.
Class to manage Dolibarr database access.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
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.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
if(empty($reshook)) $form
View.
Definition: perms.php:103
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.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller Note: This function applies same rules than get_default_tva.
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.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="")
Scan a directory and return a list of files/directories.
Definition: files.lib.php:58
$desc
Description ligne.
const TYPE_CREDIT_NOTE
Credit note invoice.
GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NULL, $noreplace=0)
Return value of a param into GET or POST supervariable.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
updatePriceNextInvoice(&$langs)
Update price of next invoice.
Class to manage standard extra fields.
$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.
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.
get_prev_progress($invoiceid)
Returns situation_percent of the previous line.
update($user='', $notrigger=0)
Update line into database.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
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 and deposits invoices used by invoice.
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.
set_remise_absolue($user, $remise, $notrigger=0)
Set absolute discount.
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:1234
getEntity($element, $shared=1, $forceentity=null)
Get list of entity id to use.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
deleteExtraFields()
Delete all extra fields values for the current object.
Class to manage translations.
createFromClone($socid=0)
Load an object from its id and create a new one in database.
const STATUS_CLOSED
Classified paid.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
dol_now($mode='gmt')
Return date for now.
newCycle()
Gets the smallest reference available for a new cycle.
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...
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
Superclass for invoices classes.
const STATUS_ABANDONED
Classified abandoned and no payment done.
setPaymentMethods($id)
Change the payments methods.
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
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8')
Clean a string from all HTML tags and entities.
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.
print
Draft customers invoices.
Definition: index.php:91
hasDelay()
Is the customer invoice delayed?
create($user, $notrigger=0, $forceduedate=0)
Create invoice in database.
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...
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) Si ...
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:1013
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.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null)
Remove a file or several files with a mask.
Definition: files.lib.php:1103
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 substition into a text string, replacing keys with vals from $substitutionarray (oldval=>newval)...
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
$fk_facture
From llx_facturedet Id facture.
type
Definition: viewcat.php:283
Class to manage ECM files.
update_total()
Mise a jour en base des champs total_xxx de ligne de facture.
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 '...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
__construct($db)
Constructor.
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.