dolibarr  7.0.0-beta
commande.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@capnetworks.com>
5  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6  * Copyright (C) 2010-2016 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2011 Jean Heimburger <jean@tiaris.info>
8  * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
9  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
10  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
11  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
12  * Copyright (C) 2016-2017 Ferran Marcet <fmarcet@2byte.es>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
33 include_once DOL_DOCUMENT_ROOT .'/core/class/commonorder.class.php';
34 require_once DOL_DOCUMENT_ROOT .'/core/class/commonobjectline.class.php';
35 require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
36 require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
37 require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
38 
42 class Commande extends CommonOrder
43 {
44  public $element='commande';
45  public $table_element='commande';
46  public $table_element_line = 'commandedet';
47  public $class_element_line = 'OrderLine';
48  public $fk_element = 'fk_commande';
49  public $picto = 'order';
54  public $ismultientitymanaged = 1;
59  public $restrictiononfksoc = 1;
60 
64  protected $table_ref_field = 'ref';
65 
70  public $socid;
71 
72  public $ref_client;
73  public $ref_int;
74  public $contactid;
75 
80  public $statut;
85  public $facturee;
86  public $billed; // billed or not
87 
88  public $brouillon;
89  public $cond_reglement_code;
90 
91  public $fk_account;
92 
97  public $mode_reglement;
98 
103  public $mode_reglement_id;
108  public $mode_reglement_code;
113  public $availability_id;
118  public $availability_code;
123  public $availability;
124 
125  public $demand_reason_id; // Source reason. Why we receive order (after a phone campaign, ...)
126  public $demand_reason_code;
127  public $date; // Date commande
133  public $date_livraison; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
134  public $fk_remise_except;
135  public $remise_percent;
136  public $remise_absolue;
137  public $info_bits;
138  public $rang;
139  public $special_code;
140  public $source; // Order mode. How we received order (by phone, by email, ...)
141  public $extraparams=array();
142 
143  public $linked_objects=array();
144 
145  public $user_author_id;
146  public $user_valid;
147 
151  public $lines = array();
152 
153  // Multicurrency
154  public $fk_multicurrency;
155  public $multicurrency_code;
156  public $multicurrency_tx;
157  public $multicurrency_total_ht;
158  public $multicurrency_total_tva;
159  public $multicurrency_total_ttc;
160 
161  public $oldcopy;
162 
167 
171  const STATUS_CANCELED = -1;
175  const STATUS_DRAFT = 0;
179  const STATUS_VALIDATED = 1;
184  const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
185 
189  const STATUS_CLOSED = 3;
190 
191 
197  function __construct($db)
198  {
199  $this->db = $db;
200 
201  $this->remise = 0;
202  $this->remise_percent = 0;
203 
204  $this->products = array();
205  }
206 
214  function getNextNumRef($soc)
215  {
216  global $langs, $conf;
217  $langs->load("order");
218 
219  if (! empty($conf->global->COMMANDE_ADDON))
220  {
221  $mybool=false;
222 
223  $file = $conf->global->COMMANDE_ADDON.".php";
224  $classname = $conf->global->COMMANDE_ADDON;
225 
226  // Include file with class
227  $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
228  foreach ($dirmodels as $reldir)
229  {
230  $dir = dol_buildpath($reldir."core/modules/commande/");
231 
232  // Load file with numbering class (if found)
233  $mybool|=@include_once $dir.$file;
234  }
235 
236  if (! $mybool)
237  {
238  dol_print_error('',"Failed to include file ".$file);
239  return '';
240  }
241 
242  $obj = new $classname();
243  $numref = $obj->getNextValue($soc,$this);
244 
245  if ($numref != "")
246  {
247  return $numref;
248  }
249  else
250  {
251  $this->error=$obj->error;
252  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
253  return "";
254  }
255  }
256  else
257  {
258  print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
259  return "";
260  }
261  }
262 
263 
272  function valid($user, $idwarehouse=0, $notrigger=0)
273  {
274  global $conf,$langs;
275  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
276 
277  $error=0;
278 
279  // Protection
280  if ($this->statut == self::STATUS_VALIDATED)
281  {
282  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
283  return 0;
284  }
285 
286  if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
287  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
288  {
289  $this->error='NotEnoughPermissions';
290  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
291  return -1;
292  }
293 
294  $now=dol_now();
295 
296  $this->db->begin();
297 
298  // Definition du nom de module de numerotation de commande
299  $soc = new Societe($this->db);
300  $soc->fetch($this->socid);
301 
302  // Class of company linked to order
303  $result=$soc->set_as_client();
304 
305  // Define new ref
306  if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
307  {
308  $num = $this->getNextNumRef($soc);
309  }
310  else
311  {
312  $num = $this->ref;
313  }
314  $this->newref = $num;
315 
316  // Validate
317  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
318  $sql.= " SET ref = '".$num."',";
319  $sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
320  $sql.= " date_valid='".$this->db->idate($now)."',";
321  $sql.= " fk_user_valid = ".$user->id;
322  $sql.= " WHERE rowid = ".$this->id;
323 
324  dol_syslog(get_class($this)."::valid()", LOG_DEBUG);
325  $resql=$this->db->query($sql);
326  if (! $resql)
327  {
328  dol_print_error($this->db);
329  $this->error=$this->db->lasterror();
330  $error++;
331  }
332 
333  if (! $error)
334  {
335  // If stock is incremented on validate order, we must increment it
336  if ($result >= 0 && ! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
337  {
338  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
339  $langs->load("agenda");
340 
341  // Loop on each line
342  $cpt=count($this->lines);
343  for ($i = 0; $i < $cpt; $i++)
344  {
345  if ($this->lines[$i]->fk_product > 0)
346  {
347  $mouvP = new MouvementStock($this->db);
348  $mouvP->origin = &$this;
349  // We decrement stock of product (and sub-products)
350  $result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr",$num));
351  if ($result < 0)
352  {
353  $error++;
354  $this->error=$mouvP->error;
355  }
356  }
357  if ($error) break;
358  }
359  }
360  }
361 
362  if (! $error && ! $notrigger)
363  {
364  // Call trigger
365  $result=$this->call_trigger('ORDER_VALIDATE',$user);
366  if ($result < 0) $error++;
367  // End call triggers
368  }
369 
370  if (! $error)
371  {
372  $this->oldref = $this->ref;
373 
374  // Rename directory if dir was a temporary ref
375  if (preg_match('/^[\(]?PROV/i', $this->ref))
376  {
377  // On renomme repertoire ($this->ref = ancienne ref, $num = nouvelle ref)
378  // in order not to lose the attachments
379  $oldref = dol_sanitizeFileName($this->ref);
380  $newref = dol_sanitizeFileName($num);
381  $dirsource = $conf->commande->dir_output.'/'.$oldref;
382  $dirdest = $conf->commande->dir_output.'/'.$newref;
383  if (file_exists($dirsource))
384  {
385  dol_syslog(get_class($this)."::valid() rename dir ".$dirsource." into ".$dirdest);
386 
387  if (@rename($dirsource, $dirdest))
388  {
389  dol_syslog("Rename ok");
390  // Rename docs starting with $oldref with $newref
391  $listoffiles=dol_dir_list($conf->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
392  foreach($listoffiles as $fileentry)
393  {
394  $dirsource=$fileentry['name'];
395  $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
396  $dirsource=$fileentry['path'].'/'.$dirsource;
397  $dirdest=$fileentry['path'].'/'.$dirdest;
398  @rename($dirsource, $dirdest);
399  }
400  }
401  }
402  }
403  }
404 
405  // Set new ref and current status
406  if (! $error)
407  {
408  $this->ref = $num;
409  $this->statut = self::STATUS_VALIDATED;
410  }
411 
412  if (! $error)
413  {
414  $this->db->commit();
415  return 1;
416  }
417  else
418  {
419  $this->db->rollback();
420  return -1;
421  }
422  }
423 
431  function set_draft($user, $idwarehouse=-1)
432  {
433  global $conf,$langs;
434 
435  $error=0;
436 
437  // Protection
438  if ($this->statut <= self::STATUS_DRAFT)
439  {
440  return 0;
441  }
442 
443  if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
444  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
445  {
446  $this->error='Permission denied';
447  return -1;
448  }
449 
450  $this->db->begin();
451 
452  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
453  $sql.= " SET fk_statut = ".self::STATUS_DRAFT;
454  $sql.= " WHERE rowid = ".$this->id;
455 
456  dol_syslog(get_class($this)."::set_draft", LOG_DEBUG);
457  if ($this->db->query($sql))
458  {
459  // If stock is decremented on validate order, we must reincrement it
460  if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
461  {
462  $result = 0;
463 
464  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
465  $langs->load("agenda");
466 
467  $num=count($this->lines);
468  for ($i = 0; $i < $num; $i++)
469  {
470  if ($this->lines[$i]->fk_product > 0)
471  {
472  $mouvP = new MouvementStock($this->db);
473  $mouvP->origin = &$this;
474  // We increment stock of product (and sub-products)
475  $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr",$this->ref));
476  if ($result < 0) { $error++; $this->error=$mouvP->error; break; }
477  }
478  }
479  }
480 
481  if (!$error) {
482  // Call trigger
483  $result=$this->call_trigger('ORDER_UNVALIDATE',$user);
484  if ($result < 0) $error++;
485  }
486 
487  if (!$error) {
488  $this->statut=self::STATUS_DRAFT;
489  $this->db->commit();
490  return 1;
491  }else {
492  $this->db->rollback();
493  return -1;
494  }
495  }
496  else
497  {
498  $this->error=$this->db->error();
499  $this->db->rollback();
500  return -1;
501  }
502  }
503 
504 
512  function set_reopen($user)
513  {
514  $error=0;
515 
516  if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED)
517  {
518  dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
519  return 0;
520  }
521 
522  $this->db->begin();
523 
524  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
525  $sql.= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0';
526  $sql.= ' WHERE rowid = '.$this->id;
527 
528  dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
529  $resql = $this->db->query($sql);
530  if ($resql)
531  {
532  // Call trigger
533  $result=$this->call_trigger('ORDER_REOPEN',$user);
534  if ($result < 0) $error++;
535  // End call triggers
536  }
537  else
538  {
539  $error++;
540  $this->error=$this->db->lasterror();
541  dol_print_error($this->db);
542  }
543 
544  if (! $error)
545  {
546  $this->statut = self::STATUS_VALIDATED;
547  $this->billed = 0;
548  $this->facturee = 0; // deprecated
549 
550  $this->db->commit();
551  return 1;
552  }
553  else
554  {
555  foreach($this->errors as $errmsg)
556  {
557  dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
558  $this->error.=($this->error?', '.$errmsg:$errmsg);
559  }
560  $this->db->rollback();
561  return -1*$error;
562  }
563  }
564 
572  function cloture($user, $notrigger=0)
573  {
574  global $conf;
575 
576  $error=0;
577 
578  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
579  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate)))
580  {
581  $this->db->begin();
582 
583  $now=dol_now();
584 
585  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
586  $sql.= ' SET fk_statut = '.self::STATUS_CLOSED.',';
587  $sql.= ' fk_user_cloture = '.$user->id.',';
588  $sql.= " date_cloture = '".$this->db->idate($now)."'";
589  $sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
590 
591  if ($this->db->query($sql))
592  {
593  if (! $notrigger)
594  {
595  // Call trigger
596  $result=$this->call_trigger('ORDER_CLOSE',$user);
597  if ($result < 0) $error++;
598  // End call triggers
599  }
600 
601  if (! $error)
602  {
603  $this->statut=self::STATUS_CLOSED;
604 
605  $this->db->commit();
606  return 1;
607  }
608  else
609  {
610  $this->db->rollback();
611  return -1;
612  }
613  }
614  else
615  {
616  $this->error=$this->db->lasterror();
617 
618  $this->db->rollback();
619  return -1;
620  }
621  }
622  return 0;
623  }
624 
632  function cancel($idwarehouse=-1)
633  {
634  global $conf,$user,$langs;
635 
636  $error=0;
637 
638  $this->db->begin();
639 
640  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
641  $sql.= " SET fk_statut = ".self::STATUS_CANCELED;
642  $sql.= " WHERE rowid = ".$this->id;
643  $sql.= " AND fk_statut = ".self::STATUS_VALIDATED;
644 
645  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
646  if ($this->db->query($sql))
647  {
648  // If stock is decremented on validate order, we must reincrement it
649  if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
650  {
651  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
652  $langs->load("agenda");
653 
654  $num=count($this->lines);
655  for ($i = 0; $i < $num; $i++)
656  {
657  if ($this->lines[$i]->fk_product > 0)
658  {
659  $mouvP = new MouvementStock($this->db);
660  // We increment stock of product (and sub-products)
661  $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderCanceledInDolibarr",$this->ref)); // price is 0, we don't want WAP to be changed
662  if ($result < 0)
663  {
664  $error++;
665  $this->error=$mouvP->error;
666  break;
667  }
668  }
669  }
670  }
671 
672  if (! $error)
673  {
674  // Call trigger
675  $result=$this->call_trigger('ORDER_CANCEL',$user);
676  if ($result < 0) $error++;
677  // End call triggers
678  }
679 
680  if (! $error)
681  {
682  $this->statut=self::STATUS_CANCELED;
683  $this->db->commit();
684  return 1;
685  }
686  else
687  {
688  foreach($this->errors as $errmsg)
689  {
690  dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
691  $this->error.=($this->error?', '.$errmsg:$errmsg);
692  }
693  $this->db->rollback();
694  return -1*$error;
695  }
696  }
697  else
698  {
699  $this->error=$this->db->error();
700  $this->db->rollback();
701  return -1;
702  }
703  }
704 
713  function create($user, $notrigger=0)
714  {
715  global $conf,$langs;
716  $error=0;
717 
718  // Clean parameters
719  $this->brouillon = 1; // set command as draft
720 
721  // $date_commande is deprecated
722  $date = ($this->date_commande ? $this->date_commande : $this->date);
723 
724  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
725  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
726  else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
727  if (empty($this->fk_multicurrency))
728  {
729  $this->multicurrency_code = $conf->currency;
730  $this->fk_multicurrency = 0;
731  $this->multicurrency_tx = 1;
732  }
733 
734  dol_syslog(get_class($this)."::create user=".$user->id);
735 
736  // Check parameters
737  if (! empty($this->ref)) // We check that ref is not already used
738  {
739  $result=self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
740  if ($result > 0)
741  {
742  $this->error='ErrorRefAlreadyExists';
743  dol_syslog(get_class($this)."::create ".$this->error,LOG_WARNING);
744  $this->db->rollback();
745  return -1;
746  }
747  }
748 
749  $soc = new Societe($this->db);
750  $result=$soc->fetch($this->socid);
751  if ($result < 0)
752  {
753  $this->error="Failed to fetch company";
754  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
755  return -2;
756  }
757  if (! empty($conf->global->COMMANDE_REQUIRE_SOURCE) && $this->source < 0)
758  {
759  $this->error=$langs->trans("ErrorFieldRequired",$langs->trans("Source"));
760  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
761  return -1;
762  }
763 
764  $now=dol_now();
765 
766  $this->db->begin();
767 
768  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
769  $sql.= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client, ref_int";
770  $sql.= ", model_pdf, fk_cond_reglement, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
771  $sql.= ", fk_shipping_method";
772  $sql.= ", fk_warehouse";
773  $sql.= ", remise_absolue, remise_percent";
774  $sql.= ", fk_incoterms, location_incoterms";
775  $sql.= ", entity";
776  $sql.= ", fk_multicurrency";
777  $sql.= ", multicurrency_code";
778  $sql.= ", multicurrency_tx";
779  $sql.= ")";
780  $sql.= " VALUES ('(PROV)', ".$this->socid.", '".$this->db->idate($now)."', ".$user->id;
781  $sql.= ", ".($this->fk_project>0?$this->fk_project:"null");
782  $sql.= ", '".$this->db->idate($date)."'";
783  $sql.= ", ".($this->source>=0 && $this->source != '' ?$this->db->escape($this->source):'null');
784  $sql.= ", '".$this->db->escape($this->note_private)."'";
785  $sql.= ", '".$this->db->escape($this->note_public)."'";
786  $sql.= ", ".($this->ref_ext?"'".$this->db->escape($this->ref_ext)."'":"null");
787  $sql.= ", ".($this->ref_client?"'".$this->db->escape($this->ref_client)."'":"null");
788  $sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
789  $sql.= ", '".$this->db->escape($this->modelpdf)."'";
790  $sql.= ", ".($this->cond_reglement_id>0?$this->cond_reglement_id:"null");
791  $sql.= ", ".($this->mode_reglement_id>0?$this->mode_reglement_id:"null");
792  $sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
793  $sql.= ", ".($this->availability_id>0?$this->availability_id:"null");
794  $sql.= ", ".($this->demand_reason_id>0?$this->demand_reason_id:"null");
795  $sql.= ", ".($this->date_livraison?"'".$this->db->idate($this->date_livraison)."'":"null");
796  $sql.= ", ".($this->fk_delivery_address>0?$this->fk_delivery_address:'NULL');
797  $sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
798  $sql.= ", ".($this->warehouse_id>0?$this->warehouse_id:'NULL');
799  $sql.= ", ".($this->remise_absolue>0?$this->db->escape($this->remise_absolue):'NULL');
800  $sql.= ", ".($this->remise_percent>0?$this->db->escape($this->remise_percent):0);
801  $sql.= ", ".(int) $this->fk_incoterms;
802  $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
803  $sql.= ", ".$conf->entity;
804  $sql.= ", ".(int) $this->fk_multicurrency;
805  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
806  $sql.= ", ".(double) $this->multicurrency_tx;
807  $sql.= ")";
808 
809  dol_syslog(get_class($this)."::create", LOG_DEBUG);
810  $resql=$this->db->query($sql);
811  if ($resql)
812  {
813  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
814 
815  if ($this->id)
816  {
817  $fk_parent_line=0;
818  $num=count($this->lines);
819 
820  /*
821  * Insert products details into db
822  */
823  for ($i=0;$i<$num;$i++)
824  {
825  $line = $this->lines[$i];
826 
827  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
828  //if (! is_object($line)) $line=json_decode(json_encode($line), FALSE); // convert recursively array into object.
829  if (! is_object($line)) $line = (object) $line;
830 
831  // Reset fk_parent_line for no child products and special product
832  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
833  $fk_parent_line = 0;
834  }
835 
836  // Complete vat rate with code
837  $vatrate = $line->tva_tx;
838  if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
839 
840  $result = $this->addline(
841  $line->desc,
842  $line->subprice,
843  $line->qty,
844  $vatrate,
845  $line->localtax1_tx,
846  $line->localtax2_tx,
847  $line->fk_product,
848  $line->remise_percent,
849  $line->info_bits,
850  $line->fk_remise_except,
851  'HT',
852  0,
853  $line->date_start,
854  $line->date_end,
855  $line->product_type,
856  $line->rang,
857  $line->special_code,
858  $fk_parent_line,
859  $line->fk_fournprice,
860  $line->pa_ht,
861  $line->label,
862  $line->array_options,
863  $line->fk_unit,
864  $this->element,
865  $line->id
866  );
867  if ($result < 0)
868  {
869  if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER)
870  {
871  $this->error=$this->db->lasterror();
872  dol_print_error($this->db);
873  }
874  $this->db->rollback();
875  return -1;
876  }
877  // Defined the new fk_parent_line
878  if ($result > 0 && $line->product_type == 9) {
879  $fk_parent_line = $result;
880  }
881  }
882 
883  // update ref
884  $initialref='(PROV'.$this->id.')';
885  if (! empty($this->ref)) $initialref=$this->ref;
886 
887  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".$this->id;
888  if ($this->db->query($sql))
889  {
890  if ($this->id)
891  {
892  $this->ref = $initialref;
893 
894  if (! empty($this->linkedObjectsIds) && empty($this->linked_objects)) // To use new linkedObjectsIds instead of old linked_objects
895  {
896  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
897  }
898 
899  // Add object linked
900  if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
901  {
902  foreach($this->linked_objects as $origin => $tmp_origin_id)
903  {
904  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, ...))
905  {
906  foreach($tmp_origin_id as $origin_id)
907  {
908  $ret = $this->add_object_linked($origin, $origin_id);
909  if (! $ret)
910  {
911  $this->error=$this->db->lasterror();
912  $error++;
913  }
914  }
915  }
916  else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
917  {
918  $origin_id = $tmp_origin_id;
919  $ret = $this->add_object_linked($origin, $origin_id);
920  if (! $ret)
921  {
922  $this->error=$this->db->lasterror();
923  $error++;
924  }
925  }
926  }
927  }
928 
929  if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id)) // Get contact from origin object
930  {
931  $originforcontact = $this->origin;
932  $originidforcontact = $this->origin_id;
933  if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
934  {
935  require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
936  $exp = new Expedition($this->db);
937  $exp->fetch($this->origin_id);
938  $exp->fetchObjectLinked();
939  if (count($exp->linkedObjectsIds['commande']) > 0)
940  {
941  foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
942  {
943  $originforcontact = 'commande';
944  if (is_object($value)) $originidforcontact = $value->id;
945  else $originidforcontact = $value;
946  break; // We take first one
947  }
948  }
949  }
950 
951  $sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
952  $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
953 
954  $resqlcontact = $this->db->query($sqlcontact);
955  if ($resqlcontact)
956  {
957  while($objcontact = $this->db->fetch_object($resqlcontact))
958  {
959  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
960  $this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source); // May failed because of duplicate key or because code of contact type does not exists for new object
961  }
962  }
963  else dol_print_error($resqlcontact);
964  }
965  }
966 
967  if (! $error)
968  {
969  $result=$this->insertExtraFields();
970  if ($result < 0) $error++;
971  }
972 
973  if (! $error && ! $notrigger)
974  {
975  // Call trigger
976  $result=$this->call_trigger('ORDER_CREATE',$user);
977  if ($result < 0) $error++;
978  // End call triggers
979  }
980 
981  if (! $error)
982  {
983  $this->db->commit();
984  return $this->id;
985  }
986  else
987  {
988  $this->db->rollback();
989  return -1*$error;
990  }
991  }
992  else
993  {
994  $this->error=$this->db->lasterror();
995  $this->db->rollback();
996  return -1;
997  }
998  }
999  }
1000  else
1001  {
1002  dol_print_error($this->db);
1003  $this->db->rollback();
1004  return -1;
1005  }
1006  }
1007 
1008 
1015  function createFromClone($socid=0)
1016  {
1017  global $user,$hookmanager;
1018 
1019  $error=0;
1020 
1021  $this->context['createfromclone'] = 'createfromclone';
1022 
1023  $this->db->begin();
1024 
1025  // get lines so they will be clone
1026  foreach($this->lines as $line)
1027  $line->fetch_optionals($line->rowid);
1028 
1029  // Load source object
1030  $objFrom = clone $this;
1031 
1032  // Change socid if needed
1033  if (! empty($socid) && $socid != $this->socid)
1034  {
1035  $objsoc = new Societe($this->db);
1036 
1037  if ($objsoc->fetch($socid)>0)
1038  {
1039  $this->socid = $objsoc->id;
1040  $this->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1041  $this->mode_reglement_id = (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1042  $this->fk_project = 0;
1043  $this->fk_delivery_address = 0;
1044  }
1045 
1046  // TODO Change product price if multi-prices
1047  }
1048 
1049  $this->id=0;
1050  $this->ref = '';
1051  $this->statut=self::STATUS_DRAFT;
1052 
1053  // Clear fields
1054  $this->user_author_id = $user->id;
1055  $this->user_valid = '';
1056  $this->date = dol_now();
1057  $this->date_commande = dol_now();
1058  $this->date_creation = '';
1059  $this->date_validation = '';
1060  $this->ref_client = '';
1061 
1062  // Create clone
1063  $result=$this->create($user);
1064  if ($result < 0) $error++;
1065 
1066  if (! $error)
1067  {
1068  // Hook of thirdparty module
1069  if (is_object($hookmanager))
1070  {
1071  $parameters=array('objFrom'=>$objFrom);
1072  $action='';
1073  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1074  if ($reshook < 0) $error++;
1075  }
1076 
1077  // Call trigger
1078  $result=$this->call_trigger('ORDER_CLONE',$user);
1079  if ($result < 0) $error++;
1080  // End call triggers
1081  }
1082 
1083  unset($this->context['createfromclone']);
1084 
1085  // End
1086  if (! $error)
1087  {
1088  $this->db->commit();
1089  return $this->id;
1090  }
1091  else
1092  {
1093  $this->db->rollback();
1094  return -1;
1095  }
1096  }
1097 
1098 
1106  function createFromProposal($object, User $user)
1107  {
1108  global $conf, $hookmanager;
1109 
1110  dol_include_once('/core/class/extrafields.class.php');
1111 
1112  $error=0;
1113 
1114 
1115  $this->date_commande = dol_now();
1116  $this->source = 0;
1117 
1118  $num=count($object->lines);
1119  for ($i = 0; $i < $num; $i++)
1120  {
1121  $line = new OrderLine($this->db);
1122 
1123  $line->libelle = $object->lines[$i]->libelle;
1124  $line->label = $object->lines[$i]->label;
1125  $line->desc = $object->lines[$i]->desc;
1126  $line->price = $object->lines[$i]->price;
1127  $line->subprice = $object->lines[$i]->subprice;
1128  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1129  $line->tva_tx = $object->lines[$i]->tva_tx;
1130  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1131  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1132  $line->qty = $object->lines[$i]->qty;
1133  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1134  $line->remise_percent = $object->lines[$i]->remise_percent;
1135  $line->fk_product = $object->lines[$i]->fk_product;
1136  $line->info_bits = $object->lines[$i]->info_bits;
1137  $line->product_type = $object->lines[$i]->product_type;
1138  $line->rang = $object->lines[$i]->rang;
1139  $line->special_code = $object->lines[$i]->special_code;
1140  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1141  $line->fk_unit = $object->lines[$i]->fk_unit;
1142 
1143  $line->date_start = $object->lines[$i]->date_start;
1144  $line->date_end = $object->lines[$i]->date_end;
1145 
1146  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1147  $marginInfos = getMarginInfos($object->lines[$i]->subprice, $object->lines[$i]->remise_percent, $object->lines[$i]->tva_tx, $object->lines[$i]->localtax1_tx, $object->lines[$i]->localtax2_tx, $object->lines[$i]->fk_fournprice, $object->lines[$i]->pa_ht);
1148  $line->pa_ht = $marginInfos[0];
1149  $line->marge_tx = $marginInfos[1];
1150  $line->marque_tx = $marginInfos[2];
1151 
1152  // get extrafields from original line
1153  $object->lines[$i]->fetch_optionals($object->lines[$i]->rowid);
1154  foreach($object->lines[$i]->array_options as $options_key => $value)
1155  $line->array_options[$options_key] = $value;
1156 
1157  $this->lines[$i] = $line;
1158  }
1159 
1160  $this->socid = $object->socid;
1161  $this->fk_project = $object->fk_project;
1162  $this->cond_reglement_id = $object->cond_reglement_id;
1163  $this->mode_reglement_id = $object->mode_reglement_id;
1164  $this->fk_account = $object->fk_account;
1165  $this->availability_id = $object->availability_id;
1166  $this->demand_reason_id = $object->demand_reason_id;
1167  $this->date_livraison = $object->date_livraison;
1168  $this->shipping_method_id = $object->shipping_method_id;
1169  $this->warehouse_id = $object->warehouse_id;
1170  $this->fk_delivery_address = $object->fk_delivery_address;
1171  $this->contact_id = $object->contactid;
1172  $this->ref_client = $object->ref_client;
1173  $this->note_private = $object->note_private;
1174  $this->note_public = $object->note_public;
1175 
1176  $this->origin = $object->element;
1177  $this->origin_id = $object->id;
1178 
1179  // get extrafields from original line
1180  $object->fetch_optionals($object->id);
1181 
1182  $e = new ExtraFields($this->db);
1183  $element_extrafields = $e->fetch_name_optionals_label($this->element);
1184 
1185  foreach($object->array_options as $options_key => $value) {
1186  if(array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)){
1187  $this->array_options[$options_key] = $value;
1188  }
1189  }
1190  // Possibility to add external linked objects with hooks
1191  $this->linked_objects[$this->origin] = $this->origin_id;
1192  if (is_array($object->other_linked_objects) && ! empty($object->other_linked_objects))
1193  {
1194  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1195  }
1196 
1197  $ret = $this->create($user);
1198 
1199  if ($ret > 0)
1200  {
1201  // Actions hooked (by external module)
1202  $hookmanager->initHooks(array('orderdao'));
1203 
1204  $parameters=array('objFrom'=>$object);
1205  $action='';
1206  $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1207  if ($reshook < 0) $error++;
1208 
1209  if (! $error)
1210  {
1211  // Ne pas passer par la commande provisoire
1212  if ($conf->global->COMMANDE_VALID_AFTER_CLOSE_PROPAL == 1)
1213  {
1214  $this->fetch($ret);
1215  $this->valid($user);
1216  }
1217  return $ret;
1218  }
1219  else return -1;
1220  }
1221  else return -1;
1222  }
1223 
1224 
1263  function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $info_bits=0, $fk_remise_except=0, $price_base_type='HT', $pu_ttc=0, $date_start='', $date_end='', $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='',$array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise = 0)
1264  {
1265  global $mysoc, $conf, $langs, $user;
1266 
1267  dol_syslog(get_class($this)."::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent, info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, date_start=$date_start, date_end=$date_end, type=$type special_code=$special_code, fk_unit=$fk_unit, origin=$origin, origin_id=$origin_id, pu_ht_devise=$pu_ht_devise", LOG_DEBUG);
1268 
1269  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1270 
1271  // Clean parameters
1272  if (empty($remise_percent)) $remise_percent=0;
1273  if (empty($qty)) $qty=0;
1274  if (empty($info_bits)) $info_bits=0;
1275  if (empty($rang)) $rang=0;
1276  if (empty($txtva)) $txtva=0;
1277  if (empty($txlocaltax1)) $txlocaltax1=0;
1278  if (empty($txlocaltax2)) $txlocaltax2=0;
1279  if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
1280  if (empty($this->fk_multicurrency)) $this->fk_multicurrency=0;
1281 
1282  $remise_percent=price2num($remise_percent);
1283  $qty=price2num($qty);
1284  $pu_ht=price2num($pu_ht);
1285  $pu_ttc=price2num($pu_ttc);
1286  $pa_ht=price2num($pa_ht);
1287  $txtva = price2num($txtva);
1288  $txlocaltax1 = price2num($txlocaltax1);
1289  $txlocaltax2 = price2num($txlocaltax2);
1290  if ($price_base_type=='HT')
1291  {
1292  $pu=$pu_ht;
1293  }
1294  else
1295  {
1296  $pu=$pu_ttc;
1297  }
1298  $label=trim($label);
1299  $desc=trim($desc);
1300 
1301  // Check parameters
1302  if ($type < 0) return -1;
1303 
1304  if ($this->statut == self::STATUS_DRAFT)
1305  {
1306  $this->db->begin();
1307 
1308  $product_type=$type;
1309  if (!empty($fk_product))
1310  {
1311  $product=new Product($this->db);
1312  $result=$product->fetch($fk_product);
1313  $product_type=$product->type;
1314 
1315  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
1316  {
1317  $langs->load("errors");
1318  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1319  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1320  $this->db->rollback();
1321  return self::STOCK_NOT_ENOUGH_FOR_ORDER;
1322  }
1323  }
1324  // Calcul du total TTC et de la TVA pour la ligne a partir de
1325  // qty, pu, remise_percent et txtva
1326  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1327  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1328 
1329  $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
1330 
1331  // Clean vat code
1332  $vat_src_code='';
1333  if (preg_match('/\((.*)\)/', $txtva, $reg))
1334  {
1335  $vat_src_code = $reg[1];
1336  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1337  }
1338 
1339  $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);
1340 
1341  /*var_dump($txlocaltax1);
1342  var_dump($txlocaltax2);
1343  var_dump($localtaxes_type);
1344  var_dump($tabprice);
1345  var_dump($tabprice[9]);
1346  var_dump($tabprice[10]);
1347  exit;*/
1348 
1349  $total_ht = $tabprice[0];
1350  $total_tva = $tabprice[1];
1351  $total_ttc = $tabprice[2];
1352  $total_localtax1 = $tabprice[9];
1353  $total_localtax2 = $tabprice[10];
1354  $pu_ht = $tabprice[3];
1355 
1356  // MultiCurrency
1357  $multicurrency_total_ht = $tabprice[16];
1358  $multicurrency_total_tva = $tabprice[17];
1359  $multicurrency_total_ttc = $tabprice[18];
1360  $pu_ht_devise = $tabprice[19];
1361 
1362  // Rang to use
1363  $rangtouse = $rang;
1364  if ($rangtouse == -1)
1365  {
1366  $rangmax = $this->line_max($fk_parent_line);
1367  $rangtouse = $rangmax + 1;
1368  }
1369 
1370  // TODO A virer
1371  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1372  $price = $pu;
1373  $remise = 0;
1374  if ($remise_percent > 0)
1375  {
1376  $remise = round(($pu * $remise_percent / 100), 2);
1377  $price = $pu - $remise;
1378  }
1379 
1380  // Insert line
1381  $this->line=new OrderLine($this->db);
1382 
1383  $this->line->context = $this->context;
1384 
1385  $this->line->fk_commande=$this->id;
1386  $this->line->label=$label;
1387  $this->line->desc=$desc;
1388  $this->line->qty=$qty;
1389 
1390  $this->line->vat_src_code=$vat_src_code;
1391  $this->line->tva_tx=$txtva;
1392  $this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
1393  $this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
1394  $this->line->localtax1_type=$localtaxes_type[0];
1395  $this->line->localtax2_type=$localtaxes_type[2];
1396  $this->line->fk_product=$fk_product;
1397  $this->line->product_type=$product_type;
1398  $this->line->fk_remise_except=$fk_remise_except;
1399  $this->line->remise_percent=$remise_percent;
1400  $this->line->subprice=$pu_ht;
1401  $this->line->rang=$rangtouse;
1402  $this->line->info_bits=$info_bits;
1403  $this->line->total_ht=$total_ht;
1404  $this->line->total_tva=$total_tva;
1405  $this->line->total_localtax1=$total_localtax1;
1406  $this->line->total_localtax2=$total_localtax2;
1407  $this->line->total_ttc=$total_ttc;
1408  $this->line->product_type=$type;
1409  $this->line->special_code=$special_code;
1410  $this->line->origin=$origin;
1411  $this->line->origin_id=$origin_id;
1412  $this->line->fk_parent_line=$fk_parent_line;
1413  $this->line->fk_unit=$fk_unit;
1414 
1415  $this->line->date_start=$date_start;
1416  $this->line->date_end=$date_end;
1417 
1418  $this->line->fk_fournprice = $fk_fournprice;
1419  $this->line->pa_ht = $pa_ht;
1420 
1421  // Multicurrency
1422  $this->line->fk_multicurrency = $this->fk_multicurrency;
1423  $this->line->multicurrency_code = $this->multicurrency_code;
1424  $this->line->multicurrency_subprice = $pu_ht_devise;
1425  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1426  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1427  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1428 
1429  // TODO Ne plus utiliser
1430  $this->line->price=$price;
1431  $this->line->remise=$remise;
1432 
1433  if (is_array($array_options) && count($array_options)>0) {
1434  $this->line->array_options=$array_options;
1435  }
1436 
1437  $result=$this->line->insert($user);
1438  if ($result > 0)
1439  {
1440  // Reorder if child line
1441  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
1442 
1443  // Mise a jour informations denormalisees au niveau de la commande meme
1444  $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.
1445  if ($result > 0)
1446  {
1447  $this->db->commit();
1448  return $this->line->rowid;
1449  }
1450  else
1451  {
1452  $this->db->rollback();
1453  return -1;
1454  }
1455  }
1456  else
1457  {
1458  $this->error=$this->line->error;
1459  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1460  $this->db->rollback();
1461  return -2;
1462  }
1463  }
1464  else
1465  {
1466  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1467  return -3;
1468  }
1469  }
1470 
1471 
1486  function add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
1487  {
1488  global $conf, $mysoc;
1489 
1490  if (! $qty) $qty = 1;
1491 
1492  if ($idproduct > 0)
1493  {
1494  $prod=new Product($this->db);
1495  $prod->fetch($idproduct);
1496 
1497  $tva_tx = get_default_tva($mysoc,$this->thirdparty,$prod->id);
1498  $tva_npr = get_default_npr($mysoc,$this->thirdparty,$prod->id);
1499  if (empty($tva_tx)) $tva_npr=0;
1500  $vat_src_code = ''; // May be defined into tva_tx
1501 
1502  $localtax1_tx=get_localtax($tva_tx,1,$this->thirdparty,$mysoc,$tva_npr);
1503  $localtax2_tx=get_localtax($tva_tx,2,$this->thirdparty,$mysoc,$tva_npr);
1504 
1505  // multiprix
1506  if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level)
1507  $price = $prod->multiprices[$this->thirdparty->price_level];
1508  else
1509  $price = $prod->price;
1510 
1511  $line=new OrderLine($this->db);
1512 
1513  $line->context = $this->context;
1514 
1515  $line->fk_product=$idproduct;
1516  $line->desc=$prod->description;
1517  $line->qty=$qty;
1518  $line->subprice=$price;
1519  $line->remise_percent=$remise_percent;
1520  $line->vat_src_code=$vat_src_code;
1521  $line->tva_tx=$tva_tx;
1522  $line->localtax1_tx=$localtax1_tx;
1523  $line->localtax2_tx=$localtax2_tx;
1524  $line->ref=$prod->ref;
1525  $line->libelle=$prod->label;
1526  $line->product_desc=$prod->description;
1527  $line->fk_unit=$prod->fk_unit;
1528 
1529  // Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1530  // Save the start and end date of the line in the object
1531  if ($date_start) { $line->date_start = $date_start; }
1532  if ($date_end) { $line->date_end = $date_end; }
1533 
1534  $this->lines[] = $line;
1535 
1556  }
1557  }
1558 
1559 
1569  function fetch($id, $ref='', $ref_ext='', $ref_int='')
1570  {
1571 
1572  // Check parameters
1573  if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
1574 
1575  $sql = 'SELECT c.rowid, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_statut';
1576  $sql.= ', c.amount_ht, c.total_ht, c.total_ttc, c.tva as total_tva, c.localtax1 as total_localtax1, c.localtax2 as total_localtax2, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_availability, c.fk_input_reason';
1577  $sql.= ', c.fk_account';
1578  $sql.= ', c.date_commande';
1579  $sql.= ', c.date_livraison';
1580  $sql.= ', c.fk_shipping_method';
1581  $sql.= ', c.fk_warehouse';
1582  $sql.= ', c.fk_projet, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1583  $sql.= ', c.note_private, c.note_public, c.ref_client, c.ref_ext, c.ref_int, c.model_pdf, c.last_main_doc, c.fk_delivery_address, c.extraparams';
1584  $sql.= ', c.fk_incoterms, c.location_incoterms';
1585  $sql.= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1586  $sql.= ", i.libelle as libelle_incoterms";
1587  $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1588  $sql.= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1589  $sql.= ', ca.code as availability_code, ca.label as availability_label';
1590  $sql.= ', dr.code as demand_reason_code';
1591  $sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1592  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')';
1593  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')';
1594  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1595  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = ca.rowid';
1596  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1597  $sql.= " WHERE c.entity IN (".getEntity('commande').")";
1598  if ($id) $sql.= " AND c.rowid=".$id;
1599  if ($ref) $sql.= " AND c.ref='".$this->db->escape($ref)."'";
1600  if ($ref_ext) $sql.= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1601  if ($ref_int) $sql.= " AND c.ref_int='".$this->db->escape($ref_int)."'";
1602 
1603  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1604  $result = $this->db->query($sql);
1605  if ($result)
1606  {
1607  $obj = $this->db->fetch_object($result);
1608  if ($obj)
1609  {
1610  $this->id = $obj->rowid;
1611  $this->ref = $obj->ref;
1612  $this->ref_client = $obj->ref_client;
1613  $this->ref_customer = $obj->ref_client;
1614  $this->ref_ext = $obj->ref_ext;
1615  $this->ref_int = $obj->ref_int;
1616  $this->socid = $obj->fk_soc;
1617  $this->statut = $obj->fk_statut;
1618  $this->user_author_id = $obj->fk_user_author;
1619  $this->user_valid = $obj->fk_user_valid;
1620  $this->total_ht = $obj->total_ht;
1621  $this->total_tva = $obj->total_tva;
1622  $this->total_localtax1 = $obj->total_localtax1;
1623  $this->total_localtax2 = $obj->total_localtax2;
1624  $this->total_ttc = $obj->total_ttc;
1625  $this->date = $this->db->jdate($obj->date_commande);
1626  $this->date_commande = $this->db->jdate($obj->date_commande);
1627  $this->remise = $obj->remise;
1628  $this->remise_percent = $obj->remise_percent;
1629  $this->remise_absolue = $obj->remise_absolue;
1630  $this->source = $obj->source;
1631  $this->facturee = $obj->billed; // deprecated
1632  $this->billed = $obj->billed;
1633  $this->note = $obj->note_private; // deprecated
1634  $this->note_private = $obj->note_private;
1635  $this->note_public = $obj->note_public;
1636  $this->fk_project = $obj->fk_projet;
1637  $this->modelpdf = $obj->model_pdf;
1638  $this->last_main_doc = $obj->last_main_doc;
1639  $this->mode_reglement_id = $obj->fk_mode_reglement;
1640  $this->mode_reglement_code = $obj->mode_reglement_code;
1641  $this->mode_reglement = $obj->mode_reglement_libelle;
1642  $this->cond_reglement_id = $obj->fk_cond_reglement;
1643  $this->cond_reglement_code = $obj->cond_reglement_code;
1644  $this->cond_reglement = $obj->cond_reglement_libelle;
1645  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1646  $this->fk_account = $obj->fk_account;
1647  $this->availability_id = $obj->fk_availability;
1648  $this->availability_code = $obj->availability_code;
1649  $this->availability = $obj->availability_label;
1650  $this->demand_reason_id = $obj->fk_input_reason;
1651  $this->demand_reason_code = $obj->demand_reason_code;
1652  $this->date_livraison = $this->db->jdate($obj->date_livraison);
1653  $this->shipping_method_id = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1654  $this->warehouse_id = ($obj->fk_warehouse>0)?$obj->fk_warehouse:null;
1655  $this->fk_delivery_address = $obj->fk_delivery_address;
1656 
1657  //Incoterms
1658  $this->fk_incoterms = $obj->fk_incoterms;
1659  $this->location_incoterms = $obj->location_incoterms;
1660  $this->libelle_incoterms = $obj->libelle_incoterms;
1661 
1662  // Multicurrency
1663  $this->fk_multicurrency = $obj->fk_multicurrency;
1664  $this->multicurrency_code = $obj->multicurrency_code;
1665  $this->multicurrency_tx = $obj->multicurrency_tx;
1666  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1667  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1668  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1669 
1670  $this->extraparams = (array) json_decode($obj->extraparams, true);
1671 
1672  $this->lines = array();
1673 
1674  if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1675 
1676  // Retrieve all extrafields for invoice
1677  // fetch optionals attributes and labels
1678  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1679  $extrafields=new ExtraFields($this->db);
1680  $extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
1681  $this->fetch_optionals($this->id,$extralabels);
1682 
1683  $this->db->free($result);
1684 
1685  /*
1686  * Lines
1687  */
1688  $result=$this->fetch_lines();
1689  if ($result < 0)
1690  {
1691  return -3;
1692  }
1693  return 1;
1694  }
1695  else
1696  {
1697  $this->error='Order with id '.$id.' not found sql='.$sql;
1698  return 0;
1699  }
1700  }
1701  else
1702  {
1703  $this->error=$this->db->error();
1704  return -1;
1705  }
1706  }
1707 
1708 
1715  function insert_discount($idremise)
1716  {
1717  global $langs;
1718 
1719  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1720  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1721 
1722  $this->db->begin();
1723 
1724  $remise=new DiscountAbsolute($this->db);
1725  $result=$remise->fetch($idremise);
1726 
1727  if ($result > 0)
1728  {
1729  if ($remise->fk_facture) // Protection against multiple submission
1730  {
1731  $this->error=$langs->trans("ErrorDiscountAlreadyUsed");
1732  $this->db->rollback();
1733  return -5;
1734  }
1735 
1736  $line = new OrderLine($this->db);
1737 
1738  $line->fk_commande=$this->id;
1739  $line->fk_remise_except=$remise->id;
1740  $line->desc=$remise->description; // Description ligne
1741  $line->vat_src_code=$remise->vat_src_code;
1742  $line->tva_tx=$remise->tva_tx;
1743  $line->subprice=-$remise->amount_ht;
1744  $line->price=-$remise->amount_ht;
1745  $line->fk_product=0; // Id produit predefini
1746  $line->qty=1;
1747  $line->remise=0;
1748  $line->remise_percent=0;
1749  $line->rang=-1;
1750  $line->info_bits=2;
1751 
1752  $line->total_ht = -$remise->amount_ht;
1753  $line->total_tva = -$remise->amount_tva;
1754  $line->total_ttc = -$remise->amount_ttc;
1755 
1756  $result=$line->insert();
1757  if ($result > 0)
1758  {
1759  $result=$this->update_price(1);
1760  if ($result > 0)
1761  {
1762  $this->db->commit();
1763  return 1;
1764  }
1765  else
1766  {
1767  $this->db->rollback();
1768  return -1;
1769  }
1770  }
1771  else
1772  {
1773  $this->error=$line->error;
1774  $this->db->rollback();
1775  return -2;
1776  }
1777  }
1778  else
1779  {
1780  $this->db->rollback();
1781  return -2;
1782  }
1783  }
1784 
1785 
1792  function fetch_lines($only_product=0)
1793  {
1794  $this->lines=array();
1795 
1796  $sql = 'SELECT l.rowid, l.fk_product, l.fk_parent_line, l.product_type, l.fk_commande, l.label as custom_label, l.description, l.price, l.qty, l.vat_src_code, l.tva_tx,';
1797  $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.fk_remise_except, l.remise_percent, l.subprice, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht, l.rang, l.info_bits, l.special_code,';
1798  $sql.= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
1799  $sql.= ' l.fk_unit,';
1800  $sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1801  $sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch,';
1802  $sql.= ' p.weight, p.weight_units, p.volume, p.volume_units';
1803  $sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
1804  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
1805  $sql.= ' WHERE l.fk_commande = '.$this->id;
1806  if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1807  $sql .= ' ORDER BY l.rang, l.rowid';
1808 
1809  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1810  $result = $this->db->query($sql);
1811  if ($result)
1812  {
1813  $num = $this->db->num_rows($result);
1814 
1815  $i = 0;
1816  while ($i < $num)
1817  {
1818  $objp = $this->db->fetch_object($result);
1819 
1820  $line = new OrderLine($this->db);
1821 
1822  $line->rowid = $objp->rowid;
1823  $line->id = $objp->rowid;
1824  $line->fk_commande = $objp->fk_commande;
1825  $line->commande_id = $objp->fk_commande;
1826  $line->label = $objp->custom_label;
1827  $line->desc = $objp->description;
1828  $line->description = $objp->description; // Description line
1829  $line->product_type = $objp->product_type;
1830  $line->qty = $objp->qty;
1831 
1832  $line->vat_src_code = $objp->vat_src_code;
1833  $line->tva_tx = $objp->tva_tx;
1834  $line->localtax1_tx = $objp->localtax1_tx;
1835  $line->localtax2_tx = $objp->localtax2_tx;
1836  $line->localtax1_type = $objp->localtax1_type;
1837  $line->localtax2_type = $objp->localtax2_type;
1838  $line->total_ht = $objp->total_ht;
1839  $line->total_ttc = $objp->total_ttc;
1840  $line->total_tva = $objp->total_tva;
1841  $line->total_localtax1 = $objp->total_localtax1;
1842  $line->total_localtax2 = $objp->total_localtax2;
1843  $line->subprice = $objp->subprice;
1844  $line->fk_remise_except = $objp->fk_remise_except;
1845  $line->remise_percent = $objp->remise_percent;
1846  $line->price = $objp->price;
1847  $line->fk_product = $objp->fk_product;
1848  $line->fk_fournprice = $objp->fk_fournprice;
1849  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1850  $line->pa_ht = $marginInfos[0];
1851  $line->marge_tx = $marginInfos[1];
1852  $line->marque_tx = $marginInfos[2];
1853  $line->rang = $objp->rang;
1854  $line->info_bits = $objp->info_bits;
1855  $line->special_code = $objp->special_code;
1856  $line->fk_parent_line = $objp->fk_parent_line;
1857 
1858  $line->ref = $objp->product_ref;
1859  $line->product_ref = $objp->product_ref;
1860  $line->libelle = $objp->product_label;
1861  $line->product_label = $objp->product_label;
1862  $line->product_desc = $objp->product_desc;
1863  $line->product_tobatch = $objp->product_tobatch;
1864  $line->fk_product_type = $objp->fk_product_type; // Produit ou service
1865  $line->fk_unit = $objp->fk_unit;
1866 
1867  $line->weight = $objp->weight;
1868  $line->weight_units = $objp->weight_units;
1869  $line->volume = $objp->volume;
1870  $line->volume_units = $objp->volume_units;
1871 
1872  $line->date_start = $this->db->jdate($objp->date_start);
1873  $line->date_end = $this->db->jdate($objp->date_end);
1874 
1875  // Multicurrency
1876  $line->fk_multicurrency = $objp->fk_multicurrency;
1877  $line->multicurrency_code = $objp->multicurrency_code;
1878  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1879  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1880  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1881  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1882 
1883  $this->lines[$i] = $line;
1884 
1885  $i++;
1886  }
1887 
1888  $this->db->free($result);
1889 
1890  return 1;
1891  }
1892  else
1893  {
1894  $this->error=$this->db->error();
1895  return -3;
1896  }
1897  }
1898 
1899 
1906  {
1907  $nb=0;
1908  foreach($this->lines as $line)
1909  {
1910  if ($line->product_type == 0) $nb++;
1911  }
1912  return $nb;
1913  }
1914 
1921  {
1922  $nb=0;
1923  foreach($this->lines as $line)
1924  {
1925  if ($line->product_type == 1) $nb++;
1926  }
1927  return $nb;
1928  }
1929 
1935  function getNbOfShipments()
1936  {
1937  $nb = 0;
1938 
1939  $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
1940  $sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
1941  $sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
1942  $sql.= ' WHERE';
1943  $sql.= ' ed.fk_origin_line = cd.rowid';
1944  $sql.= ' AND cd.fk_commande =' .$this->id;
1945  //print $sql;
1946 
1947  dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
1948  $resql = $this->db->query($sql);
1949  if ($resql)
1950  {
1951  $obj = $this->db->fetch_object($resql);
1952  if ($obj) $nb = $obj->nb;
1953 
1954  $this->db->free($resql);
1955  return $nb;
1956  }
1957  else
1958  {
1959  $this->error=$this->db->lasterror();
1960  return -1;
1961  }
1962  }
1963 
1971  function loadExpeditions($filtre_statut=-1)
1972  {
1973  $this->expeditions = array();
1974 
1975  $sql = 'SELECT cd.rowid, cd.fk_product,';
1976  $sql.= ' sum(ed.qty) as qty';
1977  $sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
1978  if ($filtre_statut >= 0) $sql.= ' '.MAIN_DB_PREFIX.'expedition as e,';
1979  $sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
1980  $sql.= ' WHERE';
1981  if ($filtre_statut >= 0) $sql.= ' ed.fk_expedition = e.rowid AND';
1982  $sql.= ' ed.fk_origin_line = cd.rowid';
1983  $sql.= ' AND cd.fk_commande =' .$this->id;
1984  if ($this->fk_product > 0) $sql.= ' AND cd.fk_product = '.$this->fk_product;
1985  if ($filtre_statut >= 0) $sql.=' AND e.fk_statut >= '.$filtre_statut;
1986  $sql.= ' GROUP BY cd.rowid, cd.fk_product';
1987  //print $sql;
1988 
1989  dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
1990  $resql = $this->db->query($sql);
1991  if ($resql)
1992  {
1993  $num = $this->db->num_rows($resql);
1994  $i = 0;
1995  while ($i < $num)
1996  {
1997  $obj = $this->db->fetch_object($resql);
1998  $this->expeditions[$obj->rowid] = $obj->qty;
1999  $i++;
2000  }
2001  $this->db->free($resql);
2002  return $num;
2003  }
2004  else
2005  {
2006  $this->error=$this->db->lasterror();
2007  return -1;
2008  }
2009  }
2010 
2018  function nb_expedition()
2019  {
2020  $sql = 'SELECT count(*)';
2021  $sql.= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2022  $sql.= ', '.MAIN_DB_PREFIX.'element_element as el';
2023  $sql.= ' WHERE el.fk_source = '.$this->id;
2024  $sql.= " AND el.fk_target = e.rowid";
2025  $sql.= " AND el.targettype = 'shipping'";
2026 
2027  $resql = $this->db->query($sql);
2028  if ($resql)
2029  {
2030  $row = $this->db->fetch_row($resql);
2031  return $row[0];
2032  }
2033  else dol_print_error($this->db);
2034  }
2035 
2044  function stock_array($filtre_statut=self::STATUS_CANCELED)
2045  {
2046  $this->stocks = array();
2047 
2048  // Tableau des id de produit de la commande
2049  $array_of_product=array();
2050 
2051  // Recherche total en stock pour chaque produit
2052  // TODO $array_of_product est défini vide juste au dessus !!
2053  if (count($array_of_product))
2054  {
2055  $sql = "SELECT fk_product, sum(ps.reel) as total";
2056  $sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2057  $sql.= " WHERE ps.fk_product IN (".join(',',$array_of_product).")";
2058  $sql.= ' GROUP BY fk_product ';
2059  $resql = $this->db->query($sql);
2060  if ($resql)
2061  {
2062  $num = $this->db->num_rows($resql);
2063  $i = 0;
2064  while ($i < $num)
2065  {
2066  $obj = $this->db->fetch_object($resql);
2067  $this->stocks[$obj->fk_product] = $obj->total;
2068  $i++;
2069  }
2070  $this->db->free($resql);
2071  }
2072  }
2073  return 0;
2074  }
2075 
2083  function deleteline($user=null, $lineid=0)
2084  {
2085  if ($this->statut == self::STATUS_DRAFT)
2086  {
2087  $this->db->begin();
2088 
2089  $sql = "SELECT fk_product, qty";
2090  $sql.= " FROM ".MAIN_DB_PREFIX."commandedet";
2091  $sql.= " WHERE rowid = ".$lineid;
2092 
2093  $result = $this->db->query($sql);
2094  if ($result)
2095  {
2096  $obj = $this->db->fetch_object($result);
2097 
2098  if ($obj)
2099  {
2100  $product = new Product($this->db);
2101  $product->id = $obj->fk_product;
2102 
2103  // Delete line
2104  $line = new OrderLine($this->db);
2105 
2106  // For triggers
2107  $line->fetch($lineid);
2108 
2109  if ($line->delete($user) > 0)
2110  {
2111  $result=$this->update_price(1);
2112 
2113  if ($result > 0)
2114  {
2115  $this->db->commit();
2116  return 1;
2117  }
2118  else
2119  {
2120  $this->db->rollback();
2121  $this->error=$this->db->lasterror();
2122  return -1;
2123  }
2124  }
2125  else
2126  {
2127  $this->db->rollback();
2128  $this->error=$line->error;
2129  return -1;
2130  }
2131  }
2132  else
2133  {
2134  $this->db->rollback();
2135  return 0;
2136  }
2137  }
2138  else
2139  {
2140  $this->db->rollback();
2141  $this->error=$this->db->lasterror();
2142  return -1;
2143  }
2144  }
2145  else
2146  {
2147  $this->error='ErrorDeleteLineNotAllowedByObjectStatus';
2148  return -1;
2149  }
2150  }
2151 
2160  function set_remise($user, $remise, $notrigger=0)
2161  {
2162  $remise=trim($remise)?trim($remise):0;
2163 
2164  if ($user->rights->commande->creer)
2165  {
2166  $error=0;
2167 
2168  $this->db->begin();
2169 
2170  $remise=price2num($remise);
2171 
2172  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2173  $sql.= ' SET remise_percent = '.$remise;
2174  $sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2175 
2176  dol_syslog(__METHOD__, LOG_DEBUG);
2177  $resql=$this->db->query($sql);
2178  if (!$resql)
2179  {
2180  $this->errors[]=$this->db->error();
2181  $error++;
2182  }
2183 
2184  if (! $error)
2185  {
2186  $this->oldcopy= clone $this;
2187  $this->remise_percent = $remise;
2188  $this->update_price(1);
2189  }
2190 
2191  if (! $notrigger && empty($error))
2192  {
2193  // Call trigger
2194  $result=$this->call_trigger('ORDER_MODIFY',$user);
2195  if ($result < 0) $error++;
2196  // End call triggers
2197  }
2198 
2199  if (! $error)
2200  {
2201  $this->db->commit();
2202  return 1;
2203  }
2204  else
2205  {
2206  foreach($this->errors as $errmsg)
2207  {
2208  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2209  $this->error.=($this->error?', '.$errmsg:$errmsg);
2210  }
2211  $this->db->rollback();
2212  return -1*$error;
2213  }
2214  }
2215  }
2216 
2217 
2226  function set_remise_absolue($user, $remise, $notrigger=0)
2227  {
2228  $remise=trim($remise)?trim($remise):0;
2229 
2230  if ($user->rights->commande->creer)
2231  {
2232  $error=0;
2233 
2234  $this->db->begin();
2235 
2236  $remise=price2num($remise);
2237 
2238  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2239  $sql.= ' SET remise_absolue = '.$remise;
2240  $sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2241 
2242  dol_syslog(__METHOD__, LOG_DEBUG);
2243  $resql=$this->db->query($sql);
2244  if (!$resql)
2245  {
2246  $this->errors[]=$this->db->error();
2247  $error++;
2248  }
2249 
2250  if (! $error)
2251  {
2252  $this->oldcopy= clone $this;
2253  $this->remise_absolue = $remise;
2254  $this->update_price(1);
2255  }
2256 
2257  if (! $notrigger && empty($error))
2258  {
2259  // Call trigger
2260  $result=$this->call_trigger('ORDER_MODIFY',$user);
2261  if ($result < 0) $error++;
2262  // End call triggers
2263  }
2264 
2265  if (! $error)
2266  {
2267  $this->db->commit();
2268  return 1;
2269  }
2270  else
2271  {
2272  foreach($this->errors as $errmsg)
2273  {
2274  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2275  $this->error.=($this->error?', '.$errmsg:$errmsg);
2276  }
2277  $this->db->rollback();
2278  return -1*$error;
2279  }
2280  }
2281  }
2282 
2283 
2292  function set_date($user, $date, $notrigger=0)
2293  {
2294  if ($user->rights->commande->creer)
2295  {
2296  $error=0;
2297 
2298  $this->db->begin();
2299 
2300  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2301  $sql.= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2302  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2303 
2304  dol_syslog(__METHOD__, LOG_DEBUG);
2305  $resql=$this->db->query($sql);
2306  if (!$resql)
2307  {
2308  $this->errors[]=$this->db->error();
2309  $error++;
2310  }
2311 
2312  if (! $error)
2313  {
2314  $this->oldcopy= clone $this;
2315  $this->date = $date;
2316  }
2317 
2318  if (! $notrigger && empty($error))
2319  {
2320  // Call trigger
2321  $result=$this->call_trigger('ORDER_MODIFY',$user);
2322  if ($result < 0) $error++;
2323  // End call triggers
2324  }
2325 
2326  if (! $error)
2327  {
2328  $this->db->commit();
2329  return 1;
2330  }
2331  else
2332  {
2333  foreach($this->errors as $errmsg)
2334  {
2335  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2336  $this->error.=($this->error?', '.$errmsg:$errmsg);
2337  }
2338  $this->db->rollback();
2339  return -1*$error;
2340  }
2341  }
2342  else
2343  {
2344  return -2;
2345  }
2346  }
2347 
2356  function set_date_livraison($user, $date_livraison, $notrigger=0)
2357  {
2358  if ($user->rights->commande->creer)
2359  {
2360  $error=0;
2361 
2362  $this->db->begin();
2363 
2364  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2365  $sql.= " SET date_livraison = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
2366  $sql.= " WHERE rowid = ".$this->id;
2367 
2368  dol_syslog(__METHOD__, LOG_DEBUG);
2369  $resql=$this->db->query($sql);
2370  if (!$resql)
2371  {
2372  $this->errors[]=$this->db->error();
2373  $error++;
2374  }
2375 
2376  if (! $error)
2377  {
2378  $this->oldcopy= clone $this;
2379  $this->date_livraison = $date_livraison;
2380  }
2381 
2382  if (! $notrigger && empty($error))
2383  {
2384  // Call trigger
2385  $result=$this->call_trigger('ORDER_MODIFY',$user);
2386  if ($result < 0) $error++;
2387  // End call triggers
2388  }
2389 
2390  if (! $error)
2391  {
2392  $this->db->commit();
2393  return 1;
2394  }
2395  else
2396  {
2397  foreach($this->errors as $errmsg)
2398  {
2399  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2400  $this->error.=($this->error?', '.$errmsg:$errmsg);
2401  }
2402  $this->db->rollback();
2403  return -1*$error;
2404  }
2405  }
2406  else
2407  {
2408  return -2;
2409  }
2410  }
2411 
2425  function liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
2426  {
2427  global $user;
2428 
2429  $ga = array();
2430 
2431  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2432  $sql.= " c.rowid as cid, c.ref";
2433  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2434  $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2435  if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2436  $sql.= " WHERE c.entity IN (".getEntity('commande').")";
2437  $sql.= " AND c.fk_soc = s.rowid";
2438  if (! $user->rights->societe->client->voir && ! $socid) //restriction
2439  {
2440  $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2441  }
2442  if ($socid) $sql.= " AND s.rowid = ".$socid;
2443  if ($draft) $sql.= " AND c.fk_statut = ".self::STATUS_DRAFT;
2444  if (is_object($excluser)) $sql.= " AND c.fk_user_author <> ".$excluser->id;
2445  $sql.= $this->db->order($sortfield,$sortorder);
2446  $sql.= $this->db->plimit($limit,$offset);
2447 
2448  $result=$this->db->query($sql);
2449  if ($result)
2450  {
2451  $numc = $this->db->num_rows($result);
2452  if ($numc)
2453  {
2454  $i = 0;
2455  while ($i < $numc)
2456  {
2457  $obj = $this->db->fetch_object($result);
2458 
2459  if ($shortlist == 1)
2460  {
2461  $ga[$obj->cid] = $obj->ref;
2462  }
2463  else if ($shortlist == 2)
2464  {
2465  $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2466  }
2467  else
2468  {
2469  $ga[$i]['id'] = $obj->cid;
2470  $ga[$i]['ref'] = $obj->ref;
2471  $ga[$i]['name'] = $obj->name;
2472  }
2473  $i++;
2474  }
2475  }
2476  return $ga;
2477  }
2478  else
2479  {
2480  dol_print_error($this->db);
2481  return -1;
2482  }
2483  }
2484 
2492  function availability($availability_id, $notrigger=0)
2493  {
2494  global $user;
2495 
2496  dol_syslog('Commande::availability('.$availability_id.')');
2497  if ($this->statut >= self::STATUS_DRAFT)
2498  {
2499  $error=0;
2500 
2501  $this->db->begin();
2502 
2503  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2504  $sql .= ' SET fk_availability = '.$availability_id;
2505  $sql .= ' WHERE rowid='.$this->id;
2506 
2507  dol_syslog(__METHOD__, LOG_DEBUG);
2508  $resql=$this->db->query($sql);
2509  if (!$resql)
2510  {
2511  $this->errors[]=$this->db->error();
2512  $error++;
2513  }
2514 
2515  if (! $error)
2516  {
2517  $this->oldcopy= clone $this;
2518  $this->availability_id = $availability_id;
2519  }
2520 
2521  if (! $notrigger && empty($error))
2522  {
2523  // Call trigger
2524  $result=$this->call_trigger('ORDER_MODIFY',$user);
2525  if ($result < 0) $error++;
2526  // End call triggers
2527  }
2528 
2529  if (! $error)
2530  {
2531  $this->db->commit();
2532  return 1;
2533  }
2534  else
2535  {
2536  foreach($this->errors as $errmsg)
2537  {
2538  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2539  $this->error.=($this->error?', '.$errmsg:$errmsg);
2540  }
2541  $this->db->rollback();
2542  return -1*$error;
2543  }
2544  }
2545  else
2546  {
2547  $error_str='Command status do not meet requirement '.$this->statut;
2548  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2549  $this->error=$error_str;
2550  $this->errors[]= $this->error;
2551  return -2;
2552  }
2553  }
2554 
2562  function demand_reason($demand_reason_id, $notrigger=0)
2563  {
2564  global $user;
2565 
2566  dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2567  if ($this->statut >= self::STATUS_DRAFT)
2568  {
2569  $error=0;
2570 
2571  $this->db->begin();
2572 
2573  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2574  $sql .= ' SET fk_input_reason = '.$demand_reason_id;
2575  $sql .= ' WHERE rowid='.$this->id;
2576 
2577  dol_syslog(__METHOD__, LOG_DEBUG);
2578  $resql=$this->db->query($sql);
2579  if (!$resql)
2580  {
2581  $this->errors[]=$this->db->error();
2582  $error++;
2583  }
2584 
2585  if (! $error)
2586  {
2587  $this->oldcopy= clone $this;
2588  $this->demand_reason_id = $demand_reason_id;
2589  }
2590 
2591  if (! $notrigger && empty($error))
2592  {
2593  // Call trigger
2594  $result=$this->call_trigger('ORDER_MODIFY',$user);
2595  if ($result < 0) $error++;
2596  // End call triggers
2597  }
2598 
2599  if (! $error)
2600  {
2601  $this->db->commit();
2602  return 1;
2603  }
2604  else
2605  {
2606  foreach($this->errors as $errmsg)
2607  {
2608  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2609  $this->error.=($this->error?', '.$errmsg:$errmsg);
2610  }
2611  $this->db->rollback();
2612  return -1*$error;
2613  }
2614  }
2615  else
2616  {
2617  $error_str='order status do not meet requirement '.$this->statut;
2618  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2619  $this->error=$error_str;
2620  $this->errors[]= $this->error;
2621  return -2;
2622  }
2623  }
2624 
2633  function set_ref_client($user, $ref_client, $notrigger=0)
2634  {
2635  if ($user->rights->commande->creer)
2636  {
2637  $error=0;
2638 
2639  $this->db->begin();
2640 
2641  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2642  $sql.= ' ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2643  $sql.= ' WHERE rowid = '.$this->id;
2644 
2645  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2646  $resql=$this->db->query($sql);
2647  if (!$resql)
2648  {
2649  $this->errors[]=$this->db->error();
2650  $error++;
2651  }
2652 
2653  if (! $error)
2654  {
2655  $this->oldcopy= clone $this;
2656  $this->ref_client = $ref_client;
2657  }
2658 
2659  if (! $notrigger && empty($error))
2660  {
2661  // Call trigger
2662  $result=$this->call_trigger('ORDER_MODIFY',$user);
2663  if ($result < 0) $error++;
2664  // End call triggers
2665  }
2666  if (! $error)
2667  {
2668  $this->db->commit();
2669  return 1;
2670  }
2671  else
2672  {
2673  foreach($this->errors as $errmsg)
2674  {
2675  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2676  $this->error.=($this->error?', '.$errmsg:$errmsg);
2677  }
2678  $this->db->rollback();
2679  return -1*$error;
2680  }
2681  }
2682  else
2683  {
2684  return -1;
2685  }
2686  }
2687 
2695  function classifyBilled(User $user, $notrigger=0)
2696  {
2697  $error = 0;
2698 
2699  $this->db->begin();
2700 
2701  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2702  $sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2703 
2704  dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2705  if ($this->db->query($sql))
2706  {
2707 
2708  if (! $error)
2709  {
2710  $this->oldcopy= clone $this;
2711  $this->facturee=1; // deprecated
2712  $this->billed=1;
2713  }
2714 
2715  if (! $notrigger && empty($error))
2716  {
2717  // Call trigger
2718  $result=$this->call_trigger('ORDER_CLASSIFY_BILLED',$user);
2719  if ($result < 0) $error++;
2720  // End call triggers
2721  }
2722 
2723  if (! $error)
2724  {
2725  $this->db->commit();
2726  return 1;
2727  }
2728  else
2729  {
2730  foreach($this->errors as $errmsg)
2731  {
2732  dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
2733  $this->error.=($this->error?', '.$errmsg:$errmsg);
2734  }
2735  $this->db->rollback();
2736  return -1*$error;
2737  }
2738  }
2739  else
2740  {
2741  $this->error=$this->db->error();
2742  $this->db->rollback();
2743  return -1;
2744  }
2745  }
2746 
2754  function classer_facturee()
2755  {
2756  global $user;
2757  dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING);
2758 
2759  return $this->classifyBilled($user);
2760  }
2761 
2767  function classifyUnBilled()
2768  {
2769  global $conf, $user, $langs;
2770  $error = 0;
2771 
2772  $this->db->begin();
2773 
2774  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
2775  $sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2776 
2777  dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
2778  if ($this->db->query($sql))
2779  {
2780  if (! $error)
2781  {
2782  $this->oldcopy= clone $this;
2783  $this->facturee=1; // deprecated
2784  $this->billed=1;
2785  }
2786 
2787  // Call trigger
2788  $result=$this->call_trigger('ORDER_CLASSIFY_UNBILLED',$user);
2789  if ($result < 0) $error++;
2790  // End call triggers
2791 
2792  if (! $error)
2793  {
2794  $this->facturee=0; // deprecated
2795  $this->billed=0;
2796 
2797  $this->db->commit();
2798  return 1;
2799  }
2800  else
2801  {
2802  foreach($this->errors as $errmsg)
2803  {
2804  dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
2805  $this->error.=($this->error?', '.$errmsg:$errmsg);
2806  }
2807  $this->db->rollback();
2808  return -1*$error;
2809  }
2810  }
2811  else
2812  {
2813  $this->error=$this->db->error();
2814  $this->db->rollback();
2815  return -1;
2816  }
2817  }
2818 
2819 
2848  function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0,$txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $fk_unit=null, $pu_ht_devise = 0, $notrigger=0)
2849  {
2850  global $conf, $mysoc, $langs, $user;
2851 
2852  dol_syslog(get_class($this)."::updateline id=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, date_start=$date_start, date_end=$date_end, type=$type, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, special_code=$special_code");
2853  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2854 
2855  if (! empty($this->brouillon))
2856  {
2857  $this->db->begin();
2858 
2859  // Clean parameters
2860  if (empty($qty)) $qty=0;
2861  if (empty($info_bits)) $info_bits=0;
2862  if (empty($txtva)) $txtva=0;
2863  if (empty($txlocaltax1)) $txlocaltax1=0;
2864  if (empty($txlocaltax2)) $txlocaltax2=0;
2865  if (empty($remise_percent)) $remise_percent=0;
2866  if (empty($special_code) || $special_code == 3) $special_code=0;
2867 
2868  $remise_percent=price2num($remise_percent);
2869  $qty=price2num($qty);
2870  $pu = price2num($pu);
2871  $pa_ht=price2num($pa_ht);
2872  $txtva=price2num($txtva);
2873  $txlocaltax1=price2num($txlocaltax1);
2874  $txlocaltax2=price2num($txlocaltax2);
2875 
2876  // Calcul du total TTC et de la TVA pour la ligne a partir de
2877  // qty, pu, remise_percent et txtva
2878  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2879  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2880 
2881  $localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
2882 
2883  // Clean vat code
2884  $vat_src_code='';
2885  if (preg_match('/\((.*)\)/', $txtva, $reg))
2886  {
2887  $vat_src_code = $reg[1];
2888  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2889  }
2890 
2891  $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);
2892 
2893  $total_ht = $tabprice[0];
2894  $total_tva = $tabprice[1];
2895  $total_ttc = $tabprice[2];
2896  $total_localtax1 = $tabprice[9];
2897  $total_localtax2 = $tabprice[10];
2898  $pu_ht = $tabprice[3];
2899  $pu_tva = $tabprice[4];
2900  $pu_ttc = $tabprice[5];
2901 
2902  // MultiCurrency
2903  $multicurrency_total_ht = $tabprice[16];
2904  $multicurrency_total_tva = $tabprice[17];
2905  $multicurrency_total_ttc = $tabprice[18];
2906  $pu_ht_devise = $tabprice[19];
2907 
2908  // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
2909  $price = $pu_ht;
2910  if ($price_base_type == 'TTC')
2911  {
2912  $subprice = $pu_ttc;
2913  }
2914  else
2915  {
2916  $subprice = $pu_ht;
2917  }
2918  $remise = 0;
2919  if ($remise_percent > 0)
2920  {
2921  $remise = round(($pu * $remise_percent / 100),2);
2922  $price = ($pu - $remise);
2923  }
2924 
2925  //Fetch current line from the database and then clone the object and set it in $oldline property
2926  $line = new OrderLine($this->db);
2927  $line->fetch($rowid);
2928 
2929  if (!empty($line->fk_product))
2930  {
2931  $product=new Product($this->db);
2932  $result=$product->fetch($line->fk_product);
2933  $product_type=$product->type;
2934 
2935  if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
2936  {
2937  $langs->load("errors");
2938  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
2939  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
2940  $this->db->rollback();
2941  return self::STOCK_NOT_ENOUGH_FOR_ORDER;
2942  }
2943  }
2944 
2945  $staticline = clone $line;
2946 
2947  $line->oldline = $staticline;
2948  $this->line = $line;
2949  $this->line->context = $this->context;
2950 
2951  // Reorder if fk_parent_line change
2952  if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
2953  {
2954  $rangmax = $this->line_max($fk_parent_line);
2955  $this->line->rang = $rangmax + 1;
2956  }
2957 
2958  $this->line->rowid=$rowid;
2959  $this->line->label=$label;
2960  $this->line->desc=$desc;
2961  $this->line->qty=$qty;
2962 
2963  $this->line->vat_src_code = $vat_src_code;
2964  $this->line->tva_tx = $txtva;
2965  $this->line->localtax1_tx = $txlocaltax1;
2966  $this->line->localtax2_tx = $txlocaltax2;
2967  $this->line->localtax1_type = $localtaxes_type[0];
2968  $this->line->localtax2_type = $localtaxes_type[2];
2969  $this->line->remise_percent = $remise_percent;
2970  $this->line->subprice = $subprice;
2971  $this->line->info_bits = $info_bits;
2972  $this->line->special_code = $special_code;
2973  $this->line->total_ht = $total_ht;
2974  $this->line->total_tva = $total_tva;
2975  $this->line->total_localtax1= $total_localtax1;
2976  $this->line->total_localtax2= $total_localtax2;
2977  $this->line->total_ttc = $total_ttc;
2978  $this->line->date_start = $date_start;
2979  $this->line->date_end = $date_end;
2980  $this->line->product_type = $type;
2981  $this->line->fk_parent_line = $fk_parent_line;
2982  $this->line->skip_update_total=$skip_update_total;
2983  $this->line->fk_unit = $fk_unit;
2984 
2985  $this->line->fk_fournprice = $fk_fournprice;
2986  $this->line->pa_ht = $pa_ht;
2987 
2988  // Multicurrency
2989  $this->line->multicurrency_subprice = $pu_ht_devise;
2990  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2991  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2992  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2993 
2994  // TODO deprecated
2995  $this->line->price=$price;
2996  $this->line->remise=$remise;
2997 
2998  if (is_array($array_options) && count($array_options)>0) {
2999  $this->line->array_options=$array_options;
3000  }
3001 
3002  $result=$this->line->update($user, $notrigger);
3003  if ($result > 0)
3004  {
3005  // Reorder if child line
3006  if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
3007 
3008  // Mise a jour info denormalisees
3009  $this->update_price(1);
3010 
3011  $this->db->commit();
3012  return $result;
3013  }
3014  else
3015  {
3016  $this->error=$this->line->error;
3017 
3018  $this->db->rollback();
3019  return -1;
3020  }
3021  }
3022  else
3023  {
3024  $this->error=get_class($this)."::updateline Order status makes operation forbidden";
3025  $this->errors=array('OrderStatusMakeOperationForbidden');
3026  return -2;
3027  }
3028  }
3029 
3037  function update(User $user, $notrigger=0)
3038  {
3039  $error=0;
3040 
3041  // Clean parameters
3042  if (isset($this->ref)) $this->ref=trim($this->ref);
3043  if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
3044  if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3045  if (isset($this->note_public)) $this->note_public=trim($this->note_public);
3046  if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
3047  if (isset($this->import_key)) $this->import_key=trim($this->import_key);
3048 
3049  // Check parameters
3050  // Put here code to add control on parameters values
3051 
3052  // Update request
3053  $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3054 
3055  $sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
3056  $sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
3057  $sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
3058  $sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
3059  $sql.= " date_commande=".(strval($this->date_commande)!='' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3060  $sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3061  $sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
3062  $sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
3063  $sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
3064  $sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
3065  $sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
3066  $sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
3067  $sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
3068  $sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
3069  $sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
3070  $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
3071  $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
3072  $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
3073  $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
3074  $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
3075  $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
3076 
3077  $sql.= " WHERE rowid=".$this->id;
3078 
3079  $this->db->begin();
3080 
3081  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3082  $resql = $this->db->query($sql);
3083  if (! $resql) {
3084  $error++; $this->errors[]="Error ".$this->db->lasterror();
3085  }
3086 
3087  if (! $error)
3088  {
3089  if (! $notrigger)
3090  {
3091  // Call trigger
3092  $result=$this->call_trigger('ORDER_MODIFY', $user);
3093  if ($result < 0) $error++;
3094  // End call triggers
3095  }
3096  }
3097 
3098  // Commit or rollback
3099  if ($error)
3100  {
3101  foreach($this->errors as $errmsg)
3102  {
3103  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3104  $this->error.=($this->error?', '.$errmsg:$errmsg);
3105  }
3106  $this->db->rollback();
3107  return -1*$error;
3108  }
3109  else
3110  {
3111  $this->db->commit();
3112  return 1;
3113  }
3114  }
3115 
3123  function delete($user, $notrigger=0)
3124  {
3125  global $conf, $langs;
3126  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3127 
3128  $error = 0;
3129 
3130  dol_syslog(get_class($this) . "::delete ".$this->id, LOG_DEBUG);
3131 
3132  $this->db->begin();
3133 
3134  if (! $error && ! $notrigger)
3135  {
3136  // Call trigger
3137  $result=$this->call_trigger('ORDER_DELETE',$user);
3138  if ($result < 0) $error++;
3139  // End call triggers
3140  }
3141 
3142  if (! $error)
3143  {
3144  // Delete order details
3145  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE fk_commande = ".$this->id;
3146  if (! $this->db->query($sql) )
3147  {
3148  $error++;
3149  $this->errors[]=$this->db->lasterror();
3150  }
3151  }
3152 
3153  if (! $error)
3154  {
3155  // Delete linked object
3156  $res = $this->deleteObjectLinked();
3157  if ($res < 0) $error++;
3158  }
3159 
3160  if (! $error)
3161  {
3162  // Delete linked contacts
3163  $res = $this->delete_linked_contact();
3164  if ($res < 0) $error++;
3165  }
3166 
3167  if (! $error)
3168  {
3169  // Remove extrafields
3170  if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
3171  {
3172  $result=$this->deleteExtraFields();
3173  if ($result < 0)
3174  {
3175  $error++;
3176  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3177  }
3178  }
3179  }
3180 
3181  if (! $error)
3182  {
3183  // Delete object
3184  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande WHERE rowid = ".$this->id;
3185  if (! $this->db->query($sql) )
3186  {
3187  $error++;
3188  $this->errors[]=$this->db->lasterror();
3189  }
3190  }
3191 
3192  if (! $error)
3193  {
3194  // Remove directory with files
3195  $comref = dol_sanitizeFileName($this->ref);
3196  if ($conf->commande->dir_output && !empty($this->ref))
3197  {
3198  $dir = $conf->commande->dir_output . "/" . $comref ;
3199  $file = $conf->commande->dir_output . "/" . $comref . "/" . $comref . ".pdf";
3200  if (file_exists($file)) // We must delete all files before deleting directory
3201  {
3202  dol_delete_preview($this);
3203 
3204  if (! dol_delete_file($file,0,0,0,$this)) // For triggers
3205  {
3206  $this->db->rollback();
3207  return 0;
3208  }
3209  }
3210  if (file_exists($dir))
3211  {
3212  if (! dol_delete_dir_recursive($dir))
3213  {
3214  $this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
3215  $this->db->rollback();
3216  return 0;
3217  }
3218  }
3219  }
3220  }
3221 
3222  if (! $error)
3223  {
3224  $this->db->commit();
3225  return 1;
3226  }
3227  else
3228  {
3229  foreach($this->errors as $errmsg)
3230  {
3231  $this->error.=($this->error?', '.$errmsg:$errmsg);
3232  }
3233  $this->db->rollback();
3234  return -1*$error;
3235  }
3236  }
3237 
3238 
3245  function load_board($user)
3246  {
3247  global $conf, $langs;
3248 
3249  $clause = " WHERE";
3250 
3251  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut";
3252  $sql.= " FROM ".MAIN_DB_PREFIX."commande as c";
3253  if (!$user->rights->societe->client->voir && !$user->societe_id)
3254  {
3255  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3256  $sql.= " WHERE sc.fk_user = " .$user->id;
3257  $clause = " AND";
3258  }
3259  $sql.= $clause." c.entity IN (".getEntity('commande').")";
3260  //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3261  $sql.= " AND ((c.fk_statut IN (".self::STATUS_VALIDATED.",".self::STATUS_SHIPMENTONPROCESS.")) OR (c.fk_statut = ".self::STATUS_CLOSED." AND c.facture = 0))"; // If status is 2 and facture=1, it must be selected
3262  if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
3263 
3264  $resql=$this->db->query($sql);
3265  if ($resql)
3266  {
3267  $response = new WorkboardResponse();
3268  $response->warning_delay=$conf->commande->client->warning_delay/60/60/24;
3269  $response->label=$langs->trans("OrdersToProcess");
3270  $response->url=DOL_URL_ROOT.'/commande/list.php?viewstatut=-3&mainmenu=commercial&leftmenu=orders';
3271  $response->img=img_object('',"order");
3272 
3273  $generic_commande = new Commande($this->db);
3274 
3275  while ($obj=$this->db->fetch_object($resql))
3276  {
3277  $response->nbtodo++;
3278 
3279  $generic_commande->statut = $obj->fk_statut;
3280  $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3281  $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3282 
3283  if ($generic_commande->hasDelay()) {
3284  $response->nbtodolate++;
3285  }
3286  }
3287 
3288  return $response;
3289  }
3290  else
3291  {
3292  $this->error=$this->db->error();
3293  return -1;
3294  }
3295  }
3296 
3302  function getLabelSource()
3303  {
3304  global $langs;
3305 
3306  $label=$langs->trans('OrderSource'.$this->source);
3307 
3308  if ($label == 'OrderSource') return '';
3309  return $label;
3310  }
3311 
3318  function getLibStatut($mode)
3319  {
3320  if ($this->facturee && empty($this->billed)) $this->billed=$this->facturee; // For backward compatibility
3321  return $this->LibStatut($this->statut, $this->billed, $mode);
3322  }
3323 
3333  function LibStatut($statut,$billed,$mode,$donotshowbilled=0)
3334  {
3335  global $langs, $conf;
3336 
3337  $billedtext = '';
3338  if (empty($donotshowbilled)) $billedtext .= ($billed?' - '.$langs->trans("Billed"):'');
3339 
3340  //print 'x'.$statut.'-'.$billed;
3341  if ($mode == 0)
3342  {
3343  if ($statut==self::STATUS_CANCELED) return $langs->trans('StatusOrderCanceled');
3344  if ($statut==self::STATUS_DRAFT) return $langs->trans('StatusOrderDraft');
3345  if ($statut==self::STATUS_VALIDATED) return $langs->trans('StatusOrderValidated').$billedtext;
3346  if ($statut==self::STATUS_SHIPMENTONPROCESS) return $langs->trans('StatusOrderSentShort').$billedtext;
3347  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderToBill');
3348  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderProcessed').$billedtext;
3349  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderDelivered');
3350  }
3351  elseif ($mode == 1)
3352  {
3353  if ($statut==self::STATUS_CANCELED) return $langs->trans('StatusOrderCanceledShort');
3354  if ($statut==self::STATUS_DRAFT) return $langs->trans('StatusOrderDraftShort');
3355  if ($statut==self::STATUS_VALIDATED) return $langs->trans('StatusOrderValidatedShort').$billedtext;
3356  if ($statut==self::STATUS_SHIPMENTONPROCESS) return $langs->trans('StatusOrderSentShort').$billedtext;
3357  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderToBillShort');
3358  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderProcessed').$billedtext;
3359  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderDelivered');
3360  }
3361  elseif ($mode == 2)
3362  {
3363  if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5').' '.$langs->trans('StatusOrderCanceledShort');
3364  if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0').' '.$langs->trans('StatusOrderDraftShort');
3365  if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated'),'statut1').' '.$langs->trans('StatusOrderValidatedShort').$billedtext;
3366  if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSent'),'statut3').' '.$langs->trans('StatusOrderSentShort').$billedtext;
3367  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4').' '.$langs->trans('StatusOrderToBillShort');
3368  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6').' '.$langs->trans('StatusOrderProcessed').$billedtext;
3369  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6').' '.$langs->trans('StatusOrderDeliveredShort');
3370  }
3371  elseif ($mode == 3)
3372  {
3373  if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3374  if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
3375  if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3376  if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSentShort').$billedtext,'statut3');
3377  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
3378  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3379  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3380  }
3381  elseif ($mode == 4)
3382  {
3383  if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5').' '.$langs->trans('StatusOrderCanceled');
3384  if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0').' '.$langs->trans('StatusOrderDraft');
3385  if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1').' '.$langs->trans('StatusOrderValidated').$billedtext;
3386  if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSentShort').$billedtext,'statut3').' '.$langs->trans('StatusOrderSent').$billedtext;
3387  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4').' '.$langs->trans('StatusOrderToBill');
3388  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessedShort').$billedtext,'statut6').' '.$langs->trans('StatusOrderProcessed').$billedtext;
3389  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6').' '.$langs->trans('StatusOrderDelivered');
3390  }
3391  elseif ($mode == 5)
3392  {
3393  if ($statut==self::STATUS_CANCELED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderCanceledShort').' </span>'.img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3394  if ($statut==self::STATUS_DRAFT) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDraftShort').' </span>'.img_picto($langs->trans('StatusOrderDraft'),'statut0');
3395  if ($statut==self::STATUS_VALIDATED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderValidatedShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3396  if ($statut==self::STATUS_SHIPMENTONPROCESS) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderSentShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderSent').$billedtext,'statut3');
3397  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderToBillShort').' </span>'.img_picto($langs->trans('StatusOrderToBill'),'statut4');
3398  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderProcessedShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3399  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDeliveredShort').' </span>'.img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3400  }
3401  elseif ($mode == 6)
3402  {
3403  if ($statut==self::STATUS_CANCELED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderCanceled').' </span>'.img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3404  if ($statut==self::STATUS_DRAFT) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDraft').' </span>'.img_picto($langs->trans('StatusOrderDraft'),'statut0');
3405  if ($statut==self::STATUS_VALIDATED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderValidated').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3406  if ($statut==self::STATUS_SHIPMENTONPROCESS) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderSent').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderSent').$billedtext,'statut3');
3407  if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderToBill').' </span>'.img_picto($langs->trans('StatusOrderToBill'),'statut4');
3408  if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderProcessed').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3409  if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDelivered').' </span>'.img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3410  }
3411 
3412  }
3413 
3414 
3426  function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
3427  {
3428  global $conf, $langs, $user;
3429 
3430  if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
3431 
3432  $result='';
3433 
3434  if (! empty($conf->expedition->enabled) && ($option == '1' || $option == '2')) $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3435  else $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3436 
3437  if ($option !== 'nolink')
3438  {
3439  // Add param to save lastsearch_values or not
3440  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3441  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3442  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3443  }
3444 
3445  if ($short) return $url;
3446 
3447  $label = '';
3448 
3449  if ($user->rights->commande->lire) {
3450  $label = '<u>'.$langs->trans("ShowOrder").'</u>';
3451  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3452  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
3453  if (!empty($this->total_ht)) {
3454  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3455  }
3456  if (!empty($this->total_tva)) {
3457  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3458  }
3459  if (!empty($this->total_ttc)) {
3460  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3461  }
3462  }
3463 
3464  $linkclose='';
3465  if (empty($notooltip) && $user->rights->commande->lire)
3466  {
3467  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3468  {
3469  $label=$langs->trans("ShowOrder");
3470  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3471  }
3472  $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3473  $linkclose.=' class="classfortooltip"';
3474  }
3475 
3476  $linkstart = '<a href="'.$url.'"';
3477  $linkstart.=$linkclose.'>';
3478  $linkend='</a>';
3479 
3480  $result .= $linkstart;
3481  if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3482  if ($withpicto != 2) $result.= $this->ref;
3483  $result .= $linkend;
3484 
3485  return $result;
3486  }
3487 
3488 
3495  function info($id)
3496  {
3497  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3498  $sql.= ' date_valid as datev,';
3499  $sql.= ' date_cloture as datecloture,';
3500  $sql.= ' fk_user_author, fk_user_valid, fk_user_cloture';
3501  $sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3502  $sql.= ' WHERE c.rowid = '.$id;
3503  $result=$this->db->query($sql);
3504  if ($result)
3505  {
3506  if ($this->db->num_rows($result))
3507  {
3508  $obj = $this->db->fetch_object($result);
3509  $this->id = $obj->rowid;
3510  if ($obj->fk_user_author)
3511  {
3512  $cuser = new User($this->db);
3513  $cuser->fetch($obj->fk_user_author);
3514  $this->user_creation = $cuser;
3515  }
3516 
3517  if ($obj->fk_user_valid)
3518  {
3519  $vuser = new User($this->db);
3520  $vuser->fetch($obj->fk_user_valid);
3521  $this->user_validation = $vuser;
3522  }
3523 
3524  if ($obj->fk_user_cloture)
3525  {
3526  $cluser = new User($this->db);
3527  $cluser->fetch($obj->fk_user_cloture);
3528  $this->user_cloture = $cluser;
3529  }
3530 
3531  $this->date_creation = $this->db->jdate($obj->datec);
3532  $this->date_modification = $this->db->jdate($obj->datem);
3533  $this->date_validation = $this->db->jdate($obj->datev);
3534  $this->date_cloture = $this->db->jdate($obj->datecloture);
3535  }
3536 
3537  $this->db->free($result);
3538 
3539  }
3540  else
3541  {
3542  dol_print_error($this->db);
3543  }
3544  }
3545 
3546 
3554  function initAsSpecimen()
3555  {
3556  global $langs;
3557 
3558  dol_syslog(get_class($this)."::initAsSpecimen");
3559 
3560  // Load array of products prodids
3561  $num_prods = 0;
3562  $prodids = array();
3563  $sql = "SELECT rowid";
3564  $sql.= " FROM ".MAIN_DB_PREFIX."product";
3565  $sql.= " WHERE entity IN (".getEntity('product').")";
3566  $resql = $this->db->query($sql);
3567  if ($resql)
3568  {
3569  $num_prods = $this->db->num_rows($resql);
3570  $i = 0;
3571  while ($i < $num_prods)
3572  {
3573  $i++;
3574  $row = $this->db->fetch_row($resql);
3575  $prodids[$i] = $row[0];
3576  }
3577  }
3578 
3579  // Initialise parametres
3580  $this->id=0;
3581  $this->ref = 'SPECIMEN';
3582  $this->specimen=1;
3583  $this->socid = 1;
3584  $this->date = time();
3585  $this->date_lim_reglement=$this->date+3600*24*30;
3586  $this->cond_reglement_code = 'RECEP';
3587  $this->mode_reglement_code = 'CHQ';
3588  $this->availability_code = 'DSP';
3589  $this->demand_reason_code = 'SRC_00';
3590  $this->note_public='This is a comment (public)';
3591  $this->note_private='This is a comment (private)';
3592  // Lines
3593  $nbp = 5;
3594  $xnbp = 0;
3595  while ($xnbp < $nbp)
3596  {
3597  $line=new OrderLine($this->db);
3598 
3599  $line->desc=$langs->trans("Description")." ".$xnbp;
3600  $line->qty=1;
3601  $line->subprice=100;
3602  $line->price=100;
3603  $line->tva_tx=20;
3604  if ($xnbp == 2)
3605  {
3606  $line->total_ht=50;
3607  $line->total_ttc=60;
3608  $line->total_tva=10;
3609  $line->remise_percent=50;
3610  }
3611  else
3612  {
3613  $line->total_ht=100;
3614  $line->total_ttc=120;
3615  $line->total_tva=20;
3616  $line->remise_percent=0;
3617  }
3618  if ($num_prods > 0)
3619  {
3620  $prodid = mt_rand(1, $num_prods);
3621  $line->fk_product=$prodids[$prodid];
3622  $line->product_ref='SPECIMEN';
3623  }
3624 
3625  $this->lines[$xnbp]=$line;
3626 
3627  $this->total_ht += $line->total_ht;
3628  $this->total_tva += $line->total_tva;
3629  $this->total_ttc += $line->total_ttc;
3630 
3631  $xnbp++;
3632  }
3633  }
3634 
3635 
3641  function load_state_board()
3642  {
3643  global $user;
3644 
3645  $this->nb=array();
3646  $clause = "WHERE";
3647 
3648  $sql = "SELECT count(co.rowid) as nb";
3649  $sql.= " FROM ".MAIN_DB_PREFIX."commande as co";
3650  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3651  if (!$user->rights->societe->client->voir && !$user->societe_id)
3652  {
3653  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3654  $sql.= " WHERE sc.fk_user = " .$user->id;
3655  $clause = "AND";
3656  }
3657  $sql.= " ".$clause." co.entity IN (".getEntity('commande').")";
3658 
3659  $resql=$this->db->query($sql);
3660  if ($resql)
3661  {
3662  while ($obj=$this->db->fetch_object($resql))
3663  {
3664  $this->nb["orders"]=$obj->nb;
3665  }
3666  $this->db->free($resql);
3667  return 1;
3668  }
3669  else
3670  {
3671  dol_print_error($this->db);
3672  $this->error=$this->db->error();
3673  return -1;
3674  }
3675  }
3676 
3682  function getLinesArray()
3683  {
3684  return $this->fetch_lines();
3685  }
3686 
3697  public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
3698  {
3699  global $conf,$langs;
3700 
3701  $langs->load("orders");
3702 
3703  if (! dol_strlen($modele)) {
3704 
3705  $modele = 'einstein';
3706 
3707  if ($this->modelpdf) {
3708  $modele = $this->modelpdf;
3709  } elseif (! empty($conf->global->COMMANDE_ADDON_PDF)) {
3710  $modele = $conf->global->COMMANDE_ADDON_PDF;
3711  }
3712  }
3713 
3714  $modelpath = "core/modules/commande/doc/";
3715 
3716  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
3717  }
3718 
3719 
3728  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3729  {
3730  $tables = array(
3731  'commande'
3732  );
3733 
3734  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3735  }
3736 
3742  public function hasDelay()
3743  {
3744  global $conf;
3745 
3746  if (! ($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
3747  return false; // Never late if not inside this status range
3748  }
3749 
3750  $now = dol_now();
3751 
3752  return max($this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
3753  }
3754 
3760  public function showDelay()
3761  {
3762  global $conf, $langs;
3763 
3764  if (empty($this->date_livraison)) $text=$langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3765  else $text=$text=$langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
3766  $text.=' '.($conf->commande->client->warning_delay>0?'+':'-').' '.round(abs($conf->commande->client->warning_delay)/3600/24,1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3767 
3768  return $text;
3769  }
3770 }
3771 
3772 
3777 {
3778  public $element='commandedet';
3779  public $table_element='commandedet';
3780 
3781  var $oldline;
3782 
3787  public $fk_commande;
3788 
3795  public $commande_id;
3796 
3797  // From llx_commandedet
3798  var $fk_parent_line;
3799  var $fk_facture;
3800  var $label;
3801  var $fk_remise_except;
3802  var $rang = 0;
3803  var $fk_fournprice;
3804 
3809  var $pa_ht;
3810  var $marge_tx;
3811  var $marque_tx;
3812 
3817  var $remise;
3818 
3819  // Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
3820  // Start and end date of the line
3821  var $date_start;
3822  var $date_end;
3823 
3824  var $skip_update_total; // Skip update price total for special lines
3825 
3826 
3832  function __construct($db)
3833  {
3834  $this->db= $db;
3835  }
3836 
3843  function fetch($rowid)
3844  {
3845  $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_parent_line, cd.fk_product, cd.product_type, cd.label as custom_label, cd.description, cd.price, cd.qty, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx,';
3846  $sql.= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice,';
3847  $sql.= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.fk_product_fournisseur_price as fk_fournprice, cd.buy_price_ht as pa_ht, cd.rang, cd.special_code,';
3848  $sql.= ' cd.fk_unit,';
3849  $sql.= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3850  $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc, p.tobatch as product_tobatch,';
3851  $sql.= ' cd.date_start, cd.date_end';
3852  $sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
3853  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3854  $sql.= ' WHERE cd.rowid = '.$rowid;
3855  $result = $this->db->query($sql);
3856  if ($result)
3857  {
3858  $objp = $this->db->fetch_object($result);
3859  $this->rowid = $objp->rowid;
3860  $this->fk_commande = $objp->fk_commande;
3861  $this->fk_parent_line = $objp->fk_parent_line;
3862  $this->label = $objp->custom_label;
3863  $this->desc = $objp->description;
3864  $this->qty = $objp->qty;
3865  $this->price = $objp->price;
3866  $this->subprice = $objp->subprice;
3867  $this->vat_src_code = $objp->vat_src_code;
3868  $this->tva_tx = $objp->tva_tx;
3869  $this->localtax1_tx = $objp->localtax1_tx;
3870  $this->localtax2_tx = $objp->localtax2_tx;
3871  $this->remise = $objp->remise;
3872  $this->remise_percent = $objp->remise_percent;
3873  $this->fk_remise_except = $objp->fk_remise_except;
3874  $this->fk_product = $objp->fk_product;
3875  $this->product_type = $objp->product_type;
3876  $this->info_bits = $objp->info_bits;
3877  $this->special_code = $objp->special_code;
3878  $this->total_ht = $objp->total_ht;
3879  $this->total_tva = $objp->total_tva;
3880  $this->total_localtax1 = $objp->total_localtax1;
3881  $this->total_localtax2 = $objp->total_localtax2;
3882  $this->total_ttc = $objp->total_ttc;
3883  $this->fk_fournprice = $objp->fk_fournprice;
3884  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3885  $this->pa_ht = $marginInfos[0];
3886  $this->marge_tx = $marginInfos[1];
3887  $this->marque_tx = $marginInfos[2];
3888  $this->special_code = $objp->special_code;
3889  $this->rang = $objp->rang;
3890 
3891  $this->ref = $objp->product_ref; // deprecated
3892  $this->product_ref = $objp->product_ref;
3893  $this->libelle = $objp->product_libelle; // deprecated
3894  $this->product_label = $objp->product_libelle;
3895  $this->product_desc = $objp->product_desc;
3896  $this->product_tobatch = $objp->product_tobatch;
3897  $this->fk_unit = $objp->fk_unit;
3898 
3899  $this->date_start = $this->db->jdate($objp->date_start);
3900  $this->date_end = $this->db->jdate($objp->date_end);
3901 
3902  $this->fk_multicurrency = $objp->fk_multicurrency;
3903  $this->multicurrency_code = $objp->multicurrency_code;
3904  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3905  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3906  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3907  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3908 
3909  $this->db->free($result);
3910 
3911  return 1;
3912  }
3913  else
3914  {
3915  $this->error = $this->db->lasterror();
3916  return -1;
3917  }
3918  }
3919 
3927  function delete($user=null, $notrigger=0)
3928  {
3929  global $conf, $user, $langs;
3930 
3931  $error=0;
3932 
3933  $this->db->begin();
3934 
3935  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid=".$this->rowid;
3936 
3937  dol_syslog("OrderLine::delete", LOG_DEBUG);
3938  $resql=$this->db->query($sql);
3939  if ($resql)
3940  {
3941  // Remove extrafields
3942  if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
3943  {
3944  $this->id=$this->rowid;
3945  $result=$this->deleteExtraFields();
3946  if ($result < 0)
3947  {
3948  $error++;
3949  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3950  }
3951  }
3952 
3953  if (! $error && ! $notrigger)
3954  {
3955  // Call trigger
3956  $result=$this->call_trigger('LINEORDER_DELETE',$user);
3957  if ($result < 0) $error++;
3958  // End call triggers
3959  }
3960 
3961  if (!$error) {
3962  $this->db->commit();
3963  return 1;
3964  }
3965 
3966  foreach($this->errors as $errmsg)
3967  {
3968  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3969  $this->error.=($this->error?', '.$errmsg:$errmsg);
3970  }
3971  $this->db->rollback();
3972  return -1*$error;
3973  }
3974  else
3975  {
3976  $this->error=$this->db->lasterror();
3977  return -1;
3978  }
3979  }
3980 
3988  function insert($user=null, $notrigger=0)
3989  {
3990  global $langs, $conf;
3991 
3992  $error=0;
3993 
3994  $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'.
3995 
3996  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3997 
3998  // Clean parameters
3999  if (empty($this->tva_tx)) $this->tva_tx=0;
4000  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4001  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4002  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4003  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4004  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4005  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4006  if (empty($this->rang)) $this->rang=0;
4007  if (empty($this->remise)) $this->remise=0;
4008  if (empty($this->remise_percent)) $this->remise_percent=0;
4009  if (empty($this->info_bits)) $this->info_bits=0;
4010  if (empty($this->special_code)) $this->special_code=0;
4011  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4012  if (empty($this->pa_ht)) $this->pa_ht=0;
4013 
4014  // if buy price not defined, define buyprice as configured in margin admin
4015  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4016  {
4017  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4018  {
4019  return $result;
4020  }
4021  else
4022  {
4023  $this->pa_ht = $result;
4024  }
4025  }
4026 
4027  // Check parameters
4028  if ($this->product_type < 0) return -1;
4029 
4030  $this->db->begin();
4031 
4032  // Insertion dans base de la ligne
4033  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4034  $sql.= ' (fk_commande, fk_parent_line, label, description, qty, ';
4035  $sql.= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4036  $sql.= ' fk_product, product_type, remise_percent, subprice, price, remise, fk_remise_except,';
4037  $sql.= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4038  $sql.= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4039  $sql.= ' fk_unit';
4040  $sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4041  $sql.= ')';
4042  $sql.= " VALUES (".$this->fk_commande.",";
4043  $sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
4044  $sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4045  $sql.= " '".$this->db->escape($this->desc)."',";
4046  $sql.= " '".price2num($this->qty)."',";
4047  $sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4048  $sql.= " '".price2num($this->tva_tx)."',";
4049  $sql.= " '".price2num($this->localtax1_tx)."',";
4050  $sql.= " '".price2num($this->localtax2_tx)."',";
4051  $sql.= " '".$this->db->escape($this->localtax1_type)."',";
4052  $sql.= " '".$this->db->escape($this->localtax2_type)."',";
4053  $sql.= ' '.(! empty($this->fk_product)?$this->fk_product:"null").',';
4054  $sql.= " '".$this->db->escape($this->product_type)."',";
4055  $sql.= " '".price2num($this->remise_percent)."',";
4056  $sql.= " ".($this->subprice!=''?"'".price2num($this->subprice)."'":"null").",";
4057  $sql.= " ".($this->price!=''?"'".price2num($this->price)."'":"null").",";
4058  $sql.= " '".price2num($this->remise)."',";
4059  $sql.= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
4060  $sql.= ' '.$this->special_code.',';
4061  $sql.= ' '.$this->rang.',';
4062  $sql.= ' '.(! empty($this->fk_fournprice)?$this->fk_fournprice:"null").',';
4063  $sql.= ' '.price2num($this->pa_ht).',';
4064  $sql.= " '".$this->db->escape($this->info_bits)."',";
4065  $sql.= " '".price2num($this->total_ht)."',";
4066  $sql.= " '".price2num($this->total_tva)."',";
4067  $sql.= " '".price2num($this->total_localtax1)."',";
4068  $sql.= " '".price2num($this->total_localtax2)."',";
4069  $sql.= " '".price2num($this->total_ttc)."',";
4070  $sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
4071  $sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").',';
4072  $sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4073  $sql.= ", ".$this->fk_multicurrency;
4074  $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4075  $sql.= ", ".$this->multicurrency_subprice;
4076  $sql.= ", ".$this->multicurrency_total_ht;
4077  $sql.= ", ".$this->multicurrency_total_tva;
4078  $sql.= ", ".$this->multicurrency_total_ttc;
4079  $sql.= ')';
4080 
4081  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4082  $resql=$this->db->query($sql);
4083  if ($resql)
4084  {
4085  $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4086 
4087  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4088  {
4089  $this->id=$this->rowid;
4090  $result=$this->insertExtraFields();
4091  if ($result < 0)
4092  {
4093  $error++;
4094  }
4095  }
4096 
4097  if (! $error && ! $notrigger)
4098  {
4099  // Call trigger
4100  $result=$this->call_trigger('LINEORDER_INSERT',$user);
4101  if ($result < 0) $error++;
4102  // End call triggers
4103  }
4104 
4105  if (!$error) {
4106  $this->db->commit();
4107  return 1;
4108  }
4109 
4110  foreach($this->errors as $errmsg)
4111  {
4112  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4113  $this->error.=($this->error?', '.$errmsg:$errmsg);
4114  }
4115  $this->db->rollback();
4116  return -1*$error;
4117  }
4118  else
4119  {
4120  $this->error=$this->db->error();
4121  $this->db->rollback();
4122  return -2;
4123  }
4124  }
4125 
4133  function update(User $user, $notrigger=0)
4134  {
4135  global $conf,$langs;
4136 
4137  $error=0;
4138 
4139  $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'.
4140 
4141  // Clean parameters
4142  if (empty($this->tva_tx)) $this->tva_tx=0;
4143  if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4144  if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4145  if (empty($this->localtax1_type)) $this->localtax1_type=0;
4146  if (empty($this->localtax2_type)) $this->localtax2_type=0;
4147  if (empty($this->qty)) $this->qty=0;
4148  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4149  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4150  if (empty($this->marque_tx)) $this->marque_tx=0;
4151  if (empty($this->marge_tx)) $this->marge_tx=0;
4152  if (empty($this->remise)) $this->remise=0;
4153  if (empty($this->remise_percent)) $this->remise_percent=0;
4154  if (empty($this->info_bits)) $this->info_bits=0;
4155  if (empty($this->special_code)) $this->special_code=0;
4156  if (empty($this->product_type)) $this->product_type=0;
4157  if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4158  if (empty($this->pa_ht)) $this->pa_ht=0;
4159 
4160  // if buy price not defined, define buyprice as configured in margin admin
4161  if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4162  {
4163  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4164  {
4165  return $result;
4166  }
4167  else
4168  {
4169  $this->pa_ht = $result;
4170  }
4171  }
4172 
4173  $this->db->begin();
4174 
4175  // Mise a jour ligne en base
4176  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4177  $sql.= " description='".$this->db->escape($this->desc)."'";
4178  $sql.= " , label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4179  $sql.= " , vat_src_code=".(! empty($this->vat_src_code)?"'".$this->db->escape($this->vat_src_code)."'":"''");
4180  $sql.= " , tva_tx=".price2num($this->tva_tx);
4181  $sql.= " , localtax1_tx=".price2num($this->localtax1_tx);
4182  $sql.= " , localtax2_tx=".price2num($this->localtax2_tx);
4183  $sql.= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4184  $sql.= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4185  $sql.= " , qty=".price2num($this->qty);
4186  $sql.= " , subprice=".price2num($this->subprice)."";
4187  $sql.= " , remise_percent=".price2num($this->remise_percent)."";
4188  $sql.= " , price=".price2num($this->price).""; // TODO A virer
4189  $sql.= " , remise=".price2num($this->remise).""; // TODO A virer
4190  if (empty($this->skip_update_total))
4191  {
4192  $sql.= " , total_ht=".price2num($this->total_ht)."";
4193  $sql.= " , total_tva=".price2num($this->total_tva)."";
4194  $sql.= " , total_ttc=".price2num($this->total_ttc)."";
4195  $sql.= " , total_localtax1=".price2num($this->total_localtax1);
4196  $sql.= " , total_localtax2=".price2num($this->total_localtax2);
4197  }
4198  $sql.= " , fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?$this->fk_fournprice:"null");
4199  $sql.= " , buy_price_ht='".price2num($this->pa_ht)."'";
4200  $sql.= " , info_bits=".$this->info_bits;
4201  $sql.= " , special_code=".$this->special_code;
4202  $sql.= " , date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4203  $sql.= " , date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4204  $sql.= " , product_type=".$this->product_type;
4205  $sql.= " , fk_parent_line=".(! empty($this->fk_parent_line)?$this->fk_parent_line:"null");
4206  if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4207  $sql.= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4208 
4209  // Multicurrency
4210  $sql.= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4211  $sql.= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4212  $sql.= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4213  $sql.= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4214 
4215  $sql.= " WHERE rowid = ".$this->rowid;
4216 
4217  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4218  $resql=$this->db->query($sql);
4219  if ($resql)
4220  {
4221  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4222  {
4223  $this->id=$this->rowid;
4224  $result=$this->insertExtraFields();
4225  if ($result < 0)
4226  {
4227  $error++;
4228  }
4229  }
4230 
4231  if (! $notrigger)
4232  {
4233  // Call trigger
4234  $result=$this->call_trigger('LINEORDER_UPDATE',$user);
4235  if ($result < 0) $error++;
4236  // End call triggers
4237  }
4238 
4239  if (!$error) {
4240  $this->db->commit();
4241  return 1;
4242  }
4243 
4244  foreach($this->errors as $errmsg)
4245  {
4246  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4247  $this->error.=($this->error?', '.$errmsg:$errmsg);
4248  }
4249  $this->db->rollback();
4250  return -1*$error;
4251  }
4252  else
4253  {
4254  $this->error=$this->db->error();
4255  $this->db->rollback();
4256  return -2;
4257  }
4258  }
4259 
4265  function update_total()
4266  {
4267  $this->db->begin();
4268 
4269  // Clean parameters
4270  if (empty($this->total_localtax1)) $this->total_localtax1=0;
4271  if (empty($this->total_localtax2)) $this->total_localtax2=0;
4272 
4273  // Mise a jour ligne en base
4274  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4275  $sql.= " total_ht='".price2num($this->total_ht)."'";
4276  $sql.= ",total_tva='".price2num($this->total_tva)."'";
4277  $sql.= ",total_localtax1='".price2num($this->total_localtax1)."'";
4278  $sql.= ",total_localtax2='".price2num($this->total_localtax2)."'";
4279  $sql.= ",total_ttc='".price2num($this->total_ttc)."'";
4280  $sql.= " WHERE rowid = ".$this->rowid;
4281 
4282  dol_syslog("OrderLine::update_total", LOG_DEBUG);
4283 
4284  $resql=$this->db->query($sql);
4285  if ($resql)
4286  {
4287  $this->db->commit();
4288  return 1;
4289  }
4290  else
4291  {
4292  $this->error=$this->db->error();
4293  $this->db->rollback();
4294  return -2;
4295  }
4296  }
4297 }
4298 
getNbOfProductsLines()
Return number of line with type product.
Class to manage stock movements.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it's its name (generic function)
const STATUS_CLOSED
Closed (Sent, billed or not)
getLinesArray()
Create an array of order lines.
static getIdFromCode(&$db, $code)
Get id of currency from code.
set_date($user, $date, $notrigger=0)
Set the order date.
cancel($idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
set_draft($user, $idwarehouse=-1)
Set draft status.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
Delete all links between an object $this.
LibStatut($statut, $billed, $mode, $donotshowbilled=0)
Return label of status.
getNextNumRef($soc)
Returns the reference to the following non used Order depending on the active numbering module define...
update(User $user, $notrigger=0)
Update the line object into db.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $info_bits=0, $fk_remise_except=0, $price_base_type='HT', $pu_ttc=0, $date_start='', $date_end='', $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0)
Add an order line into database (linked to product/service or not)
getNbOfServicesLines()
Return number of line with type service.
hasDelay()
Is the customer order delayed?
demand_reason($demand_reason_id, $notrigger=0)
Update order demand_reason.
createFromClone($socid=0)
Load an object from its id and create a new one in database.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
__construct($db)
Constructor.
Class to manage products or services.
set_remise($user, $remise, $notrigger=0)
Applique une remise relative.
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1283
loadExpeditions($filtre_statut=-1)
Load array this->expeditions of lines of shipments with nb of products sent for each order line Note:...
Class to manage Dolibarr users.
Definition: user.class.php:39
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
Class to manage Dolibarr database access.
initAsSpecimen()
Initialise an instance with random values.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
classifyBilled(User $user, $notrigger=0)
Classify the order as invoiced.
valid($user, $idwarehouse=0, $notrigger=0)
Validate order.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
const STATUS_SHIPMENTONPROCESS
Shipment on process.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
availability($availability_id, $notrigger=0)
Update delivery delay.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
set_date_livraison($user, $date_livraison, $notrigger=0)
Set the planned delivery date.
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.
nb_expedition()
Returns a array with expeditions lines number.
info($id)
Charge les informations d'ordre info dans l'objet commande.
set_remise_absolue($user, $remise, $notrigger=0)
Applique une remise absolue.
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
Class to manage order lines.
$table_ref_field
{}
stock_array($filtre_statut=self::STATUS_CANCELED)
Return a array with the pending stock by product.
const STOCK_NOT_ENOUGH_FOR_ORDER
ERR Not enough stock.
Class to manage standard extra fields.
create($user, $notrigger=0)
Create order Note that this->ref can be set or empty.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
insert_discount($idremise)
Adding line of fixed discount in the order in DB.
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.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
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)...
insert($user=null, $notrigger=0)
Insert line into database.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
Class to manage shipments.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
Class to manage customers orders.
const STATUS_DRAFT
Draft status.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1234
getEntity($element, $shared=1, $forceentity=null)
Get list of entity id to use.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
deleteExtraFields()
Delete all extra fields values for the current object.
set_reopen($user)
Tag the order as validated (opened) Function used when order is reopend after being closed...
deleteline($user=null, $lineid=0)
Delete an order line.
getLibStatut($mode)
Return status label of Order.
dol_now($mode='gmt')
Return date for now.
fetch($id, $ref='', $ref_ext='', $ref_int='')
Get object and lines from database.
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...
Superclass for orders classes.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller= '', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0)
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:85
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
dol_print_date($time, $format='', $tzoutput='tzserver', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
const STATUS_VALIDATED
Validated status.
print
Draft customers invoices.
Definition: index.php:91
Superclass for orders classes.
cloture($user, $notrigger=0)
Close order.
fetch_lines($only_product=0)
Load array lines.
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
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
getLabelSource()
Return source label of order.
classifyUnBilled()
Classify the order as not invoiced.
Class to manage absolute discounts.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $fk_unit=null, $pu_ht_devise=0, $notrigger=0)
Update a line in database.
add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
Add line into array $this->client must be loaded.
call_trigger($trigger_name, $user)
Call trigger based on this instance.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
update(User $user, $notrigger=0)
Update database.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
createFromProposal($object, User $user)
Load an object from a proposal and create a new order into database.
update_total()
Update totals of order into database.
price2num($amount, $rounding='', $alreadysqlnb=0)
Function that return a number with universal decimal format (decimal separator is '...
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
Return clicable link of object (with eventually picto)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
Return list of orders (eventuelly filtered on a user) into an array.
fetch($rowid)
Load line order.
showDelay()
Show the customer delayed info.
getNbOfShipments()
Count numbe rof shipments for this order.
__construct($db)
Constructor.
static getIdAndTxFromCode(&$db, $code, $date_document='')
Get id and rate of currency from code.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer ref.
classer_facturee()
Classify the order as invoiced.
const STATUS_CANCELED
Canceled status.