dolibarr  7.0.0-beta
propal.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4  * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6  * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@capnetworks.com>
7  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8  * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9  * Copyright (C) 2010-2014 Juanjo Menent <jmenent@2byte.es>
10  * Copyright (C) 2010-2017 Philippe Grand <philippe.grand@atoo-net.com>
11  * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
13  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
14  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
28  */
29 
35 require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
36 require_once DOL_DOCUMENT_ROOT ."/core/class/commonobjectline.class.php";
37 require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
38 require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php';
39 require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
40 require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
41 
45 class Propal extends CommonObject
46 {
47  public $element='propal';
48  public $table_element='propal';
49  public $table_element_line='propaldet';
50  public $fk_element='fk_propal';
51  public $picto='propal';
56  public $ismultientitymanaged = 1;
61  public $restrictiononfksoc = 1;
62 
66  protected $table_ref_field = 'ref';
67 
72  public $socid;
73 
74  public $contactid;
75  public $author;
76  public $ref_client;
77 
83  public $statut;
84 
89  public $datec;
90 
95  public $date_creation;
96 
101  public $datev;
102 
107  public $date_validation;
108 
113  public $date;
114 
119  public $datep;
120  public $date_livraison;
121  public $fin_validite;
122 
123  public $user_author_id;
124  public $user_valid_id;
125  public $user_close_id;
126 
131  public $price;
136  public $tva;
141  public $total;
142 
143  public $cond_reglement_code;
144  public $mode_reglement_code;
145  public $remise;
146  public $remise_percent;
147  public $remise_absolue;
148  public $fk_address;
149  public $address_type;
150  public $address;
151  public $availability_id;
152  public $availability_code;
153  public $demand_reason_id;
154  public $demand_reason_code;
155 
156  public $products=array();
157  public $extraparams=array();
158 
162  public $lines = array();
163  public $line;
164 
165  public $labelstatut=array();
166  public $labelstatut_short=array();
167 
168  public $specimen;
169 
170  // Multicurrency
171  public $fk_multicurrency;
172  public $multicurrency_code;
173  public $multicurrency_tx;
174  public $multicurrency_total_ht;
175  public $multicurrency_total_tva;
176  public $multicurrency_total_ttc;
177 
178  public $oldcopy;
179 
183  const STATUS_DRAFT = 0;
187  const STATUS_VALIDATED = 1;
191  const STATUS_SIGNED = 2;
195  const STATUS_NOTSIGNED = 3;
199  const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
200 
208  function __construct($db, $socid="", $propalid=0)
209  {
210  global $conf,$langs;
211 
212  $this->db = $db;
213  $this->socid = $socid;
214  $this->id = $propalid;
215  $this->products = array();
216  $this->remise = 0;
217  $this->remise_percent = 0;
218  $this->remise_absolue = 0;
219 
220  $this->duree_validite=$conf->global->PROPALE_VALIDITY_DURATION;
221 
222  $langs->load("propal");
223  $this->labelstatut[0]=(! empty($conf->global->PROPAL_STATUS_DRAFT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFT_LABEL : $langs->trans("PropalStatusDraft"));
224  $this->labelstatut[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATED_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATED_LABEL : $langs->trans("PropalStatusValidated"));
225  $this->labelstatut[2]=(! empty($conf->global->PROPAL_STATUS_SIGNED_LABEL) ? $conf->global->PROPAL_STATUS_SIGNED_LABEL : $langs->trans("PropalStatusSigned"));
226  $this->labelstatut[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNED_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNED_LABEL : $langs->trans("PropalStatusNotSigned"));
227  $this->labelstatut[4]=(! empty($conf->global->PROPAL_STATUS_BILLED_LABEL) ? $conf->global->PROPAL_STATUS_BILLED_LABEL : $langs->trans("PropalStatusBilled"));
228  $this->labelstatut_short[0]=(! empty($conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL : $langs->trans("PropalStatusDraftShort"));
229  $this->labelstatut_short[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL : $langs->trans("Opened"));
230  $this->labelstatut_short[2]=(! empty($conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL : $langs->trans("PropalStatusSignedShort"));
231  $this->labelstatut_short[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL : $langs->trans("PropalStatusNotSignedShort"));
232  $this->labelstatut_short[4]=(! empty($conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL : $langs->trans("PropalStatusBilledShort"));
233  }
234 
235 
248  function add_product($idproduct, $qty, $remise_percent=0)
249  {
250  global $conf, $mysoc;
251 
252  if (! $qty) $qty = 1;
253 
254  dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
255  if ($idproduct > 0)
256  {
257  $prod=new Product($this->db);
258  $prod->fetch($idproduct);
259 
260  $productdesc = $prod->description;
261 
262  $tva_tx = get_default_tva($mysoc,$this->thirdparty,$prod->id);
263  $tva_npr = get_default_npr($mysoc,$this->thirdparty,$prod->id);
264  if (empty($tva_tx)) $tva_npr=0;
265  $vat_src_code = ''; // May be defined into tva_tx
266 
267  $localtax1_tx = get_localtax($tva_tx,1,$mysoc,$this->thirdparty,$tva_npr);
268  $localtax2_tx = get_localtax($tva_tx,2,$mysoc,$this->thirdparty,$tva_npr);
269 
270  // multiprices
271  if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level)
272  {
273  $price = $prod->multiprices[$this->thirdparty->price_level];
274  }
275  else
276  {
277  $price = $prod->price;
278  }
279 
280  $line = new PropaleLigne($this->db);
281 
282  $line->fk_product=$idproduct;
283  $line->desc=$productdesc;
284  $line->qty=$qty;
285  $line->subprice=$price;
286  $line->remise_percent=$remise_percent;
287  $line->vat_src_code=$vat_src_code;
288  $line->tva_tx=$tva_tx;
289  $line->fk_unit=$prod->fk_unit;
290  if ($tva_npr) $line->info_bits = 1;
291 
292  $this->lines[]=$line;
293  }
294  }
295 
302  function insert_discount($idremise)
303  {
304  global $langs;
305 
306  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
307  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
308 
309  $this->db->begin();
310 
311  $remise=new DiscountAbsolute($this->db);
312  $result=$remise->fetch($idremise);
313 
314  if ($result > 0)
315  {
316  if ($remise->fk_facture) // Protection against multiple submission
317  {
318  $this->error=$langs->trans("ErrorDiscountAlreadyUsed");
319  $this->db->rollback();
320  return -5;
321  }
322 
323  $line=new PropaleLigne($this->db);
324 
325  $this->line->context = $this->context;
326 
327  $line->fk_propal=$this->id;
328  $line->fk_remise_except=$remise->id;
329  $line->desc=$remise->description; // Description ligne
330  $line->vat_src_code=$remise->vat_src_code;
331  $line->tva_tx=$remise->tva_tx;
332  $line->subprice=-$remise->amount_ht;
333  $line->fk_product=0; // Id produit predefined
334  $line->qty=1;
335  $line->remise=0;
336  $line->remise_percent=0;
337  $line->rang=-1;
338  $line->info_bits=2;
339 
340  // TODO deprecated
341  $line->price=-$remise->amount_ht;
342 
343  $line->total_ht = -$remise->amount_ht;
344  $line->total_tva = -$remise->amount_tva;
345  $line->total_ttc = -$remise->amount_ttc;
346 
347  $result=$line->insert();
348  if ($result > 0)
349  {
350  $result=$this->update_price(1);
351  if ($result > 0)
352  {
353  $this->db->commit();
354  return 1;
355  }
356  else
357  {
358  $this->db->rollback();
359  return -1;
360  }
361  }
362  else
363  {
364  $this->error=$line->error;
365  $this->db->rollback();
366  return -2;
367  }
368  }
369  else
370  {
371  $this->db->rollback();
372  return -2;
373  }
374  }
375 
412  function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='',$date_start='', $date_end='',$array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0, $fk_remise_except=0)
413  {
414  global $mysoc, $conf, $langs;
415 
416  dol_syslog(get_class($this)."::addline propalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type, fk_remise_except=".$fk_remise_except);
417  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
418 
419  // Clean parameters
420  if (empty($remise_percent)) $remise_percent=0;
421  if (empty($qty)) $qty=0;
422  if (empty($info_bits)) $info_bits=0;
423  if (empty($rang)) $rang=0;
424  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
425 
426  $remise_percent=price2num($remise_percent);
427  $qty=price2num($qty);
428  $pu_ht=price2num($pu_ht);
429  $pu_ttc=price2num($pu_ttc);
430  $txtva=price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
431  $txlocaltax1=price2num($txlocaltax1);
432  $txlocaltax2=price2num($txlocaltax2);
433  $pa_ht=price2num($pa_ht);
434  if ($price_base_type=='HT')
435  {
436  $pu=$pu_ht;
437  }
438  else
439  {
440  $pu=$pu_ttc;
441  }
442 
443  // Check parameters
444  if ($type < 0) return -1;
445 
446  if ($this->statut == self::STATUS_DRAFT)
447  {
448  $this->db->begin();
449 
450  $product_type=$type;
451  if (!empty($fk_product))
452  {
453  $product=new Product($this->db);
454  $result=$product->fetch($fk_product);
455  $product_type=$product->type;
456 
457  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL) && $product_type == 0 && $product->stock_reel < $qty) {
458  $langs->load("errors");
459  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
460  $this->db->rollback();
461  return -3;
462  }
463  }
464 
465  // Calcul du total TTC et de la TVA pour la ligne a partir de
466  // qty, pu, remise_percent et txtva
467  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
468  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
469 
470  $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
471 
472  // Clean vat code
473  $vat_src_code='';
474  if (preg_match('/\((.*)\)/', $txtva, $reg))
475  {
476  $vat_src_code = $reg[1];
477  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
478  }
479 
480  $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
481 
482  $total_ht = $tabprice[0];
483  $total_tva = $tabprice[1];
484  $total_ttc = $tabprice[2];
485  $total_localtax1 = $tabprice[9];
486  $total_localtax2 = $tabprice[10];
487  $pu_ht = $tabprice[3];
488  $pu_tva = $tabprice[4];
489  $pu_ttc = $tabprice[5];
490 
491  // MultiCurrency
492  $multicurrency_total_ht = $tabprice[16];
493  $multicurrency_total_tva = $tabprice[17];
494  $multicurrency_total_ttc = $tabprice[18];
495  $pu_ht_devise = $tabprice[19];
496 
497  // Rang to use
498  $rangtouse = $rang;
499  if ($rangtouse == -1)
500  {
501  $rangmax = $this->line_max($fk_parent_line);
502  $rangtouse = $rangmax + 1;
503  }
504 
505  // TODO A virer
506  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
507  $price = $pu;
508  $remise = 0;
509  if ($remise_percent > 0)
510  {
511  $remise = round(($pu * $remise_percent / 100), 2);
512  $price = $pu - $remise;
513  }
514 
515  // Insert line
516  $this->line=new PropaleLigne($this->db);
517 
518  $this->line->context = $this->context;
519 
520  $this->line->fk_propal=$this->id;
521  $this->line->label=$label;
522  $this->line->desc=$desc;
523  $this->line->qty=$qty;
524 
525  $this->line->vat_src_code=$vat_src_code;
526  $this->line->tva_tx=$txtva;
527  $this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
528  $this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
529  $this->line->localtax1_type = $localtaxes_type[0];
530  $this->line->localtax2_type = $localtaxes_type[2];
531  $this->line->fk_product=$fk_product;
532  $this->line->product_type=$type;
533  $this->line->fk_remise_except=$fk_remise_except;
534  $this->line->remise_percent=$remise_percent;
535  $this->line->subprice=$pu_ht;
536  $this->line->rang=$rangtouse;
537  $this->line->info_bits=$info_bits;
538  $this->line->total_ht=$total_ht;
539  $this->line->total_tva=$total_tva;
540  $this->line->total_localtax1=$total_localtax1;
541  $this->line->total_localtax2=$total_localtax2;
542  $this->line->total_ttc=$total_ttc;
543  $this->line->special_code=$special_code;
544  $this->line->fk_parent_line=$fk_parent_line;
545  $this->line->fk_unit=$fk_unit;
546 
547  $this->line->date_start=$date_start;
548  $this->line->date_end=$date_end;
549 
550  $this->line->fk_fournprice = $fk_fournprice;
551  $this->line->pa_ht = $pa_ht;
552 
553  $this->line->origin_id = $origin_id;
554  $this->line->origin = $origin;
555 
556  // Multicurrency
557  $this->line->fk_multicurrency = $this->fk_multicurrency;
558  $this->line->multicurrency_code = $this->multicurrency_code;
559  $this->line->multicurrency_subprice = $pu_ht_devise;
560  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
561  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
562  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
563 
564  // Mise en option de la ligne
565  if (empty($qty) && empty($special_code)) $this->line->special_code=3;
566 
567  // TODO deprecated
568  $this->line->price=$price;
569  $this->line->remise=$remise;
570 
571  if (is_array($array_options) && count($array_options)>0) {
572  $this->line->array_options=$array_options;
573  }
574 
575  $result=$this->line->insert();
576  if ($result > 0)
577  {
578  // Reorder if child line
579  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
580 
581  // Mise a jour informations denormalisees au niveau de la propale meme
582  $result=$this->update_price(1,'auto',0,$mysoc); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
583  if ($result > 0)
584  {
585  $this->db->commit();
586  return $this->line->rowid;
587  }
588  else
589  {
590  $this->error=$this->db->error();
591  $this->db->rollback();
592  return -1;
593  }
594  }
595  else
596  {
597  $this->error=$this->line->error;
598  $this->db->rollback();
599  return -2;
600  }
601  }
602  else
603  {
604  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
605  return -3;
606  }
607  }
608 
609 
638  function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $date_start='', $date_end='', $array_options=0, $fk_unit=null, $pu_ht_devise = 0, $notrigger=0)
639  {
640  global $mysoc;
641 
642  dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
643  txtva=$txtva, desc=$desc, price_base_type=$price_base_type, info_bits=$info_bits, special_code=$special_code, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, type=$type, date_start=$date_start, date_end=$date_end");
644  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
645 
646  // Clean parameters
647  $remise_percent=price2num($remise_percent);
648  $qty=price2num($qty);
649  $pu = price2num($pu);
650  $txtva = price2num($txtva);
651  $txlocaltax1=price2num($txlocaltax1);
652  $txlocaltax2=price2num($txlocaltax2);
653  $pa_ht=price2num($pa_ht);
654  if (empty($qty) && empty($special_code)) $special_code=3; // Set option tag
655  if (! empty($qty) && $special_code == 3) $special_code=0; // Remove option tag
656  if (empty($type)) $type=0;
657 
658  if ($this->statut == self::STATUS_DRAFT)
659  {
660  $this->db->begin();
661 
662  // Calcul du total TTC et de la TVA pour la ligne a partir de
663  // qty, pu, remise_percent et txtva
664  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
665  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
666 
667  $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
668 
669  // Clean vat code
670  $vat_src_code='';
671  if (preg_match('/\((.*)\)/', $txtva, $reg))
672  {
673  $vat_src_code = $reg[1];
674  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
675  }
676 
677  $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
678  $total_ht = $tabprice[0];
679  $total_tva = $tabprice[1];
680  $total_ttc = $tabprice[2];
681  $total_localtax1 = $tabprice[9];
682  $total_localtax2 = $tabprice[10];
683  $pu_ht = $tabprice[3];
684  $pu_tva = $tabprice[4];
685  $pu_ttc = $tabprice[5];
686 
687  // MultiCurrency
688  $multicurrency_total_ht = $tabprice[16];
689  $multicurrency_total_tva = $tabprice[17];
690  $multicurrency_total_ttc = $tabprice[18];
691  $pu_ht_devise = $tabprice[19];
692 
693  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
694  $price = $pu;
695  $remise = 0;
696  if ($remise_percent > 0)
697  {
698  $remise = round(($pu * $remise_percent / 100), 2);
699  $price = $pu - $remise;
700  }
701 
702  //Fetch current line from the database and then clone the object and set it in $oldline property
703  $line = new PropaleLigne($this->db);
704  $line->fetch($rowid);
705  $line->fetch_optionals(); // Fetch extrafields for oldcopy
706 
707  $staticline = clone $line;
708 
709  $line->oldline = $staticline;
710  $this->line = $line;
711  $this->line->context = $this->context;
712 
713  // Reorder if fk_parent_line change
714  if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
715  {
716  $rangmax = $this->line_max($fk_parent_line);
717  $this->line->rang = $rangmax + 1;
718  }
719 
720  $this->line->rowid = $rowid;
721  $this->line->label = $label;
722  $this->line->desc = $desc;
723  $this->line->qty = $qty;
724  $this->line->product_type = $type;
725  $this->line->vat_src_code = $vat_src_code;
726  $this->line->tva_tx = $txtva;
727  $this->line->localtax1_tx = $txlocaltax1;
728  $this->line->localtax2_tx = $txlocaltax2;
729  $this->line->localtax1_type = $localtaxes_type[0];
730  $this->line->localtax2_type = $localtaxes_type[2];
731  $this->line->remise_percent = $remise_percent;
732  $this->line->subprice = $pu_ht;
733  $this->line->info_bits = $info_bits;
734 
735  $this->line->total_ht = $total_ht;
736  $this->line->total_tva = $total_tva;
737  $this->line->total_localtax1 = $total_localtax1;
738  $this->line->total_localtax2 = $total_localtax2;
739  $this->line->total_ttc = $total_ttc;
740  $this->line->special_code = $special_code;
741  $this->line->fk_parent_line = $fk_parent_line;
742  $this->line->skip_update_total = $skip_update_total;
743  $this->line->fk_unit = $fk_unit;
744 
745  $this->line->fk_fournprice = $fk_fournprice;
746  $this->line->pa_ht = $pa_ht;
747 
748  $this->line->date_start=$date_start;
749  $this->line->date_end=$date_end;
750 
751  // TODO deprecated
752  $this->line->price=$price;
753  $this->line->remise=$remise;
754 
755  if (is_array($array_options) && count($array_options)>0) {
756  $this->line->array_options=$array_options;
757  }
758 
759  // Multicurrency
760  $this->line->multicurrency_subprice = $pu_ht_devise;
761  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
762  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
763  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
764 
765  $result=$this->line->update($notrigger);
766  if ($result > 0)
767  {
768  // Reorder if child line
769  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
770 
771  $this->update_price(1);
772 
773  $this->fk_propal = $this->id;
774  $this->rowid = $rowid;
775 
776  $this->db->commit();
777  return $result;
778  }
779  else
780  {
781  $this->error=$this->line->error;
782 
783  $this->db->rollback();
784  return -1;
785  }
786  }
787  else
788  {
789  dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
790  return -2;
791  }
792  }
793 
794 
801  function deleteline($lineid)
802  {
803  global $user;
804 
805  if ($this->statut == self::STATUS_DRAFT)
806  {
807  $this->db->begin();
808 
809  $line=new PropaleLigne($this->db);
810 
811  // For triggers
812  $line->fetch($lineid);
813 
814  if ($line->delete($user) > 0)
815  {
816  $this->update_price(1);
817 
818  $this->db->commit();
819  return 1;
820  }
821  else
822  {
823  $this->db->rollback();
824  return -1;
825  }
826  }
827  else
828  {
829  $this->error='ErrorDeleteLineNotAllowedByObjectStatus';
830  return -2;
831  }
832  }
833 
834 
843  function create($user, $notrigger=0)
844  {
845  global $conf,$hookmanager;
846  $error=0;
847 
848  $now=dol_now();
849 
850  // Clean parameters
851  if (empty($this->date)) $this->date=$this->datep;
852  $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
853  if (empty($this->availability_id)) $this->availability_id=0;
854  if (empty($this->demand_reason_id)) $this->demand_reason_id=0;
855 
856  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
857  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
858  else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
859  if (empty($this->fk_multicurrency))
860  {
861  $this->multicurrency_code = $conf->currency;
862  $this->fk_multicurrency = 0;
863  $this->multicurrency_tx = 1;
864  }
865 
866  dol_syslog(get_class($this)."::create");
867 
868  // Check parameters
869  $result=$this->fetch_thirdparty();
870  if ($result < 0)
871  {
872  $this->error="Failed to fetch company";
873  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
874  return -3;
875  }
876 
877  // Check parameters
878  if (! empty($this->ref)) // We check that ref is not already used
879  {
880  $result=self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
881  if ($result > 0)
882  {
883  $this->error='ErrorRefAlreadyExists';
884  dol_syslog(get_class($this)."::create ".$this->error,LOG_WARNING);
885  $this->db->rollback();
886  return -1;
887  }
888  }
889 
890  if (empty($this->date))
891  {
892  $this->error="Date of proposal is required";
893  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
894  return -4;
895  }
896 
897 
898  $this->db->begin();
899 
900  // Insert into database
901  $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
902  $sql.= "fk_soc";
903  $sql.= ", price";
904  $sql.= ", remise";
905  $sql.= ", remise_percent";
906  $sql.= ", remise_absolue";
907  $sql.= ", tva";
908  $sql.= ", total";
909  $sql.= ", datep";
910  $sql.= ", datec";
911  $sql.= ", ref";
912  $sql.= ", fk_user_author";
913  $sql.= ", note_private";
914  $sql.= ", note_public";
915  $sql.= ", model_pdf";
916  $sql.= ", fin_validite";
917  $sql.= ", fk_cond_reglement";
918  $sql.= ", fk_mode_reglement";
919  $sql.= ", fk_account";
920  $sql.= ", ref_client";
921  $sql.= ", date_livraison";
922  $sql.= ", fk_shipping_method";
923  $sql.= ", fk_availability";
924  $sql.= ", fk_input_reason";
925  $sql.= ", fk_projet";
926  $sql.= ", fk_incoterms";
927  $sql.= ", location_incoterms";
928  $sql.= ", entity";
929  $sql.= ", fk_multicurrency";
930  $sql.= ", multicurrency_code";
931  $sql.= ", multicurrency_tx";
932  $sql.= ") ";
933  $sql.= " VALUES (";
934  $sql.= $this->socid;
935  $sql.= ", 0";
936  $sql.= ", ".$this->remise;
937  $sql.= ", ".($this->remise_percent?$this->db->escape($this->remise_percent):'NULL');
938  $sql.= ", ".($this->remise_absolue?$this->db->escape($this->remise_absolue):'NULL');
939  $sql.= ", 0";
940  $sql.= ", 0";
941  $sql.= ", '".$this->db->idate($this->date)."'";
942  $sql.= ", '".$this->db->idate($now)."'";
943  $sql.= ", '(PROV)'";
944  $sql.= ", ".($user->id > 0 ? "'".$user->id."'":"NULL");
945  $sql.= ", '".$this->db->escape($this->note_private)."'";
946  $sql.= ", '".$this->db->escape($this->note_public)."'";
947  $sql.= ", '".$this->db->escape($this->modelpdf)."'";
948  $sql.= ", ".($this->fin_validite!=''?"'".$this->db->idate($this->fin_validite)."'":"NULL");
949  $sql.= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'NULL');
950  $sql.= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'NULL');
951  $sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
952  $sql.= ", '".$this->db->escape($this->ref_client)."'";
953  $sql.= ", ".($this->date_livraison!=''?"'".$this->db->idate($this->date_livraison)."'":"NULL");
954  $sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
955  $sql.= ", ".$this->availability_id;
956  $sql.= ", ".$this->demand_reason_id;
957  $sql.= ", ".($this->fk_project?$this->fk_project:"null");
958  $sql.= ", ".(int) $this->fk_incoterms;
959  $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
960  $sql.= ", ".$conf->entity;
961  $sql.= ", ".(int) $this->fk_multicurrency;
962  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
963  $sql.= ", ".(double) $this->multicurrency_tx;
964  $sql.= ")";
965 
966  dol_syslog(get_class($this)."::create", LOG_DEBUG);
967  $resql=$this->db->query($sql);
968  if ($resql)
969  {
970  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
971 
972  if ($this->id)
973  {
974  $this->ref='(PROV'.$this->id.')';
975  $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
976 
977  dol_syslog(get_class($this)."::create", LOG_DEBUG);
978  $resql=$this->db->query($sql);
979  if (! $resql) $error++;
980 
981  if (! empty($this->linkedObjectsIds) && empty($this->linked_objects)) // To use new linkedObjectsIds instead of old linked_objects
982  {
983  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
984  }
985 
986  // Add object linked
987  if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
988  {
989  foreach($this->linked_objects as $origin => $tmp_origin_id)
990  {
991  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, ...))
992  {
993  foreach($tmp_origin_id as $origin_id)
994  {
995  $ret = $this->add_object_linked($origin, $origin_id);
996  if (! $ret)
997  {
998  $this->error=$this->db->lasterror();
999  $error++;
1000  }
1001  }
1002  }
1003  else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1004  {
1005  $origin_id = $tmp_origin_id;
1006  $ret = $this->add_object_linked($origin, $origin_id);
1007  if (! $ret)
1008  {
1009  $this->error=$this->db->lasterror();
1010  $error++;
1011  }
1012  }
1013  }
1014  }
1015 
1016  // Add linked object (deprecated, use ->linkedObjectsIds instead)
1017  if (! $error && $this->origin && $this->origin_id)
1018  {
1019  $ret = $this->add_object_linked();
1020  if (! $ret) dol_print_error($this->db);
1021  }
1022 
1023  /*
1024  * Insertion du detail des produits dans la base
1025  * Insert products detail in database
1026  */
1027  if (! $error)
1028  {
1029  $fk_parent_line=0;
1030  $num=count($this->lines);
1031 
1032  for ($i=0;$i<$num;$i++)
1033  {
1034  if (! is_object($this->lines[$i])) // If this->lines is not array of objects, coming from REST API
1035  { // Convert into object this->lines[$i].
1036  $line = (object) $this->lines[$i];
1037  }
1038  else
1039  {
1040  $line = $this->lines[$i];
1041  }
1042  // Reset fk_parent_line for line that are not child lines or special product
1043  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1044  $fk_parent_line = 0;
1045  }
1046  // Complete vat rate with code
1047  $vatrate = $line->tva_tx;
1048  if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
1049 
1050  $result = $this->addline(
1051  $line->desc,
1052  $line->subprice,
1053  $line->qty,
1054  $vatrate,
1055  $line->localtax1_tx,
1056  $line->localtax2_tx,
1057  $line->fk_product,
1058  $line->remise_percent,
1059  'HT',
1060  0,
1061  $line->info_bits,
1062  $line->product_type,
1063  $line->rang,
1064  $line->special_code,
1065  $fk_parent_line,
1066  $line->fk_fournprice,
1067  $line->pa_ht,
1068  $line->label,
1069  $line->date_start,
1070  $line->date_end,
1071  $line->array_options,
1072  $line->fk_unit,
1073  $this->element,
1074  $line->id
1075  );
1076 
1077  if ($result < 0)
1078  {
1079  $error++;
1080  $this->error=$this->db->error;
1081  dol_print_error($this->db);
1082  break;
1083  }
1084  // Defined the new fk_parent_line
1085  if ($result > 0 && $line->product_type == 9) {
1086  $fk_parent_line = $result;
1087  }
1088  }
1089  }
1090 
1091  // Add linked object
1092  if (! $error && $this->origin && $this->origin_id)
1093  {
1094  $ret = $this->add_object_linked();
1095  if (! $ret) dol_print_error($this->db);
1096  }
1097 
1098  // Set delivery address
1099  if (! $error && $this->fk_delivery_address)
1100  {
1101  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1102  $sql.= " SET fk_delivery_address = ".$this->fk_delivery_address;
1103  $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1104  $sql.= " AND entity = ".$conf->entity;
1105 
1106  $result=$this->db->query($sql);
1107  }
1108 
1109  if (! $error)
1110  {
1111  // Mise a jour infos denormalisees
1112  $resql=$this->update_price(1);
1113  if ($resql)
1114  {
1115  $action='update';
1116 
1117  // Actions on extra fields (by external module or standard code)
1118  // TODO le hook fait double emploi avec le trigger !!
1119  $hookmanager->initHooks(array('propaldao'));
1120  $parameters=array('socid'=>$this->id);
1121  $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1122  if (empty($reshook))
1123  {
1124  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1125  {
1126  $result=$this->insertExtraFields();
1127  if ($result < 0)
1128  {
1129  $error++;
1130  }
1131  }
1132  }
1133  else if ($reshook < 0) $error++;
1134 
1135  if (! $notrigger)
1136  {
1137  // Call trigger
1138  $result=$this->call_trigger('PROPAL_CREATE',$user);
1139  if ($result < 0) { $error++; }
1140  // End call triggers
1141  }
1142  }
1143  else
1144  {
1145  $this->error=$this->db->lasterror();
1146  $error++;
1147  }
1148  }
1149  }
1150  else
1151  {
1152  $this->error=$this->db->lasterror();
1153  $error++;
1154  }
1155 
1156  if (! $error)
1157  {
1158  $this->db->commit();
1159  dol_syslog(get_class($this)."::create done id=".$this->id);
1160  return $this->id;
1161  }
1162  else
1163  {
1164  $this->db->rollback();
1165  return -2;
1166  }
1167  }
1168  else
1169  {
1170  $this->error=$this->db->lasterror();
1171  $this->db->rollback();
1172  return -1;
1173  }
1174  }
1175 
1176 
1184  function create_from($user)
1185  {
1186  // i love this function because $this->products is not used in create function...
1187  $this->products=$this->lines;
1188 
1189  return $this->create($user);
1190  }
1191 
1198  function createFromClone($socid=0)
1199  {
1200  global $user,$conf,$hookmanager;
1201 
1202  dol_include_once('/projet/class/project.class.php');
1203 
1204  $this->context['createfromclone']='createfromclone';
1205 
1206  $error=0;
1207  $now=dol_now();
1208 
1209  $this->db->begin();
1210 
1211  // get extrafields so they will be clone
1212  foreach($this->lines as $line)
1213  $line->fetch_optionals($line->rowid);
1214 
1215  // Load dest object
1216  $clonedObj = clone $this;
1217 
1218  $objsoc=new Societe($this->db);
1219 
1220  // Change socid if needed
1221  if (! empty($socid) && $socid != $clonedObj->socid)
1222  {
1223  if ($objsoc->fetch($socid) > 0)
1224  {
1225  $clonedObj->socid = $objsoc->id;
1226  $clonedObj->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1227  $clonedObj->mode_reglement_id = (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1228  $clonedObj->fk_delivery_address = '';
1229 
1230  /*if (!empty($conf->projet->enabled))
1231  {
1232  $project = new Project($db);
1233  if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1234  if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1235  else $clonedObj->fk_project = '';
1236  } else {
1237  $clonedObj->fk_project = '';
1238  }
1239  }*/
1240  $clonedObj->fk_project = ''; // A cloned proposal is set by default to no project.
1241  }
1242 
1243  // reset ref_client
1244  $clonedObj->ref_client = '';
1245 
1246  // TODO Change product price if multi-prices
1247  }
1248  else
1249  {
1250  $objsoc->fetch($clonedObj->socid);
1251  }
1252 
1253  $clonedObj->id=0;
1254  $clonedObj->ref='';
1255  $clonedObj->statut=self::STATUS_DRAFT;
1256 
1257  // Clear fields
1258  $clonedObj->user_author = $user->id;
1259  $clonedObj->user_valid = '';
1260  $clonedObj->date = $now;
1261  $clonedObj->datep = $now; // deprecated
1262  $clonedObj->fin_validite = $clonedObj->date + ($clonedObj->duree_validite * 24 * 3600);
1263  if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $clonedObj->ref_client = '';
1264 
1265  // Create clone
1266  $result=$clonedObj->create($user);
1267  if ($result < 0) $error++;
1268  else
1269  {
1270  // copy internal contacts
1271  if ($clonedObj->copy_linked_contact($this, 'internal') < 0)
1272  $error++;
1273 
1274  // copy external contacts if same company
1275  elseif ($this->socid == $clonedObj->socid)
1276  {
1277  if ($clonedObj->copy_linked_contact($this, 'external') < 0)
1278  $error++;
1279  }
1280  }
1281 
1282  if (! $error)
1283  {
1284  // Hook of thirdparty module
1285  if (is_object($hookmanager))
1286  {
1287  $parameters=array('objFrom'=>$this,'clonedObj'=>$clonedObj);
1288  $action='';
1289  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$clonedObj,$action); // Note that $action and $object may have been modified by some hooks
1290  if ($reshook < 0) $error++;
1291  }
1292 
1293  // Call trigger
1294  $result=$clonedObj->call_trigger('PROPAL_CLONE',$user);
1295  if ($result < 0) { $error++; }
1296  // End call triggers
1297  }
1298 
1299  unset($this->context['createfromclone']);
1300 
1301  // End
1302  if (! $error)
1303  {
1304  $this->db->commit();
1305  return $clonedObj->id;
1306  }
1307  else
1308  {
1309  $this->db->rollback();
1310  return -1;
1311  }
1312  }
1313 
1321  function fetch($rowid,$ref='')
1322  {
1323 
1324  $sql = "SELECT p.rowid, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1325  $sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht";
1326  $sql.= ", p.datec";
1327  $sql.= ", p.date_valid as datev";
1328  $sql.= ", p.datep as dp";
1329  $sql.= ", p.fin_validite as dfv";
1330  $sql.= ", p.date_livraison as date_livraison";
1331  $sql.= ", p.model_pdf, p.last_main_doc, p.ref_client, p.extraparams";
1332  $sql.= ", p.note_private, p.note_public";
1333  $sql.= ", p.fk_projet, p.fk_statut";
1334  $sql.= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1335  $sql.= ", p.fk_delivery_address";
1336  $sql.= ", p.fk_availability";
1337  $sql.= ", p.fk_input_reason";
1338  $sql.= ", p.fk_cond_reglement";
1339  $sql.= ", p.fk_mode_reglement";
1340  $sql.= ', p.fk_account';
1341  $sql.= ", p.fk_shipping_method";
1342  $sql.= ", p.fk_incoterms, p.location_incoterms";
1343  $sql.= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1344  $sql.= ", i.libelle as libelle_incoterms";
1345  $sql.= ", c.label as statut_label";
1346  $sql.= ", ca.code as availability_code, ca.label as availability";
1347  $sql.= ", dr.code as demand_reason_code, dr.label as demand_reason";
1348  $sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1349  $sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1350  $sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."propal as p";
1351  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1352  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')';
1353  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1354  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1355  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1356  $sql.= " WHERE p.fk_statut = c.id";
1357  $sql.= " AND p.entity IN (".getEntity('propal').")";
1358  if ($ref) $sql.= " AND p.ref='".$ref."'";
1359  else $sql.= " AND p.rowid=".$rowid;
1360 
1361  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1362  $resql=$this->db->query($sql);
1363  if ($resql)
1364  {
1365  if ($this->db->num_rows($resql))
1366  {
1367  $obj = $this->db->fetch_object($resql);
1368 
1369  $this->id = $obj->rowid;
1370 
1371  $this->ref = $obj->ref;
1372  $this->ref_client = $obj->ref_client;
1373  $this->remise = $obj->remise;
1374  $this->remise_percent = $obj->remise_percent;
1375  $this->remise_absolue = $obj->remise_absolue;
1376  $this->total = $obj->total; // TODO deprecated
1377  $this->total_ht = $obj->total_ht;
1378  $this->total_tva = $obj->tva;
1379  $this->total_localtax1 = $obj->localtax1;
1380  $this->total_localtax2 = $obj->localtax2;
1381  $this->total_ttc = $obj->total;
1382  $this->socid = $obj->fk_soc;
1383  $this->fk_project = $obj->fk_projet;
1384  $this->modelpdf = $obj->model_pdf;
1385  $this->last_main_doc = $obj->last_main_doc;
1386  $this->note = $obj->note_private; // TODO deprecated
1387  $this->note_private = $obj->note_private;
1388  $this->note_public = $obj->note_public;
1389  $this->statut = (int) $obj->fk_statut;
1390  $this->statut_libelle = $obj->statut_label;
1391 
1392  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1393  $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1394  $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1395  $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1396  $this->date = $this->db->jdate($obj->dp); // Proposal date
1397  $this->datep = $this->db->jdate($obj->dp); // deprecated
1398  $this->fin_validite = $this->db->jdate($obj->dfv);
1399  $this->date_livraison = $this->db->jdate($obj->date_livraison);
1400  $this->shipping_method_id = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1401  $this->availability_id = $obj->fk_availability;
1402  $this->availability_code = $obj->availability_code;
1403  $this->availability = $obj->availability;
1404  $this->demand_reason_id = $obj->fk_input_reason;
1405  $this->demand_reason_code = $obj->demand_reason_code;
1406  $this->demand_reason = $obj->demand_reason;
1407  $this->fk_address = $obj->fk_delivery_address;
1408 
1409  $this->mode_reglement_id = $obj->fk_mode_reglement;
1410  $this->mode_reglement_code = $obj->mode_reglement_code;
1411  $this->mode_reglement = $obj->mode_reglement;
1412  $this->fk_account = ($obj->fk_account>0)?$obj->fk_account:null;
1413  $this->cond_reglement_id = $obj->fk_cond_reglement;
1414  $this->cond_reglement_code = $obj->cond_reglement_code;
1415  $this->cond_reglement = $obj->cond_reglement;
1416  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1417 
1418  $this->extraparams = (array) json_decode($obj->extraparams, true);
1419 
1420  $this->user_author_id = $obj->fk_user_author;
1421  $this->user_valid_id = $obj->fk_user_valid;
1422  $this->user_close_id = $obj->fk_user_cloture;
1423 
1424  //Incoterms
1425  $this->fk_incoterms = $obj->fk_incoterms;
1426  $this->location_incoterms = $obj->location_incoterms;
1427  $this->libelle_incoterms = $obj->libelle_incoterms;
1428 
1429  // Multicurrency
1430  $this->fk_multicurrency = $obj->fk_multicurrency;
1431  $this->multicurrency_code = $obj->multicurrency_code;
1432  $this->multicurrency_tx = $obj->multicurrency_tx;
1433  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1434  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1435  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1436 
1437  if ($obj->fk_statut == self::STATUS_DRAFT)
1438  {
1439  $this->brouillon = 1;
1440  }
1441 
1442  // Retreive all extrafield for invoice
1443  // fetch optionals attributes and labels
1444  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1445  $extrafields=new ExtraFields($this->db);
1446  $extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
1447  $this->fetch_optionals($this->id,$extralabels);
1448 
1449  $this->db->free($resql);
1450 
1451  $this->lines = array();
1452 
1453  /*
1454  * Lines
1455  */
1456  $result=$this->fetch_lines();
1457  if ($result < 0)
1458  {
1459  return -3;
1460  }
1461 
1462  return 1;
1463  }
1464 
1465  $this->error="Record Not Found";
1466  return 0;
1467  }
1468  else
1469  {
1470  $this->error=$this->db->lasterror();
1471  return -1;
1472  }
1473  }
1474 
1482  function update(User $user, $notrigger=0)
1483  {
1484  $error=0;
1485 
1486  // Clean parameters
1487  if (isset($this->ref)) $this->ref=trim($this->ref);
1488  if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1489  if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1490  if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1491  if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1492  if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1493 
1494  // Check parameters
1495  // Put here code to add control on parameters values
1496 
1497  // Update request
1498  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1499 
1500  $sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1501  $sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1502  $sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1503  $sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1504  $sql.= " datep=".(strval($this->datep)!='' ? "'".$this->db->idate($this->datep)."'" : 'null').",";
1505  $sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1506  $sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1507  $sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1508  $sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1509  $sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
1510  $sql.= " total=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1511  $sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1512  $sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
1513  $sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
1514  $sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1515  $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
1516  $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
1517  $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1518  $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1519  $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1520  $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1521 
1522  $sql.= " WHERE rowid=".$this->id;
1523 
1524  $this->db->begin();
1525 
1526  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1527  $resql = $this->db->query($sql);
1528  if (! $resql) {
1529  $error++; $this->errors[]="Error ".$this->db->lasterror();
1530  }
1531 
1532  if (! $error)
1533  {
1534  if (! $notrigger)
1535  {
1536  // Call trigger
1537  $result=$this->call_trigger('PROPAL_MODIFY', $user);
1538  if ($result < 0) $error++;
1539  // End call triggers
1540  }
1541  }
1542 
1543  // Commit or rollback
1544  if ($error)
1545  {
1546  foreach($this->errors as $errmsg)
1547  {
1548  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1549  $this->error.=($this->error?', '.$errmsg:$errmsg);
1550  }
1551  $this->db->rollback();
1552  return -1*$error;
1553  }
1554  else
1555  {
1556  $this->db->commit();
1557  return 1;
1558  }
1559  }
1560 
1561 
1568  function fetch_lines($only_product=0)
1569  {
1570  $this->lines=array();
1571 
1572  $sql = 'SELECT d.rowid, d.fk_propal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,';
1573  $sql.= ' d.info_bits, d.total_ht, d.total_tva, d.total_localtax1, d.total_localtax2, d.total_ttc, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht, d.special_code, d.rang, d.product_type,';
1574  $sql.= ' d.fk_unit,';
1575  $sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1576  $sql.= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1577  $sql.= ' d.date_start, d.date_end';
1578  $sql.= ' ,d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1579  $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1580  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1581  $sql.= ' WHERE d.fk_propal = '.$this->id;
1582  if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1583  $sql.= ' ORDER by d.rang';
1584 
1585  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1586  $result = $this->db->query($sql);
1587  if ($result)
1588  {
1589  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1590 
1591  $num = $this->db->num_rows($result);
1592 
1593  $i = 0;
1594  while ($i < $num)
1595  {
1596  $objp = $this->db->fetch_object($result);
1597 
1598  $line = new PropaleLigne($this->db);
1599 
1600  $line->rowid = $objp->rowid; //Deprecated
1601  $line->id = $objp->rowid;
1602  $line->fk_propal = $objp->fk_propal;
1603  $line->fk_parent_line = $objp->fk_parent_line;
1604  $line->product_type = $objp->product_type;
1605  $line->label = $objp->custom_label;
1606  $line->desc = $objp->description; // Description ligne
1607  $line->qty = $objp->qty;
1608  $line->vat_src_code = $objp->vat_src_code;
1609  $line->tva_tx = $objp->tva_tx;
1610  $line->localtax1_tx = $objp->localtax1_tx;
1611  $line->localtax2_tx = $objp->localtax2_tx;
1612  $line->localtax1_type = $objp->localtax1_type;
1613  $line->localtax2_type = $objp->localtax2_type;
1614  $line->subprice = $objp->subprice;
1615  $line->fk_remise_except = $objp->fk_remise_except;
1616  $line->remise_percent = $objp->remise_percent;
1617  $line->price = $objp->price; // TODO deprecated
1618 
1619  $line->info_bits = $objp->info_bits;
1620  $line->total_ht = $objp->total_ht;
1621  $line->total_tva = $objp->total_tva;
1622  $line->total_localtax1 = $objp->total_localtax1;
1623  $line->total_localtax2 = $objp->total_localtax2;
1624  $line->total_ttc = $objp->total_ttc;
1625  $line->fk_fournprice = $objp->fk_fournprice;
1626  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1627  $line->pa_ht = $marginInfos[0];
1628  $line->marge_tx = $marginInfos[1];
1629  $line->marque_tx = $marginInfos[2];
1630  $line->special_code = $objp->special_code;
1631  $line->rang = $objp->rang;
1632 
1633  $line->fk_product = $objp->fk_product;
1634 
1635  $line->ref = $objp->product_ref; // TODO deprecated
1636  $line->product_ref = $objp->product_ref;
1637  $line->libelle = $objp->product_label; // TODO deprecated
1638  $line->product_label = $objp->product_label;
1639  $line->product_desc = $objp->product_desc; // Description produit
1640  $line->fk_product_type = $objp->fk_product_type;
1641  $line->fk_unit = $objp->fk_unit;
1642  $line->weight = $objp->weight;
1643  $line->weight_units = $objp->weight_units;
1644  $line->volume = $objp->volume;
1645  $line->volume_units = $objp->volume_units;
1646 
1647  $line->date_start = $this->db->jdate($objp->date_start);
1648  $line->date_end = $this->db->jdate($objp->date_end);
1649 
1650  // Multicurrency
1651  $line->fk_multicurrency = $objp->fk_multicurrency;
1652  $line->multicurrency_code = $objp->multicurrency_code;
1653  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1654  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1655  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1656  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1657 
1658  $line->fetch_optionals();
1659 
1660  $this->lines[$i] = $line;
1661  //dol_syslog("1 ".$line->fk_product);
1662  //print "xx $i ".$this->lines[$i]->fk_product;
1663  $i++;
1664  }
1665 
1666  $this->db->free($result);
1667 
1668  return 1;
1669  }
1670  else
1671  {
1672  $this->error=$this->db->lasterror();
1673  return -3;
1674  }
1675  }
1676 
1684  function valid($user, $notrigger=0)
1685  {
1686  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1687 
1688  global $conf;
1689 
1690  $error=0;
1691 
1692  // Protection
1693  if ($this->statut == self::STATUS_VALIDATED)
1694  {
1695  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1696  return 0;
1697  }
1698 
1699  if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->creer))
1700  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->propal_advance->validate))))
1701  {
1702  $this->error='ErrorPermissionDenied';
1703  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1704  return -1;
1705  }
1706 
1707  $now=dol_now();
1708 
1709  $this->db->begin();
1710 
1711  // Numbering module definition
1712  $soc = new Societe($this->db);
1713  $soc->fetch($this->socid);
1714 
1715  // Define new ref
1716  if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
1717  {
1718  $num = $this->getNextNumRef($soc);
1719  }
1720  else
1721  {
1722  $num = $this->ref;
1723  }
1724  $this->newref = $num;
1725 
1726  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1727  $sql.= " SET ref = '".$num."',";
1728  $sql.= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".$user->id;
1729  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1730 
1731  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1732  $resql=$this->db->query($sql);
1733  if (! $resql)
1734  {
1735  dol_print_error($this->db);
1736  $error++;
1737  }
1738 
1739  // Trigger calls
1740  if (! $error && ! $notrigger)
1741  {
1742  // Call trigger
1743  $result=$this->call_trigger('PROPAL_VALIDATE',$user);
1744  if ($result < 0) { $error++; }
1745  // End call triggers
1746  }
1747 
1748  if (! $error)
1749  {
1750  $this->oldref = $this->ref;
1751 
1752  // Rename directory if dir was a temporary ref
1753  if (preg_match('/^[\(]?PROV/i', $this->ref))
1754  {
1755  // Rename of propal directory ($this->ref = old ref, $num = new ref)
1756  // to not lose the linked files
1757  $oldref = dol_sanitizeFileName($this->ref);
1758  $newref = dol_sanitizeFileName($num);
1759  $dirsource = $conf->propal->dir_output.'/'.$oldref;
1760  $dirdest = $conf->propal->dir_output.'/'.$newref;
1761 
1762  if (file_exists($dirsource))
1763  {
1764  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1765  if (@rename($dirsource, $dirdest))
1766  {
1767  dol_syslog("Rename ok");
1768  // Rename docs starting with $oldref with $newref
1769  $listoffiles=dol_dir_list($conf->propal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
1770  foreach($listoffiles as $fileentry)
1771  {
1772  $dirsource=$fileentry['name'];
1773  $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
1774  $dirsource=$fileentry['path'].'/'.$dirsource;
1775  $dirdest=$fileentry['path'].'/'.$dirdest;
1776  @rename($dirsource, $dirdest);
1777  }
1778  }
1779  }
1780  }
1781 
1782  $this->ref=$num;
1783  $this->brouillon=0;
1784  $this->statut = self::STATUS_VALIDATED;
1785  $this->user_valid_id=$user->id;
1786  $this->datev=$now;
1787 
1788  $this->db->commit();
1789  return 1;
1790  }
1791  else
1792  {
1793  $this->db->rollback();
1794  return -1;
1795  }
1796  }
1797 
1798 
1807  function set_date($user, $date, $notrigger=0)
1808  {
1809  if (empty($date))
1810  {
1811  $this->error='ErrorBadParameter';
1812  dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
1813  return -1;
1814  }
1815 
1816  if (! empty($user->rights->propal->creer))
1817  {
1818  $error=0;
1819 
1820  $this->db->begin();
1821 
1822  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
1823  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1824 
1825  dol_syslog(__METHOD__, LOG_DEBUG);
1826  $resql=$this->db->query($sql);
1827  if (!$resql)
1828  {
1829  $this->errors[]=$this->db->error();
1830  $error++;
1831  }
1832 
1833  if (! $error)
1834  {
1835  $this->oldcopy= clone $this;
1836  $this->date = $date;
1837  $this->datep = $date; // deprecated
1838  }
1839 
1840  if (! $notrigger && empty($error))
1841  {
1842  // Call trigger
1843  $result=$this->call_trigger('PROPAL_MODIFY',$user);
1844  if ($result < 0) $error++;
1845  // End call triggers
1846  }
1847 
1848  if (! $error)
1849  {
1850  $this->db->commit();
1851  return 1;
1852  }
1853  else
1854  {
1855  foreach($this->errors as $errmsg)
1856  {
1857  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1858  $this->error.=($this->error?', '.$errmsg:$errmsg);
1859  }
1860  $this->db->rollback();
1861  return -1*$error;
1862  }
1863  }
1864  }
1865 
1874  function set_echeance($user, $date_fin_validite, $notrigger=0)
1875  {
1876  if (! empty($user->rights->propal->creer))
1877  {
1878  $error=0;
1879 
1880  $this->db->begin();
1881 
1882  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite!=''?"'".$this->db->idate($date_fin_validite)."'":'null');
1883  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1884 
1885  dol_syslog(__METHOD__, LOG_DEBUG);
1886  $resql=$this->db->query($sql);
1887  if (!$resql)
1888  {
1889  $this->errors[]=$this->db->error();
1890  $error++;
1891  }
1892 
1893 
1894  if (! $error)
1895  {
1896  $this->oldcopy= clone $this;
1897  $this->fin_validite = $date_fin_validite;
1898  }
1899 
1900  if (! $notrigger && empty($error))
1901  {
1902  // Call trigger
1903  $result=$this->call_trigger('PROPAL_MODIFY',$user);
1904  if ($result < 0) $error++;
1905  // End call triggers
1906  }
1907 
1908  if (! $error)
1909  {
1910  $this->db->commit();
1911  return 1;
1912  }
1913  else
1914  {
1915  foreach($this->errors as $errmsg)
1916  {
1917  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1918  $this->error.=($this->error?', '.$errmsg:$errmsg);
1919  }
1920  $this->db->rollback();
1921  return -1*$error;
1922  }
1923  }
1924  }
1925 
1934  function set_date_livraison($user, $date_livraison, $notrigger=0)
1935  {
1936  if (! empty($user->rights->propal->creer))
1937  {
1938  $error=0;
1939 
1940  $this->db->begin();
1941 
1942  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
1943  $sql.= " SET date_livraison = ".($date_livraison!=''?"'".$this->db->idate($date_livraison)."'":'null');
1944  $sql.= " WHERE rowid = ".$this->id;
1945 
1946  dol_syslog(__METHOD__, LOG_DEBUG);
1947  $resql=$this->db->query($sql);
1948  if (!$resql)
1949  {
1950  $this->errors[]=$this->db->error();
1951  $error++;
1952  }
1953 
1954  if (! $error)
1955  {
1956  $this->oldcopy= clone $this;
1957  $this->date_livraison = $date_livraison;
1958  }
1959 
1960  if (! $notrigger && empty($error))
1961  {
1962  // Call trigger
1963  $result=$this->call_trigger('PROPAL_MODIFY',$user);
1964  if ($result < 0) $error++;
1965  // End call triggers
1966  }
1967 
1968  if (! $error)
1969  {
1970  $this->db->commit();
1971  return 1;
1972  }
1973  else
1974  {
1975  foreach($this->errors as $errmsg)
1976  {
1977  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1978  $this->error.=($this->error?', '.$errmsg:$errmsg);
1979  }
1980  $this->db->rollback();
1981  return -1*$error;
1982  }
1983  }
1984  }
1985 
1994  function set_availability($user, $id, $notrigger=0)
1995  {
1996  if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
1997  {
1998  $error=0;
1999 
2000  $this->db->begin();
2001 
2002  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2003  $sql.= " SET fk_availability = '".$id."'";
2004  $sql.= " WHERE rowid = ".$this->id;
2005 
2006  dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2007  $resql=$this->db->query($sql);
2008  if (!$resql)
2009  {
2010  $this->errors[]=$this->db->error();
2011  $error++;
2012  }
2013 
2014  if (! $error)
2015  {
2016  $this->oldcopy= clone $this;
2017  $this->fk_availability = $id;
2018  $this->availability_id = $id;
2019  }
2020 
2021  if (! $notrigger && empty($error))
2022  {
2023  // Call trigger
2024  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2025  if ($result < 0) $error++;
2026  // End call triggers
2027  }
2028 
2029  if (! $error)
2030  {
2031  $this->db->commit();
2032  return 1;
2033  }
2034  else
2035  {
2036  foreach($this->errors as $errmsg)
2037  {
2038  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2039  $this->error.=($this->error?', '.$errmsg:$errmsg);
2040  }
2041  $this->db->rollback();
2042  return -1*$error;
2043  }
2044  }
2045  else
2046  {
2047  $error_str='Propal status do not meet requirement '.$this->statut;
2048  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2049  $this->error=$error_str;
2050  $this->errors[]= $this->error;
2051  return -2;
2052  }
2053  }
2054 
2063  function set_demand_reason($user, $id, $notrigger=0)
2064  {
2065  if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2066  {
2067  $error=0;
2068 
2069  $this->db->begin();
2070 
2071  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2072  $sql.= " SET fk_input_reason = ".$id;
2073  $sql.= " WHERE rowid = ".$this->id;
2074 
2075  dol_syslog(__METHOD__, LOG_DEBUG);
2076  $resql=$this->db->query($sql);
2077  if (!$resql)
2078  {
2079  $this->errors[]=$this->db->error();
2080  $error++;
2081  }
2082 
2083 
2084  if (! $error)
2085  {
2086  $this->oldcopy= clone $this;
2087  $this->fk_input_reason = $id;
2088  $this->demand_reason_id = $id;
2089  }
2090 
2091 
2092  if (! $notrigger && empty($error))
2093  {
2094  // Call trigger
2095  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2096  if ($result < 0) $error++;
2097  // End call triggers
2098  }
2099 
2100  if (! $error)
2101  {
2102  $this->db->commit();
2103  return 1;
2104  }
2105  else
2106  {
2107  foreach($this->errors as $errmsg)
2108  {
2109  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2110  $this->error.=($this->error?', '.$errmsg:$errmsg);
2111  }
2112  $this->db->rollback();
2113  return -1*$error;
2114  }
2115  }
2116  else
2117  {
2118  $error_str='Propal status do not meet requirement '.$this->statut;
2119  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2120  $this->error=$error_str;
2121  $this->errors[]= $this->error;
2122  return -2;
2123  }
2124  }
2125 
2134  function set_ref_client($user, $ref_client, $notrigger=0)
2135  {
2136  if (! empty($user->rights->propal->creer))
2137  {
2138  $error=0;
2139 
2140  $this->db->begin();
2141 
2142  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2143  $sql.= ' WHERE rowid = '.$this->id;
2144 
2145  dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2146  $resql=$this->db->query($sql);
2147  if (!$resql)
2148  {
2149  $this->errors[]=$this->db->error();
2150  $error++;
2151  }
2152 
2153  if (! $error)
2154  {
2155  $this->oldcopy= clone $this;
2156  $this->ref_client = $ref_client;
2157  }
2158 
2159  if (! $notrigger && empty($error))
2160  {
2161  // Call trigger
2162  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2163  if ($result < 0) $error++;
2164  // End call triggers
2165  }
2166 
2167  if (! $error)
2168  {
2169  $this->db->commit();
2170  return 1;
2171  }
2172  else
2173  {
2174  foreach($this->errors as $errmsg)
2175  {
2176  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2177  $this->error.=($this->error?', '.$errmsg:$errmsg);
2178  }
2179  $this->db->rollback();
2180  return -1*$error;
2181  }
2182  }
2183  else
2184  {
2185  return -1;
2186  }
2187  }
2188 
2197  function set_remise_percent($user, $remise, $notrigger=0)
2198  {
2199  $remise=trim($remise)?trim($remise):0;
2200 
2201  if (! empty($user->rights->propal->creer))
2202  {
2203  $remise = price2num($remise);
2204 
2205  $error=0;
2206 
2207  $this->db->begin();
2208 
2209  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".$remise;
2210  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2211 
2212  dol_syslog(__METHOD__, LOG_DEBUG);
2213  $resql=$this->db->query($sql);
2214  if (!$resql)
2215  {
2216  $this->errors[]=$this->db->error();
2217  $error++;
2218  }
2219 
2220  if (! $error)
2221  {
2222  $this->oldcopy= clone $this;
2223  $this->remise_percent = $remise;
2224  $this->update_price(1);
2225  }
2226 
2227  if (! $notrigger && empty($error))
2228  {
2229  // Call trigger
2230  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2231  if ($result < 0) $error++;
2232  // End call triggers
2233  }
2234 
2235  if (! $error)
2236  {
2237  $this->db->commit();
2238  return 1;
2239  }
2240  else
2241  {
2242  foreach($this->errors as $errmsg)
2243  {
2244  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2245  $this->error.=($this->error?', '.$errmsg:$errmsg);
2246  }
2247  $this->db->rollback();
2248  return -1*$error;
2249  }
2250  }
2251  }
2252 
2253 
2262  function set_remise_absolue($user, $remise, $notrigger=0)
2263  {
2264  $remise=trim($remise)?trim($remise):0;
2265 
2266  if (! empty($user->rights->propal->creer))
2267  {
2268  $remise = price2num($remise);
2269 
2270  $error=0;
2271 
2272  $this->db->begin();
2273 
2274  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2275  $sql.= " SET remise_absolue = ".$remise;
2276  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2277 
2278  dol_syslog(__METHOD__, LOG_DEBUG);
2279  $resql=$this->db->query($sql);
2280  if (!$resql)
2281  {
2282  $this->errors[]=$this->db->error();
2283  $error++;
2284  }
2285 
2286  if (! $error)
2287  {
2288  $this->oldcopy= clone $this;
2289  $this->remise_absolue = $remise;
2290  $this->update_price(1);
2291  }
2292 
2293  if (! $notrigger && empty($error))
2294  {
2295  // Call trigger
2296  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2297  if ($result < 0) $error++;
2298  // End call triggers
2299  }
2300 
2301  if (! $error)
2302  {
2303  $this->db->commit();
2304  return 1;
2305  }
2306  else
2307  {
2308  foreach($this->errors as $errmsg)
2309  {
2310  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2311  $this->error.=($this->error?', '.$errmsg:$errmsg);
2312  }
2313  $this->db->rollback();
2314  return -1*$error;
2315  }
2316  }
2317  }
2318 
2319 
2320 
2330  function reopen($user, $statut, $note='', $notrigger=0)
2331  {
2332 
2333  $this->statut = $statut;
2334  $error=0;
2335 
2336  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2337  $sql.= " SET fk_statut = ".$this->statut.",";
2338  if (! empty($note)) $sql.= " note_private = '".$this->db->escape($note)."',";
2339  $sql.= " date_cloture=NULL, fk_user_cloture=NULL";
2340  $sql.= " WHERE rowid = ".$this->id;
2341 
2342  $this->db->begin();
2343 
2344  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2345  $resql = $this->db->query($sql);
2346  if (! $resql) {
2347  $error++; $this->errors[]="Error ".$this->db->lasterror();
2348  }
2349  if (! $error)
2350  {
2351  if (! $notrigger)
2352  {
2353  // Call trigger
2354  $result=$this->call_trigger('PROPAL_REOPEN',$user);
2355  if ($result < 0) { $error++; }
2356  // End call triggers
2357  }
2358  }
2359 
2360  // Commit or rollback
2361  if ($error)
2362  {
2363  if (!empty($this->errors))
2364  {
2365  foreach($this->errors as $errmsg)
2366  {
2367  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2368  $this->error.=($this->error?', '.$errmsg:$errmsg);
2369  }
2370  }
2371  $this->db->rollback();
2372  return -1*$error;
2373  }
2374  else
2375  {
2376  $this->db->commit();
2377  return 1;
2378  }
2379  }
2380 
2381 
2391  function cloture($user, $statut, $note, $notrigger=0)
2392  {
2393  global $langs,$conf;
2394 
2395  $error=0;
2396  $now=dol_now();
2397 
2398  $this->db->begin();
2399 
2400  $newprivatenote = dol_concatdesc($this->note_private, $note);
2401 
2402  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2403  $sql.= " SET fk_statut = ".$statut.", note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
2404  $sql.= " WHERE rowid = ".$this->id;
2405 
2406  $resql=$this->db->query($sql);
2407  if ($resql)
2408  {
2409  $modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED?$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED:$this->modelpdf;
2410  $trigger_name='PROPAL_CLOSE_REFUSED';
2411 
2412  if ($statut == self::STATUS_SIGNED)
2413  {
2414  $trigger_name='PROPAL_CLOSE_SIGNED';
2415  $modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL?$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL:$this->modelpdf;
2416 
2417  // The connected company is classified as a client
2418  $soc=new Societe($this->db);
2419  $soc->id = $this->socid;
2420  $result=$soc->set_as_client();
2421 
2422  if ($result < 0)
2423  {
2424  $this->error=$this->db->lasterror();
2425  $this->db->rollback();
2426  return -2;
2427  }
2428  }
2429  if ($statut == self::STATUS_BILLED) // Why this ?
2430  {
2431  $trigger_name='PROPAL_CLASSIFY_BILLED';
2432  }
2433 
2434  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE))
2435  {
2436  // Define output language
2437  $outputlangs = $langs;
2438  if (! empty($conf->global->MAIN_MULTILANGS))
2439  {
2440  $outputlangs = new Translate("",$conf);
2441  $newlang=(GETPOST('lang_id','aZ09') ? GETPOST('lang_id','aZ09') : $this->thirdparty->default_lang);
2442  $outputlangs->setDefaultLang($newlang);
2443  }
2444  //$ret=$object->fetch($id); // Reload to get new records
2445  $this->generateDocument($modelpdf, $outputlangs);
2446  }
2447 
2448  if (! $error)
2449  {
2450  $this->oldcopy= clone $this;
2451  $this->statut = $statut;
2452  $this->date_cloture = $now;
2453  $this->note_private = $note;
2454  }
2455 
2456  if (! $notrigger && empty($error))
2457  {
2458  // Call trigger
2459  $result=$this->call_trigger($trigger_name,$user);
2460  if ($result < 0) { $error++; }
2461  // End call triggers
2462  }
2463 
2464  if ( ! $error )
2465  {
2466  $this->db->commit();
2467  return 1;
2468  }
2469  else
2470  {
2471  $this->db->rollback();
2472  return -1;
2473  }
2474  }
2475  else
2476  {
2477  $this->error=$this->db->lasterror();
2478  $this->db->rollback();
2479  return -1;
2480  }
2481  }
2482 
2490  function classifyBilled(User $user, $notrigger=0)
2491  {
2492  $error=0;
2493 
2494  $this->db->begin();
2495 
2496  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED;
2497  $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2498 
2499  dol_syslog(__METHOD__, LOG_DEBUG);
2500  $resql=$this->db->query($sql);
2501  if (!$resql)
2502  {
2503  $this->errors[]=$this->db->error();
2504  $error++;
2505  }
2506 
2507  if (! $error)
2508  {
2509  $this->oldcopy= clone $this;
2510  $this->statut=self::STATUS_BILLED;
2511  }
2512 
2513  if (! $notrigger && empty($error))
2514  {
2515  // Call trigger
2516  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2517  if ($result < 0) $error++;
2518  // End call triggers
2519  }
2520 
2521  if (! $error)
2522  {
2523  $this->db->commit();
2524  return 1;
2525  }
2526  else
2527  {
2528  foreach($this->errors as $errmsg)
2529  {
2530  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2531  $this->error.=($this->error?', '.$errmsg:$errmsg);
2532  }
2533  $this->db->rollback();
2534  return -1*$error;
2535  }
2536  }
2537 
2545  function classer_facturee()
2546  {
2547  global $user;
2548  dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING);
2549 
2550  return $this->classifyBilled($user);
2551  }
2552 
2560  function set_draft($user, $notrigger=0)
2561  {
2562  $error=0;
2563 
2564  $this->db->begin();
2565 
2566  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fk_statut = ".self::STATUS_DRAFT;
2567  $sql.= " WHERE rowid = ".$this->id;
2568 
2569  dol_syslog(__METHOD__, LOG_DEBUG);
2570  $resql=$this->db->query($sql);
2571  if (!$resql)
2572  {
2573  $this->errors[]=$this->db->error();
2574  $error++;
2575  }
2576 
2577  if (! $error)
2578  {
2579  $this->oldcopy= clone $this;
2580  $this->statut = self::STATUS_DRAFT;
2581  $this->brouillon = 1;
2582  }
2583 
2584  if (! $notrigger && empty($error))
2585  {
2586  // Call trigger
2587  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2588  if ($result < 0) $error++;
2589  // End call triggers
2590  }
2591 
2592  if (! $error)
2593  {
2594  $this->db->commit();
2595  return 1;
2596  }
2597  else
2598  {
2599  foreach($this->errors as $errmsg)
2600  {
2601  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2602  $this->error.=($this->error?', '.$errmsg:$errmsg);
2603  }
2604  $this->db->rollback();
2605  return -1*$error;
2606  }
2607  }
2608 
2609 
2623  function liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
2624  {
2625  global $user;
2626 
2627  $ga = array();
2628 
2629  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2630  $sql.= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2631  $sql.= " p.datep as dp, p.fin_validite as datelimite";
2632  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2633  $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2634  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2635  $sql.= " WHERE p.entity IN (".getEntity('propal').")";
2636  $sql.= " AND p.fk_soc = s.rowid";
2637  $sql.= " AND p.fk_statut = c.id";
2638  if (! $user->rights->societe->client->voir && ! $socid) //restriction
2639  {
2640  $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2641  }
2642  if ($socid) $sql.= " AND s.rowid = ".$socid;
2643  if ($draft) $sql.= " AND p.fk_statut = ".self::STATUS_DRAFT;
2644  if ($notcurrentuser > 0) $sql.= " AND p.fk_user_author <> ".$user->id;
2645  $sql.= $this->db->order($sortfield,$sortorder);
2646  $sql.= $this->db->plimit($limit,$offset);
2647 
2648  $result=$this->db->query($sql);
2649  if ($result)
2650  {
2651  $num = $this->db->num_rows($result);
2652  if ($num)
2653  {
2654  $i = 0;
2655  while ($i < $num)
2656  {
2657  $obj = $this->db->fetch_object($result);
2658 
2659  if ($shortlist == 1)
2660  {
2661  $ga[$obj->propalid] = $obj->ref;
2662  }
2663  else if ($shortlist == 2)
2664  {
2665  $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2666  }
2667  else
2668  {
2669  $ga[$i]['id'] = $obj->propalid;
2670  $ga[$i]['ref'] = $obj->ref;
2671  $ga[$i]['name'] = $obj->name;
2672  }
2673 
2674  $i++;
2675  }
2676  }
2677  return $ga;
2678  }
2679  else
2680  {
2681  dol_print_error($this->db);
2682  return -1;
2683  }
2684  }
2685 
2692  {
2693  return $this->InvoiceArrayList($this->id);
2694  }
2695 
2702  function InvoiceArrayList($id)
2703  {
2704  $ga = array();
2705  $linkedInvoices = array();
2706 
2707  $this->fetchObjectLinked($id,$this->element);
2708  foreach($this->linkedObjectsIds as $objecttype => $objectid)
2709  {
2710  // Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
2711  // On parcourt donc une liste d'objets en tant qu'objet unique
2712  foreach($objectid as $key => $object)
2713  {
2714  // Cas des factures liees directement
2715  if ($objecttype == 'facture')
2716  {
2717  $linkedInvoices[] = $object;
2718  }
2719  // Cas des factures liees par un autre objet (ex: commande)
2720  else
2721  {
2722  $this->fetchObjectLinked($object,$objecttype);
2723  foreach($this->linkedObjectsIds as $subobjecttype => $subobjectid)
2724  {
2725  foreach($subobjectid as $subkey => $subobject)
2726  {
2727  if ($subobjecttype == 'facture')
2728  {
2729  $linkedInvoices[] = $subobject;
2730  }
2731  }
2732  }
2733  }
2734  }
2735  }
2736 
2737  if (count($linkedInvoices) > 0)
2738  {
2739  $sql= "SELECT rowid as facid, facnumber, total, datef as df, fk_user_author, fk_statut, paye";
2740  $sql.= " FROM ".MAIN_DB_PREFIX."facture";
2741  $sql.= " WHERE rowid IN (".implode(',',$linkedInvoices).")";
2742 
2743  dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2744  $resql=$this->db->query($sql);
2745 
2746  if ($resql)
2747  {
2748  $tab_sqlobj=array();
2749  $nump = $this->db->num_rows($resql);
2750  for ($i = 0;$i < $nump;$i++)
2751  {
2752  $sqlobj = $this->db->fetch_object($resql);
2753  $tab_sqlobj[] = $sqlobj;
2754  }
2755  $this->db->free($resql);
2756 
2757  $nump = count($tab_sqlobj);
2758 
2759  if ($nump)
2760  {
2761  $i = 0;
2762  while ($i < $nump)
2763  {
2764  $obj = array_shift($tab_sqlobj);
2765 
2766  $ga[$i] = $obj;
2767 
2768  $i++;
2769  }
2770  }
2771  return $ga;
2772  }
2773  else
2774  {
2775  return -1;
2776  }
2777  }
2778  else return $ga;
2779  }
2780 
2788  function delete($user, $notrigger=0)
2789  {
2790  global $conf;
2791  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2792 
2793  $error=0;
2794 
2795  $this->db->begin();
2796 
2797  if (! $notrigger)
2798  {
2799  // Call trigger
2800  $result=$this->call_trigger('PROPAL_DELETE',$user);
2801  if ($result < 0) { $error++; }
2802  // End call triggers
2803  }
2804 
2805  if (! $error)
2806  {
2807  $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id;
2808  if ($this->db->query($sql))
2809  {
2810  $sql = "DELETE FROM ".MAIN_DB_PREFIX."propal WHERE rowid = ".$this->id;
2811  if ($this->db->query($sql))
2812  {
2813  // Delete linked object
2814  $res = $this->deleteObjectLinked();
2815  if ($res < 0) $error++;
2816 
2817  // Delete linked contacts
2818  $res = $this->delete_linked_contact();
2819  if ($res < 0) $error++;
2820 
2821  if (! $error)
2822  {
2823  // We remove directory
2824  $ref = dol_sanitizeFileName($this->ref);
2825  if ($conf->propal->dir_output && !empty($this->ref))
2826  {
2827  $dir = $conf->propal->dir_output . "/" . $ref ;
2828  $file = $dir . "/" . $ref . ".pdf";
2829  if (file_exists($file))
2830  {
2831  dol_delete_preview($this);
2832 
2833  if (! dol_delete_file($file,0,0,0,$this)) // For triggers
2834  {
2835  $this->error='ErrorFailToDeleteFile';
2836  $this->errors=array('ErrorFailToDeleteFile');
2837  $this->db->rollback();
2838  return 0;
2839  }
2840  }
2841  if (file_exists($dir))
2842  {
2843  $res=@dol_delete_dir_recursive($dir);
2844  if (! $res)
2845  {
2846  $this->error='ErrorFailToDeleteDir';
2847  $this->errors=array('ErrorFailToDeleteDir');
2848  $this->db->rollback();
2849  return 0;
2850  }
2851  }
2852  }
2853  }
2854 
2855  // Removed extrafields
2856  if (! $error)
2857  {
2858  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2859  {
2860  $result=$this->deleteExtraFields();
2861  if ($result < 0)
2862  {
2863  $error++;
2864  $errorflag=-4;
2865  dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2866  }
2867  }
2868  }
2869 
2870  if (! $error)
2871  {
2872  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2873  $this->db->commit();
2874  return 1;
2875  }
2876  else
2877  {
2878  $this->error=$this->db->lasterror();
2879  $this->db->rollback();
2880  return 0;
2881  }
2882  }
2883  else
2884  {
2885  $this->error=$this->db->lasterror();
2886  $this->db->rollback();
2887  return -3;
2888  }
2889  }
2890  else
2891  {
2892  $this->error=$this->db->lasterror();
2893  $this->db->rollback();
2894  return -2;
2895  }
2896  }
2897  else
2898  {
2899  $this->db->rollback();
2900  return -1;
2901  }
2902  }
2903 
2912  function availability($availability_id, $notrigger=0)
2913  {
2914  global $user;
2915 
2916  if ($this->statut >= self::STATUS_DRAFT)
2917  {
2918  $error=0;
2919 
2920  $this->db->begin();
2921 
2922  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
2923  $sql .= ' SET fk_availability = '.$availability_id;
2924  $sql .= ' WHERE rowid='.$this->id;
2925 
2926  dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
2927  $resql=$this->db->query($sql);
2928  if (!$resql)
2929  {
2930  $this->errors[]=$this->db->error();
2931  $error++;
2932  }
2933 
2934  if (! $error)
2935  {
2936  $this->oldcopy= clone $this;
2937  $this->availability_id = $availability_id;
2938  }
2939 
2940  if (! $notrigger && empty($error))
2941  {
2942  // Call trigger
2943  $result=$this->call_trigger('PROPAL_MODIFY',$user);
2944  if ($result < 0) $error++;
2945  // End call triggers
2946  }
2947 
2948  if (! $error)
2949  {
2950  $this->db->commit();
2951  return 1;
2952  }
2953  else
2954  {
2955  foreach($this->errors as $errmsg)
2956  {
2957  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2958  $this->error.=($this->error?', '.$errmsg:$errmsg);
2959  }
2960  $this->db->rollback();
2961  return -1*$error;
2962  }
2963  }
2964  else
2965  {
2966  $error_str='Propal status do not meet requirement '.$this->statut;
2967  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2968  $this->error=$error_str;
2969  $this->errors[]= $this->error;
2970  return -2;
2971  }
2972  }
2973 
2982  function demand_reason($demand_reason_id, $notrigger=0)
2983  {
2984  global $user;
2985 
2986  if ($this->statut >= self::STATUS_DRAFT)
2987  {
2988  $error=0;
2989 
2990  $this->db->begin();
2991 
2992  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
2993  $sql .= ' SET fk_input_reason = '.$demand_reason_id;
2994  $sql .= ' WHERE rowid='.$this->id;
2995 
2996  dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
2997  $resql=$this->db->query($sql);
2998  if (!$resql)
2999  {
3000  $this->errors[]=$this->db->error();
3001  $error++;
3002  }
3003 
3004  if (! $error)
3005  {
3006  $this->oldcopy= clone $this;
3007  $this->demand_reason_id = $demand_reason_id;
3008  }
3009 
3010  if (! $notrigger && empty($error))
3011  {
3012  // Call trigger
3013  $result=$this->call_trigger('PROPAL_MODIFY',$user);
3014  if ($result < 0) $error++;
3015  // End call triggers
3016  }
3017 
3018  if (! $error)
3019  {
3020  $this->db->commit();
3021  return 1;
3022  }
3023  else
3024  {
3025  foreach($this->errors as $errmsg)
3026  {
3027  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3028  $this->error.=($this->error?', '.$errmsg:$errmsg);
3029  }
3030  $this->db->rollback();
3031  return -1*$error;
3032  }
3033  }
3034  else
3035  {
3036  $error_str='Propal status do not meet requirement '.$this->statut;
3037  dol_syslog(__METHOD__.$error_str, LOG_ERR);
3038  $this->error=$error_str;
3039  $this->errors[]= $this->error;
3040  return -2;
3041  }
3042  }
3043 
3044 
3051  function info($id)
3052  {
3053  $sql = "SELECT c.rowid, ";
3054  $sql.= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
3055  $sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
3056  $sql.= " FROM ".MAIN_DB_PREFIX."propal as c";
3057  $sql.= " WHERE c.rowid = ".$id;
3058 
3059  $result = $this->db->query($sql);
3060 
3061  if ($result)
3062  {
3063  if ($this->db->num_rows($result))
3064  {
3065  $obj = $this->db->fetch_object($result);
3066 
3067  $this->id = $obj->rowid;
3068 
3069  $this->date_creation = $this->db->jdate($obj->datec);
3070  $this->date_validation = $this->db->jdate($obj->datev);
3071  $this->date_cloture = $this->db->jdate($obj->dateo);
3072 
3073  $cuser = new User($this->db);
3074  $cuser->fetch($obj->fk_user_author);
3075  $this->user_creation = $cuser;
3076 
3077  if ($obj->fk_user_valid)
3078  {
3079  $vuser = new User($this->db);
3080  $vuser->fetch($obj->fk_user_valid);
3081  $this->user_validation = $vuser;
3082  }
3083 
3084  if ($obj->fk_user_cloture)
3085  {
3086  $cluser = new User($this->db);
3087  $cluser->fetch($obj->fk_user_cloture);
3088  $this->user_cloture = $cluser;
3089  }
3090 
3091 
3092  }
3093  $this->db->free($result);
3094 
3095  }
3096  else
3097  {
3098  dol_print_error($this->db);
3099  }
3100  }
3101 
3102 
3109  function getLibStatut($mode=0)
3110  {
3111  return $this->LibStatut($this->statut, $mode);
3112  }
3113 
3121  function LibStatut($statut,$mode=1)
3122  {
3123  global $langs;
3124  $langs->load("propal");
3125 
3126  $statuttrans='';
3127  if ($statut==self::STATUS_DRAFT) $statuttrans='statut0';
3128  if ($statut==self::STATUS_VALIDATED) $statuttrans='statut1';
3129  if ($statut==self::STATUS_SIGNED) $statuttrans='statut3';
3130  if ($statut==self::STATUS_NOTSIGNED) $statuttrans='statut5';
3131  if ($statut==self::STATUS_BILLED) $statuttrans='statut6';
3132 
3133  if ($mode == 0) return $this->labelstatut[$statut];
3134  if ($mode == 1) return $this->labelstatut_short[$statut];
3135  if ($mode == 2) return img_picto($this->labelstatut_short[$statut], $statuttrans).' '.$this->labelstatut_short[$statut];
3136  if ($mode == 3) return img_picto($this->labelstatut[$statut], $statuttrans);
3137  if ($mode == 4) return img_picto($this->labelstatut[$statut],$statuttrans).' '.$this->labelstatut[$statut];
3138  if ($mode == 5) return '<span class="hideonsmartphone">'.$this->labelstatut_short[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3139  if ($mode == 6) return '<span class="hideonsmartphone">'.$this->labelstatut[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3140  }
3141 
3142 
3150  function load_board($user,$mode)
3151  {
3152  global $conf, $langs;
3153 
3154  $clause = " WHERE";
3155 
3156  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin";
3157  $sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3158  if (!$user->rights->societe->client->voir && !$user->societe_id)
3159  {
3160  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3161  $sql.= " WHERE sc.fk_user = " .$user->id;
3162  $clause = " AND";
3163  }
3164  $sql.= $clause." p.entity IN (".getEntity('propal').")";
3165  if ($mode == 'opened') $sql.= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3166  if ($mode == 'signed') $sql.= " AND p.fk_statut = ".self::STATUS_SIGNED;
3167  if ($user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
3168 
3169  $resql=$this->db->query($sql);
3170  if ($resql)
3171  {
3172  $langs->load("propal");
3173  $now=dol_now();
3174 
3175  $delay_warning = 0;
3176  $statut = 0;
3177  $label = '';
3178  if ($mode == 'opened') {
3179  $delay_warning=$conf->propal->cloture->warning_delay;
3180  $statut = self::STATUS_VALIDATED;
3181  $label = $langs->trans("PropalsToClose");
3182  }
3183  if ($mode == 'signed') {
3184  $delay_warning=$conf->propal->facturation->warning_delay;
3185  $statut = self::STATUS_SIGNED;
3186  $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3187  }
3188 
3189  $response = new WorkboardResponse();
3190  $response->warning_delay = $delay_warning/60/60/24;
3191  $response->label = $label;
3192  $response->url = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals';
3193  $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3194  $response->img = img_object('',"propal");
3195 
3196  // This assignment in condition is not a bug. It allows walking the results.
3197  while ($obj=$this->db->fetch_object($resql))
3198  {
3199  $response->nbtodo++;
3200  if ($mode == 'opened')
3201  {
3202  $datelimit = $this->db->jdate($obj->datefin);
3203  if ($datelimit < ($now - $delay_warning))
3204  {
3205  $response->nbtodolate++;
3206  }
3207  }
3208  // TODO Definir regle des propales a facturer en retard
3209  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3210  }
3211 
3212  return $response;
3213  }
3214  else
3215  {
3216  $this->error=$this->db->error();
3217  return -1;
3218  }
3219  }
3220 
3221 
3229  function initAsSpecimen()
3230  {
3231  global $langs;
3232 
3233  // Load array of products prodids
3234  $num_prods = 0;
3235  $prodids = array();
3236  $sql = "SELECT rowid";
3237  $sql.= " FROM ".MAIN_DB_PREFIX."product";
3238  $sql.= " WHERE entity IN (".getEntity('product').")";
3239  $resql = $this->db->query($sql);
3240  if ($resql)
3241  {
3242  $num_prods = $this->db->num_rows($resql);
3243  $i = 0;
3244  while ($i < $num_prods)
3245  {
3246  $i++;
3247  $row = $this->db->fetch_row($resql);
3248  $prodids[$i] = $row[0];
3249  }
3250  }
3251 
3252  // Initialise parametres
3253  $this->id=0;
3254  $this->ref = 'SPECIMEN';
3255  $this->ref_client='NEMICEPS';
3256  $this->specimen=1;
3257  $this->socid = 1;
3258  $this->date = time();
3259  $this->fin_validite = $this->date+3600*24*30;
3260  $this->cond_reglement_id = 1;
3261  $this->cond_reglement_code = 'RECEP';
3262  $this->mode_reglement_id = 7;
3263  $this->mode_reglement_code = 'CHQ';
3264  $this->availability_id = 1;
3265  $this->availability_code = 'AV_NOW';
3266  $this->demand_reason_id = 1;
3267  $this->demand_reason_code = 'SRC_00';
3268  $this->note_public='This is a comment (public)';
3269  $this->note_private='This is a comment (private)';
3270  // Lines
3271  $nbp = 5;
3272  $xnbp = 0;
3273  while ($xnbp < $nbp)
3274  {
3275  $line=new PropaleLigne($this->db);
3276  $line->desc=$langs->trans("Description")." ".$xnbp;
3277  $line->qty=1;
3278  $line->subprice=100;
3279  $line->price=100;
3280  $line->tva_tx=20;
3281  $line->localtax1_tx=0;
3282  $line->localtax2_tx=0;
3283  if ($xnbp == 2)
3284  {
3285  $line->total_ht=50;
3286  $line->total_ttc=60;
3287  $line->total_tva=10;
3288  $line->remise_percent=50;
3289  }
3290  else
3291  {
3292  $line->total_ht=100;
3293  $line->total_ttc=120;
3294  $line->total_tva=20;
3295  $line->remise_percent=00;
3296  }
3297 
3298  if ($num_prods > 0)
3299  {
3300  $prodid = mt_rand(1, $num_prods);
3301  $line->fk_product=$prodids[$prodid];
3302  $line->product_ref='SPECIMEN';
3303  }
3304 
3305  $this->lines[$xnbp]=$line;
3306 
3307  $this->total_ht += $line->total_ht;
3308  $this->total_tva += $line->total_tva;
3309  $this->total_ttc += $line->total_ttc;
3310 
3311  $xnbp++;
3312  }
3313  }
3314 
3320  function load_state_board()
3321  {
3322  global $user;
3323 
3324  $this->nb=array();
3325  $clause = "WHERE";
3326 
3327  $sql = "SELECT count(p.rowid) as nb";
3328  $sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3329  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3330  if (!$user->rights->societe->client->voir && !$user->societe_id)
3331  {
3332  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3333  $sql.= " WHERE sc.fk_user = " .$user->id;
3334  $clause = "AND";
3335  }
3336  $sql.= " ".$clause." p.entity IN (".getEntity('propal').")";
3337 
3338  $resql=$this->db->query($sql);
3339  if ($resql)
3340  {
3341  // This assignment in condition is not a bug. It allows walking the results.
3342  while ($obj=$this->db->fetch_object($resql))
3343  {
3344  $this->nb["proposals"]=$obj->nb;
3345  }
3346  $this->db->free($resql);
3347  return 1;
3348  }
3349  else
3350  {
3351  dol_print_error($this->db);
3352  $this->error=$this->db->error();
3353  return -1;
3354  }
3355  }
3356 
3357 
3365  function getNextNumRef($soc)
3366  {
3367  global $conf,$langs;
3368  $langs->load("propal");
3369 
3370  if (! empty($conf->global->PROPALE_ADDON))
3371  {
3372  $mybool=false;
3373 
3374  $file = $conf->global->PROPALE_ADDON.".php";
3375  $classname = $conf->global->PROPALE_ADDON;
3376 
3377  // Include file with class
3378  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3379  foreach ($dirmodels as $reldir) {
3380 
3381  $dir = dol_buildpath($reldir."core/modules/propale/");
3382 
3383  // Load file with numbering class (if found)
3384  $mybool|=@include_once $dir.$file;
3385  }
3386 
3387  if (! $mybool)
3388  {
3389  dol_print_error('',"Failed to include file ".$file);
3390  return '';
3391  }
3392 
3393  $obj = new $classname();
3394  $numref = "";
3395  $numref = $obj->getNextValue($soc,$this);
3396 
3397  if ($numref != "")
3398  {
3399  return $numref;
3400  }
3401  else
3402  {
3403  $this->error=$obj->error;
3404  //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3405  return "";
3406  }
3407  }
3408  else
3409  {
3410  $langs->load("errors");
3411  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3412  return "";
3413  }
3414  }
3415 
3426  function getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1)
3427  {
3428  global $langs, $conf, $user;
3429 
3430  if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
3431 
3432  $result='';
3433  $label='';
3434  $url='';
3435 
3436  if ($user->rights->propal->lire)
3437  {
3438  $label = '<u>' . $langs->trans("ShowPropal") . '</u>';
3439  if (! empty($this->ref))
3440  $label.= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3441  if (! empty($this->ref_client))
3442  $label.= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3443  if (! empty($this->total_ht))
3444  $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3445  if (! empty($this->total_tva))
3446  $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3447  if (! empty($this->total_ttc))
3448  $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3449  if ($option == '') {
3450  $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3451  }
3452  if ($option == 'compta') { // deprecated
3453  $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3454  }
3455  if ($option == 'expedition') {
3456  $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id. $get_params;
3457  }
3458  if ($option == 'document') {
3459  $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id. $get_params;
3460  }
3461 
3462  if ($option != 'nolink')
3463  {
3464  // Add param to save lastsearch_values or not
3465  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3466  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3467  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3468  }
3469  }
3470 
3471  $linkclose='';
3472  if (empty($notooltip) && $user->rights->propal->lire)
3473  {
3474  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3475  {
3476  $label=$langs->trans("ShowPropal");
3477  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3478  }
3479  $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3480  $linkclose.=' class="classfortooltip"';
3481  }
3482 
3483  $linkstart = '<a href="'.$url.'"';
3484  $linkstart.=$linkclose.'>';
3485  $linkend='</a>';
3486 
3487  $result .= $linkstart;
3488  if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3489  if ($withpicto != 2) $result.= $this->ref;
3490  $result .= $linkend;
3491 
3492  return $result;
3493  }
3494 
3500  function getLinesArray()
3501  {
3502  // TODO Duplicate with fetch_lines ? Wich one to keep ?
3503 
3504  $this->lines = array();
3505 
3506  $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
3507  $sql.= ' pt.qty, pt.vat_src_code, pt.tva_tx, pt.localtax1_tx, pt.localtax2_tx, pt.localtax1_type, pt.localtax2_type, pt.remise_percent, pt.subprice, pt.info_bits,';
3508  $sql.= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.total_localtax1, pt.total_localtax2, pt.fk_product_fournisseur_price as fk_fournprice, pt.buy_price_ht as pa_ht, pt.special_code,';
3509  $sql.= ' pt.date_start, pt.date_end, pt.product_type, pt.rang, pt.fk_parent_line,';
3510  $sql.= ' pt.fk_unit,';
3511  $sql.= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.description as product_desc, p.tobatch as product_tobatch,';
3512  $sql.= ' p.entity,';
3513  $sql.= ' pt.fk_multicurrency, pt.multicurrency_code, pt.multicurrency_subprice, pt.multicurrency_total_ht, pt.multicurrency_total_tva, pt.multicurrency_total_ttc';
3514  $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pt';
3515  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
3516  $sql.= ' WHERE pt.fk_propal = '.$this->id;
3517  $sql.= ' ORDER BY pt.rang ASC, pt.rowid';
3518 
3519  dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
3520  $resql = $this->db->query($sql);
3521  if ($resql)
3522  {
3523  $num = $this->db->num_rows($resql);
3524  $i = 0;
3525 
3526  while ($i < $num)
3527  {
3528  $obj = $this->db->fetch_object($resql);
3529 
3530  $this->lines[$i] = new PropaleLigne($this->db);
3531  $this->lines[$i]->id = $obj->rowid; // for backward compatibility
3532  $this->lines[$i]->rowid = $obj->rowid;
3533  $this->lines[$i]->label = $obj->custom_label;
3534  $this->lines[$i]->desc = $obj->description;
3535  $this->lines[$i]->description = $obj->description;
3536  $this->lines[$i]->fk_product = $obj->fk_product;
3537  $this->lines[$i]->ref = $obj->ref;
3538  $this->lines[$i]->product_ref = $obj->ref;
3539  $this->lines[$i]->entity = $obj->entity; // Product entity
3540  $this->lines[$i]->product_label = $obj->product_label;
3541  $this->lines[$i]->product_desc = $obj->product_desc;
3542  $this->lines[$i]->product_tobatch = $obj->product_tobatch;
3543  $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
3544  $this->lines[$i]->product_type = $obj->product_type;
3545  $this->lines[$i]->qty = $obj->qty;
3546  $this->lines[$i]->subprice = $obj->subprice;
3547  $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
3548  $this->lines[$i]->remise_percent = $obj->remise_percent;
3549 
3550  $this->lines[$i]->vat_src_code = $obj->vat_src_code;
3551  $this->lines[$i]->tva_tx = $obj->tva_tx;
3552  $this->lines[$i]->localtax1_tx = $obj->localtax1_tx;
3553  $this->lines[$i]->localtax2_tx = $obj->localtax2_tx;
3554  $this->lines[$i]->localtax1_type = $obj->localtax1_type;
3555  $this->lines[$i]->localtax2_type = $obj->localtax2_type;
3556  $this->lines[$i]->info_bits = $obj->info_bits;
3557  $this->lines[$i]->total_ht = $obj->total_ht;
3558  $this->lines[$i]->total_tva = $obj->total_tva;
3559  $this->lines[$i]->total_ttc = $obj->total_ttc;
3560  $this->lines[$i]->total_localtax1 = $obj->total_localtax1;
3561  $this->lines[$i]->total_localtax2 = $obj->total_localtax2;
3562  $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
3563  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
3564  $this->lines[$i]->pa_ht = $marginInfos[0];
3565  $this->lines[$i]->marge_tx = $marginInfos[1];
3566  $this->lines[$i]->marque_tx = $marginInfos[2];
3567  $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
3568  $this->lines[$i]->special_code = $obj->special_code;
3569  $this->lines[$i]->rang = $obj->rang;
3570  $this->lines[$i]->date_start = $this->db->jdate($obj->date_start);
3571  $this->lines[$i]->date_end = $this->db->jdate($obj->date_end);
3572  $this->lines[$i]->fk_unit = $obj->fk_unit;
3573 
3574  // Multicurrency
3575  $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
3576  $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
3577  $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
3578  $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
3579  $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
3580  $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3581 
3582  $i++;
3583  }
3584  $this->db->free($resql);
3585 
3586  return 1;
3587  }
3588  else
3589  {
3590  $this->error=$this->db->error();
3591  return -1;
3592  }
3593  }
3594 
3605  public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
3606  {
3607  global $conf,$langs;
3608 
3609  $langs->load("propale");
3610 
3611  if (! dol_strlen($modele)) {
3612 
3613  $modele = 'azur';
3614 
3615  if ($this->modelpdf) {
3616  $modele = $this->modelpdf;
3617  } elseif (! empty($conf->global->PROPALE_ADDON_PDF)) {
3618  $modele = $conf->global->PROPALE_ADDON_PDF;
3619  }
3620  }
3621 
3622  $modelpath = "core/modules/propale/doc/";
3623 
3624  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
3625  }
3626 
3635  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3636  {
3637  $tables = array(
3638  'propal'
3639  );
3640 
3641  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3642  }
3643 }
3644 
3645 
3650 {
3651  public $element='propaldet';
3652  public $table_element='propaldet';
3653 
3654  var $oldline;
3655 
3656  // From llx_propaldet
3657  var $fk_propal;
3658  var $fk_parent_line;
3659  var $desc; // Description ligne
3660  var $fk_product; // Id produit predefini
3671  var $product_type = Product::TYPE_PRODUCT;
3672 
3673  var $qty;
3674  var $tva_tx;
3675  var $subprice;
3676  var $remise_percent;
3677  var $fk_remise_except;
3678 
3679  var $rang = 0;
3680 
3681  var $fk_fournprice;
3682  var $pa_ht;
3683  var $marge_tx;
3684  var $marque_tx;
3685 
3686  var $special_code; // Tag for special lines (exlusive tags)
3687  // 1: frais de port
3688  // 2: ecotaxe
3689  // 3: option line (when qty = 0)
3690 
3691  var $info_bits = 0; // Liste d'options cumulables:
3692  // Bit 0: 0 si TVA normal - 1 si TVA NPR
3693  // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
3694 
3695  var $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3696  var $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3697  var $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3698 
3703  var $remise;
3708  var $price;
3709 
3710  // From llx_product
3715  var $ref;
3720  public $product_ref;
3730  public $product_label;
3735  public $product_desc;
3736 
3737  var $localtax1_tx; // Local tax 1
3738  var $localtax2_tx; // Local tax 2
3739  var $localtax1_type; // Local tax 1 type
3740  var $localtax2_type; // Local tax 2 type
3741  var $total_localtax1; // Line total local tax 1
3742  var $total_localtax2; // Line total local tax 2
3743 
3744  var $date_start;
3745  var $date_end;
3746 
3747  var $skip_update_total; // Skip update price total for special lines
3748 
3749  // Multicurrency
3750  var $fk_multicurrency;
3751  var $multicurrency_code;
3752  var $multicurrency_subprice;
3753  var $multicurrency_total_ht;
3754  var $multicurrency_total_tva;
3755  var $multicurrency_total_ttc;
3756 
3762  function __construct($db)
3763  {
3764  $this->db= $db;
3765  }
3766 
3773  function fetch($rowid)
3774  {
3775  $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.vat_src_code, pd.tva_tx,';
3776  $sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3777  $sql.= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
3778  $sql.= ' pd.fk_unit,';
3779  $sql.= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3780  $sql.= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
3781  $sql.= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3782  $sql.= ' pd.date_start, pd.date_end, pd.product_type';
3783  $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
3784  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3785  $sql.= ' WHERE pd.rowid = '.$rowid;
3786 
3787  $result = $this->db->query($sql);
3788  if ($result)
3789  {
3790  $objp = $this->db->fetch_object($result);
3791 
3792  if ($objp)
3793  {
3794  $this->id = $objp->rowid;
3795  $this->rowid = $objp->rowid; // deprecated
3796  $this->fk_propal = $objp->fk_propal;
3797  $this->fk_parent_line = $objp->fk_parent_line;
3798  $this->label = $objp->custom_label;
3799  $this->desc = $objp->description;
3800  $this->qty = $objp->qty;
3801  $this->price = $objp->price; // deprecated
3802  $this->subprice = $objp->subprice;
3803  $this->vat_src_code = $objp->vat_src_code;
3804  $this->tva_tx = $objp->tva_tx;
3805  $this->remise = $objp->remise; // deprecated
3806  $this->remise_percent = $objp->remise_percent;
3807  $this->fk_remise_except = $objp->fk_remise_except;
3808  $this->fk_product = $objp->fk_product;
3809  $this->info_bits = $objp->info_bits;
3810 
3811  $this->total_ht = $objp->total_ht;
3812  $this->total_tva = $objp->total_tva;
3813  $this->total_ttc = $objp->total_ttc;
3814 
3815  $this->fk_fournprice = $objp->fk_fournprice;
3816 
3817  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3818  $this->pa_ht = $marginInfos[0];
3819  $this->marge_tx = $marginInfos[1];
3820  $this->marque_tx = $marginInfos[2];
3821 
3822  $this->special_code = $objp->special_code;
3823  $this->product_type = $objp->product_type;
3824  $this->rang = $objp->rang;
3825 
3826  $this->ref = $objp->product_ref; // deprecated
3827  $this->product_ref = $objp->product_ref;
3828  $this->libelle = $objp->product_label; // deprecated
3829  $this->product_label = $objp->product_label;
3830  $this->product_desc = $objp->product_desc;
3831  $this->fk_unit = $objp->fk_unit;
3832 
3833  $this->date_start = $this->db->jdate($objp->date_start);
3834  $this->date_end = $this->db->jdate($objp->date_end);
3835 
3836  // Multicurrency
3837  $this->fk_multicurrency = $objp->fk_multicurrency;
3838  $this->multicurrency_code = $objp->multicurrency_code;
3839  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3840  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3841  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3842  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3843 
3844  $this->db->free($result);
3845 
3846  return 1;
3847  }
3848  else
3849  {
3850  return 0;
3851  }
3852  }
3853  else
3854  {
3855  return -1;
3856  }
3857  }
3858 
3865  function insert($notrigger=0)
3866  {
3867  global $conf,$user;
3868 
3869  $error=0;
3870 
3871  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3872 
3873  $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'.
3874 
3875  // Clean parameters
3876  if (empty($this->tva_tx)) $this->tva_tx=0;
3877  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
3878  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
3879  if (empty($this->localtax1_type)) $this->localtax1_type=0;
3880  if (empty($this->localtax2_type)) $this->localtax2_type=0;
3881  if (empty($this->total_localtax1)) $this->total_localtax1=0;
3882  if (empty($this->total_localtax2)) $this->total_localtax2=0;
3883  if (empty($this->rang)) $this->rang=0;
3884  if (empty($this->remise)) $this->remise=0;
3885  if (empty($this->remise_percent) || ! is_numeric($this->remise_percent)) $this->remise_percent=0;
3886  if (empty($this->info_bits)) $this->info_bits=0;
3887  if (empty($this->special_code)) $this->special_code=0;
3888  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
3889  if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
3890  if (! is_numeric($this->qty)) $this->qty = 0;
3891  if (empty($this->pa_ht)) $this->pa_ht=0;
3892  if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice=0;
3893  if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht=0;
3894  if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
3895  if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
3896 
3897  // if buy price not defined, define buyprice as configured in margin admin
3898  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
3899  {
3900  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3901  {
3902  return $result;
3903  }
3904  else
3905  {
3906  $this->pa_ht = $result;
3907  }
3908  }
3909 
3910  // Check parameters
3911  if ($this->product_type < 0) return -1;
3912 
3913  $this->db->begin();
3914 
3915  // Insert line into database
3916  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
3917  $sql.= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
3918  $sql.= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3919  $sql.= ' subprice, remise_percent, ';
3920  $sql.= ' info_bits, ';
3921  $sql.= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3922  $sql.= ' fk_unit,';
3923  $sql.= ' date_start, date_end';
3924  $sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
3925  $sql.= " VALUES (".$this->fk_propal.",";
3926  $sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
3927  $sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
3928  $sql.= " '".$this->db->escape($this->desc)."',";
3929  $sql.= " ".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":"null").",";
3930  $sql.= " '".$this->db->escape($this->product_type)."',";
3931  $sql.= " ".($this->fk_remise_except?"'".$this->db->escape($this->fk_remise_except)."'":"null").",";
3932  $sql.= " ".price2num($this->qty).",";
3933  $sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
3934  $sql.= " ".price2num($this->tva_tx).",";
3935  $sql.= " ".price2num($this->localtax1_tx).",";
3936  $sql.= " ".price2num($this->localtax2_tx).",";
3937  $sql.= " '".$this->db->escape($this->localtax1_type)."',";
3938  $sql.= " '".$this->db->escape($this->localtax2_type)."',";
3939  $sql.= " ".($this->subprice?price2num($this->subprice):"null").",";
3940  $sql.= " ".price2num($this->remise_percent).",";
3941  $sql.= " ".(isset($this->info_bits)?"'".$this->db->escape($this->info_bits)."'":"null").",";
3942  $sql.= " ".price2num($this->total_ht).",";
3943  $sql.= " ".price2num($this->total_tva).",";
3944  $sql.= " ".price2num($this->total_localtax1).",";
3945  $sql.= " ".price2num($this->total_localtax2).",";
3946  $sql.= " ".price2num($this->total_ttc).",";
3947  $sql.= " ".(!empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null").",";
3948  $sql.= " ".(isset($this->pa_ht)?"'".price2num($this->pa_ht)."'":"null").",";
3949  $sql.= ' '.$this->special_code.',';
3950  $sql.= ' '.$this->rang.',';
3951  $sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit).',';
3952  $sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
3953  $sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
3954  $sql.= ", ".($this->fk_multicurrency > 0?$this->fk_multicurrency:'null');
3955  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
3956  $sql.= ", ".$this->multicurrency_subprice;
3957  $sql.= ", ".$this->multicurrency_total_ht;
3958  $sql.= ", ".$this->multicurrency_total_tva;
3959  $sql.= ", ".$this->multicurrency_total_ttc;
3960  $sql.= ')';
3961 
3962  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3963  $resql=$this->db->query($sql);
3964  if ($resql)
3965  {
3966  $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
3967 
3968  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
3969  {
3970  $this->id=$this->rowid;
3971  $result=$this->insertExtraFields();
3972  if ($result < 0)
3973  {
3974  $error++;
3975  }
3976  }
3977 
3978  if (! $notrigger)
3979  {
3980  // Call trigger
3981  $result=$this->call_trigger('LINEPROPAL_INSERT',$user);
3982  if ($result < 0)
3983  {
3984  $this->db->rollback();
3985  return -1;
3986  }
3987  // End call triggers
3988  }
3989 
3990  $this->db->commit();
3991  return 1;
3992  }
3993  else
3994  {
3995  $this->error=$this->db->error()." sql=".$sql;
3996  $this->db->rollback();
3997  return -1;
3998  }
3999  }
4000 
4008  function delete(User $user, $notrigger=0)
4009  {
4010  global $conf;
4011 
4012  $error=0;
4013  $this->db->begin();
4014 
4015  $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".$this->rowid;
4016  dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4017  if ($this->db->query($sql) )
4018  {
4019 
4020  // Remove extrafields
4021  if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
4022  {
4023  $this->id=$this->rowid;
4024  $result=$this->deleteExtraFields();
4025  if ($result < 0)
4026  {
4027  $error++;
4028  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4029  }
4030  }
4031 
4032  if (! $error && ! $notrigger)
4033  {
4034  // Call trigger
4035  $result=$this->call_trigger('LINEPROPAL_DELETE',$user);
4036  if ($result < 0)
4037  {
4038  $this->db->rollback();
4039  return -1;
4040  }
4041  }
4042  // End call triggers
4043 
4044  $this->db->commit();
4045 
4046  return 1;
4047  }
4048  else
4049  {
4050  $this->error=$this->db->error()." sql=".$sql;
4051  $this->db->rollback();
4052  return -1;
4053  }
4054  }
4055 
4062  function update($notrigger=0)
4063  {
4064  global $conf,$user;
4065 
4066  $error=0;
4067 
4068  $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'.
4069 
4070  // Clean parameters
4071  if (empty($this->tva_tx)) $this->tva_tx=0;
4072  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4073  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4074  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4075  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4076  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4077  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4078  if (empty($this->marque_tx)) $this->marque_tx=0;
4079  if (empty($this->marge_tx)) $this->marge_tx=0;
4080  if (empty($this->price)) $this->price=0; // TODO A virer
4081  if (empty($this->remise)) $this->remise=0; // TODO A virer
4082  if (empty($this->remise_percent)) $this->remise_percent=0;
4083  if (empty($this->info_bits)) $this->info_bits=0;
4084  if (empty($this->special_code)) $this->special_code=0;
4085  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4086  if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
4087  if (empty($this->subprice)) $this->subprice=0;
4088  if (empty($this->pa_ht)) $this->pa_ht=0;
4089 
4090  // if buy price not defined, define buyprice as configured in margin admin
4091  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4092  {
4093  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4094  {
4095  return $result;
4096  }
4097  else
4098  {
4099  $this->pa_ht = $result;
4100  }
4101  }
4102 
4103  $this->db->begin();
4104 
4105  // Mise a jour ligne en base
4106  $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4107  $sql.= " description='".$this->db->escape($this->desc)."'";
4108  $sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4109  $sql.= ", product_type=".$this->product_type;
4110  $sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->vat_src_code)."'";
4111  $sql.= ", tva_tx='".price2num($this->tva_tx)."'";
4112  $sql.= ", localtax1_tx=".price2num($this->localtax1_tx);
4113  $sql.= ", localtax2_tx=".price2num($this->localtax2_tx);
4114  $sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4115  $sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4116  $sql.= ", qty='".price2num($this->qty)."'";
4117  $sql.= ", subprice=".price2num($this->subprice)."";
4118  $sql.= ", remise_percent=".price2num($this->remise_percent)."";
4119  $sql.= ", price=".price2num($this->price).""; // TODO A virer
4120  $sql.= ", remise=".price2num($this->remise).""; // TODO A virer
4121  $sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4122  if (empty($this->skip_update_total))
4123  {
4124  $sql.= ", total_ht=".price2num($this->total_ht)."";
4125  $sql.= ", total_tva=".price2num($this->total_tva)."";
4126  $sql.= ", total_ttc=".price2num($this->total_ttc)."";
4127  $sql.= ", total_localtax1=".price2num($this->total_localtax1)."";
4128  $sql.= ", total_localtax2=".price2num($this->total_localtax2)."";
4129  }
4130  $sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4131  $sql.= ", buy_price_ht=".price2num($this->pa_ht);
4132  if (strlen($this->special_code)) $sql.= ", special_code=".$this->special_code;
4133  $sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4134  if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4135  $sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4136  $sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4137  $sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4138 
4139  // Multicurrency
4140  $sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4141  $sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4142  $sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4143  $sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4144 
4145  $sql.= " WHERE rowid = ".$this->rowid;
4146 
4147  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4148  $resql=$this->db->query($sql);
4149  if ($resql)
4150  {
4151  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4152  {
4153  $this->id=$this->rowid;
4154  $result=$this->insertExtraFields();
4155  if ($result < 0)
4156  {
4157  $error++;
4158  }
4159  }
4160 
4161  if (! $notrigger)
4162  {
4163  // Call trigger
4164  $result=$this->call_trigger('LINEPROPAL_UPDATE',$user);
4165  if ($result < 0)
4166  {
4167  $this->db->rollback();
4168  return -1;
4169  }
4170  // End call triggers
4171  }
4172 
4173  $this->db->commit();
4174  return 1;
4175  }
4176  else
4177  {
4178  $this->error=$this->db->error();
4179  $this->db->rollback();
4180  return -2;
4181  }
4182  }
4183 
4190  function update_total()
4191  {
4192  $this->db->begin();
4193 
4194  // Mise a jour ligne en base
4195  $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4196  $sql.= " total_ht=".price2num($this->total_ht,'MT')."";
4197  $sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
4198  $sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
4199  $sql.= " WHERE rowid = ".$this->rowid;
4200 
4201  dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4202 
4203  $resql=$this->db->query($sql);
4204  if ($resql)
4205  {
4206  $this->db->commit();
4207  return 1;
4208  }
4209  else
4210  {
4211  $this->error=$this->db->error();
4212  $this->db->rollback();
4213  return -2;
4214  }
4215  }
4216 
4217 }
4218 
deleteline($lineid)
Delete detail line.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1)
Return clicable link of object (with eventually picto)
getInvoiceArrayList()
Returns an array with the numbers of related invoices.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer reference number.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it's its name (generic function)
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.
fetch($rowid)
Retrieve the propal line object.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
update($notrigger=0)
Update propal line object into DB.
LibStatut($statut, $mode=1)
Return label of a status (draft, validated, ...)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
Delete all links between an object $this.
InvoiceArrayList($id)
Returns an array with id and ref of related invoices.
reopen($user, $statut, $note='', $notrigger=0)
Reopen the commercial proposal.
__construct($db, $socid="", $propalid=0)
Constructor.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
set_draft($user, $notrigger=0)
Set draft status.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
if(GETPOST('cancel','alpha')) if(!GETPOST('confirmmassaction','alpha')&&$massaction!= 'presend'&&$massaction!= 'confirm_presend')
Draft customers invoices.
Definition: list.php:147
Class to manage products or services.
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1283
Class to manage Dolibarr users.
Definition: user.class.php:39
Class to manage Dolibarr database access.
set_echeance($user, $date_fin_validite, $notrigger=0)
Define end validity date.
set_availability($user, $id, $notrigger=0)
Set delivery.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
createFromClone($socid=0)
Load an object from its id and create a new one in database.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
getLinesArray()
Retrieve an array of propal lines.
dol_concatdesc($text1, $text2, $forxml=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
const TYPE_PRODUCT
Regular product.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
insert($notrigger=0)
Insert object line propal in database.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
update(User $user, $notrigger=0)
Update database.
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.
valid($user, $notrigger=0)
Set status to validated.
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
add_product($idproduct, $qty, $remise_percent=0)
Add line into array products $this->thirdparty should be loaded.
GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NULL, $noreplace=0)
Return value of a param into GET or POST supervariable.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
Class to manage standard extra fields.
set_demand_reason($user, $id, $notrigger=0)
Set source of demand.
const STATUS_NOTSIGNED
Not signed quote.
$table_ref_field
{}
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.
classer_facturee()
Class invoiced the Propal.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
const STATUS_BILLED
Billed or processed quote.
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)...
const STATUS_SIGNED
Signed quote.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
set_remise_percent($user, $remise, $notrigger=0)
Set an overall discount on the proposal.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
demand_reason($demand_reason_id, $notrigger=0)
Change source demand.
cloture($user, $statut, $note, $notrigger=0)
Close the commercial proposal.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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
Class to manage commercial proposal lines.
getEntity($element, $shared=1, $forceentity=null)
Get list of entity id to use.
fetch($rowid, $ref='')
Load a proposal from database and its ligne array.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
Return list of proposal (eventually filtered on user) into an array.
deleteExtraFields()
Delete all extra fields values for the current object.
set_remise_absolue($user, $remise, $notrigger=0)
Set an absolute overall discount on the proposal.
Class to manage translations.
set_date($user, $date, $notrigger=0)
Define proposal date.
dol_now($mode='gmt')
Return date for now.
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.
const STATUS_DRAFT
Draft status.
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
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...
print
Draft customers invoices.
Definition: index.php:91
set_date_livraison($user, $date_livraison, $notrigger=0)
Set delivery date.
fetch_lines($only_product=0)
Load array lines.
__construct($db)
Class line Contructor.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $date_start='', $date_end='', $array_options=0, $fk_unit=null, $pu_ht_devise=0, $notrigger=0)
Update a proposal line.
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.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Return a prefix to use for this Dolibarr instance, for session/cookie names or email id...
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.
create_from($user)
Insert into DB a proposal object completely defined by its data members (ex, results from copy)...
const STATUS_VALIDATED
Validated status.
call_trigger($trigger_name, $user)
Call trigger based on this instance.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
initAsSpecimen()
Initialise an instance with random values.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
update_total()
Update DB line fields total_xxx Used by migration.
availability($availability_id, $notrigger=0)
Change the delivery time.
classifyBilled(User $user, $notrigger=0)
Class invoiced the Propal.
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.
info($id)
Object Proposal Information.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $date_start='', $date_end='', $array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0, $fk_remise_except=0)
Add a proposal line into database (linked to product/service or not) The parameters are already suppo...
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
Class to manage proposals.
static getIdAndTxFromCode(&$db, $code, $date_document='')
Get id and rate of currency from code.