dolibarr  x.y.z
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@inodbox.com>
5  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6  * Copyright (C) 2010-2020 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) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
13  * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
14  * Copyright (C) 2021-2022 Frédéric France <frederic.france@netlogic.fr>
15  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program. If not, see <https://www.gnu.org/licenses/>.
29  */
30 
36 include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
37 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
38 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
39 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
40 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
41 
42 
46 class Commande extends CommonOrder
47 {
51  public $element = 'commande';
52 
56  public $table_element = 'commande';
57 
61  public $table_element_line = 'commandedet';
62 
66  public $class_element_line = 'OrderLine';
67 
71  public $fk_element = 'fk_commande';
72 
76  public $picto = 'order';
77 
82  public $ismultientitymanaged = 1;
83 
88  public $restrictiononfksoc = 1;
89 
93  protected $table_ref_field = 'ref';
94 
98  public $socid;
99 
103  public $ref_client;
104 
108  public $contactid;
109 
114  public $statut;
115 
119  public $billed;
120 
124  public $brouillon;
125 
129  public $cond_reglement_code;
130 
134  public $deposit_percent;
135 
139  public $fk_account;
140 
144  public $mode_reglement;
145 
149  public $mode_reglement_id;
150 
154  public $mode_reglement_code;
155 
160  public $availability_id;
161 
166  public $availability_code;
167 
172  public $availability;
173 
177  public $demand_reason_id;
178 
182  public $demand_reason_code;
183 
187  public $date;
188 
194  public $date_commande;
195 
201  public $date_livraison;
202 
206  public $delivery_date;
207 
211  public $fk_remise_except;
212 
213  public $remise_percent;
214  public $remise_absolue;
215  public $info_bits;
216  public $rang;
217  public $special_code;
218  public $source; // Order mode. How we received order (by phone, by email, ...)
219 
223  public $warehouse_id;
224 
225  public $extraparams = array();
226 
227  public $linked_objects = array();
228 
232  public $user_author_id;
233 
237  public $user_valid;
238 
242  public $lines = array();
243 
244  // Multicurrency
248  public $fk_multicurrency;
249 
253  public $multicurrency_code;
254  public $multicurrency_tx;
255  public $multicurrency_total_ht;
256  public $multicurrency_total_tva;
257  public $multicurrency_total_ttc;
258 
262  public $pos_source;
263 
267  public $expeditions;
268 
272  public $online_payment_url;
273 
274 
299  // BEGIN MODULEBUILDER PROPERTIES
303  public $fields = array(
304  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
305  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
306  'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25),
307  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>26),
308  'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>28),
309  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'notnull'=>1, 'position'=>20),
310  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>25),
311  'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>1, 'position'=>60),
312  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
313  'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
314  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
315  'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
316  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
317  //'amount_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
318  'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
319  'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
320  //'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
321  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
322  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
323  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
324  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
325  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
326  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>150),
327  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155),
328  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>160),
329  //'facture' =>array('type'=>'tinyint(4)', 'label'=>'ParentInvoice', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
330  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'$conf->banque->enabled', 'visible'=>-1, 'position'=>170),
331  'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
332  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
333  'deposit_percent' =>array('type'=>'varchar(63)', 'label'=>'DepositPercent', 'enabled'=>1, 'visible'=>-1, 'position'=>181),
334  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
335  'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
336  'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
337  'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>'$conf->stock->enabled', 'visible'=>-1, 'position'=>200),
338  'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
339  'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
340  //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
341  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
342  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>230),
343  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>235),
344  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
345  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>245),
346  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
347  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
348  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>260, 'isameasure'=>1),
349  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>265, 'isameasure'=>1),
350  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>270),
351  'module_source' =>array('type'=>'varchar(32)', 'label'=>'POSModule', 'enabled'=>1, 'visible'=>-1, 'position'=>275),
352  'pos_source' =>array('type'=>'varchar(32)', 'label'=>'POSTerminal', 'enabled'=>1, 'visible'=>-1, 'position'=>280),
353  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>300),
354  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>302),
355  'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>304),
356  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>306),
357  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
358  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
359  );
360  // END MODULEBUILDER PROPERTIES
361 
366 
370  const STATUS_CANCELED = -1;
374  const STATUS_DRAFT = 0;
378  const STATUS_VALIDATED = 1;
383  const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
384 
388  const STATUS_CLOSED = 3;
389 
390 
396  public function __construct($db)
397  {
398  $this->db = $db;
399  }
400 
408  public function getNextNumRef($soc)
409  {
410  global $langs, $conf;
411  $langs->load("order");
412 
413  if (!empty($conf->global->COMMANDE_ADDON)) {
414  $mybool = false;
415 
416  $file = $conf->global->COMMANDE_ADDON.".php";
417  $classname = $conf->global->COMMANDE_ADDON;
418 
419  // Include file with class
420  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
421  foreach ($dirmodels as $reldir) {
422  $dir = dol_buildpath($reldir."core/modules/commande/");
423 
424  // Load file with numbering class (if found)
425  $mybool |= @include_once $dir.$file;
426  }
427 
428  if ($mybool === false) {
429  dol_print_error('', "Failed to include file ".$file);
430  return '';
431  }
432 
433  $obj = new $classname();
434  $numref = $obj->getNextValue($soc, $this);
435 
436  if ($numref != "") {
437  return $numref;
438  } else {
439  $this->error = $obj->error;
440  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
441  return "";
442  }
443  } else {
444  print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
445  return "";
446  }
447  }
448 
449 
458  public function valid($user, $idwarehouse = 0, $notrigger = 0)
459  {
460  global $conf, $langs;
461 
462  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
463 
464  $error = 0;
465 
466  // Protection
467  if ($this->statut == self::STATUS_VALIDATED) {
468  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
469  return 0;
470  }
471 
472  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
473  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
474  $this->error = 'NotEnoughPermissions';
475  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
476  return -1;
477  }
478 
479  $now = dol_now();
480 
481  $this->db->begin();
482 
483  // Definition du nom de module de numerotation de commande
484  $soc = new Societe($this->db);
485  $soc->fetch($this->socid);
486 
487  // Class of company linked to order
488  $result = $soc->set_as_client();
489 
490  // Define new ref
491  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
492  $num = $this->getNextNumRef($soc);
493  } else {
494  $num = $this->ref;
495  }
496  $this->newref = dol_sanitizeFileName($num);
497 
498  // Validate
499  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
500  $sql .= " SET ref = '".$this->db->escape($num)."',";
501  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
502  $sql .= " date_valid='".$this->db->idate($now)."',";
503  $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
504  $sql .= " fk_user_modif = ".((int) $user->id);
505  $sql .= " WHERE rowid = ".((int) $this->id);
506 
507  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
508  $resql = $this->db->query($sql);
509  if (!$resql) {
510  dol_print_error($this->db);
511  $this->error = $this->db->lasterror();
512  $error++;
513  }
514 
515  if (!$error) {
516  // If stock is incremented on validate order, we must increment it
517  if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
518  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
519  $langs->load("agenda");
520 
521  // Loop on each line
522  $cpt = count($this->lines);
523  for ($i = 0; $i < $cpt; $i++) {
524  if ($this->lines[$i]->fk_product > 0) {
525  $mouvP = new MouvementStock($this->db);
526  $mouvP->origin = &$this;
527  $mouvP->setOrigin($this->element, $this->id);
528  // We decrement stock of product (and sub-products)
529  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
530  if ($result < 0) {
531  $error++;
532  $this->error = $mouvP->error;
533  }
534  }
535  if ($error) {
536  break;
537  }
538  }
539  }
540  }
541 
542  if (!$error && !$notrigger) {
543  // Call trigger
544  $result = $this->call_trigger('ORDER_VALIDATE', $user);
545  if ($result < 0) {
546  $error++;
547  }
548  // End call triggers
549  }
550 
551  if (!$error) {
552  $this->oldref = $this->ref;
553 
554  // Rename directory if dir was a temporary ref
555  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
556  // Now we rename also files into index
557  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'commande/".$this->db->escape($this->newref)."'";
558  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
559  $resql = $this->db->query($sql);
560  if (!$resql) {
561  $error++; $this->error = $this->db->lasterror();
562  }
563 
564  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
565  $oldref = dol_sanitizeFileName($this->ref);
566  $newref = dol_sanitizeFileName($num);
567  $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
568  $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
569  if (!$error && file_exists($dirsource)) {
570  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
571 
572  if (@rename($dirsource, $dirdest)) {
573  dol_syslog("Rename ok");
574  // Rename docs starting with $oldref with $newref
575  $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
576  foreach ($listoffiles as $fileentry) {
577  $dirsource = $fileentry['name'];
578  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
579  $dirsource = $fileentry['path'].'/'.$dirsource;
580  $dirdest = $fileentry['path'].'/'.$dirdest;
581  @rename($dirsource, $dirdest);
582  }
583  }
584  }
585  }
586  }
587 
588  // Set new ref and current status
589  if (!$error) {
590  $this->ref = $num;
591  $this->statut = self::STATUS_VALIDATED;
592  $this->brouillon = 0;
593  }
594 
595  if (!$error) {
596  $this->db->commit();
597  return 1;
598  } else {
599  $this->db->rollback();
600  return -1;
601  }
602  }
603 
604  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
612  public function setDraft($user, $idwarehouse = -1)
613  {
614  //phpcs:enable
615  global $conf, $langs;
616 
617  $error = 0;
618 
619  // Protection
620  if ($this->statut <= self::STATUS_DRAFT) {
621  return 0;
622  }
623 
624  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
625  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
626  $this->error = 'Permission denied';
627  return -1;
628  }
629 
630  dol_syslog(__METHOD__, LOG_DEBUG);
631 
632  $this->db->begin();
633 
634  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
635  $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
636  $sql .= " fk_user_modif = ".((int) $user->id);
637  $sql .= " WHERE rowid = ".((int) $this->id);
638 
639  if ($this->db->query($sql)) {
640  if (!$error) {
641  $this->oldcopy = clone $this;
642  }
643 
644  // If stock is decremented on validate order, we must reincrement it
645  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
646  $result = 0;
647 
648  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
649  $langs->load("agenda");
650 
651  $num = count($this->lines);
652  for ($i = 0; $i < $num; $i++) {
653  if ($this->lines[$i]->fk_product > 0) {
654  $mouvP = new MouvementStock($this->db);
655  $mouvP->origin = &$this;
656  $mouvP->setOrigin($this->element, $this->id);
657  // We increment stock of product (and sub-products)
658  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
659  if ($result < 0) {
660  $error++; $this->error = $mouvP->error; break;
661  }
662  }
663  }
664  }
665 
666  if (!$error) {
667  // Call trigger
668  $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
669  if ($result < 0) {
670  $error++;
671  }
672  }
673 
674  if (!$error) {
675  $this->statut = self::STATUS_DRAFT;
676  $this->db->commit();
677  return 1;
678  } else {
679  $this->db->rollback();
680  return -1;
681  }
682  } else {
683  $this->error = $this->db->error();
684  $this->db->rollback();
685  return -1;
686  }
687  }
688 
689 
690  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
698  public function set_reopen($user)
699  {
700  // phpcs:enable
701  $error = 0;
702 
703  if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
704  dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
705  return 0;
706  }
707 
708  $this->db->begin();
709 
710  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
711  $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
712  $sql .= " fk_user_modif = ".((int) $user->id);
713  $sql .= " WHERE rowid = ".((int) $this->id);
714 
715  dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
716  $resql = $this->db->query($sql);
717  if ($resql) {
718  // Call trigger
719  $result = $this->call_trigger('ORDER_REOPEN', $user);
720  if ($result < 0) {
721  $error++;
722  }
723  // End call triggers
724  } else {
725  $error++;
726  $this->error = $this->db->lasterror();
727  dol_print_error($this->db);
728  }
729 
730  if (!$error) {
731  $this->statut = self::STATUS_VALIDATED;
732  $this->billed = 0;
733 
734  $this->db->commit();
735  return 1;
736  } else {
737  foreach ($this->errors as $errmsg) {
738  dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
739  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
740  }
741  $this->db->rollback();
742  return -1 * $error;
743  }
744  }
745 
753  public function cloture($user, $notrigger = 0)
754  {
755  global $conf;
756 
757  $error = 0;
758 
759  $usercanclose = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
760  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->close)));
761 
762  if ($usercanclose) {
763  if ($this->statut == self::STATUS_CLOSED) {
764  return 0;
765  }
766  $this->db->begin();
767 
768  $now = dol_now();
769 
770  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
771  $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
772  $sql .= ' fk_user_cloture = '.((int) $user->id).',';
773  $sql .= " date_cloture = '".$this->db->idate($now)."',";
774  $sql .= " fk_user_modif = ".((int) $user->id);
775  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
776 
777  if ($this->db->query($sql)) {
778  if (!$notrigger) {
779  // Call trigger
780  $result = $this->call_trigger('ORDER_CLOSE', $user);
781  if ($result < 0) {
782  $error++;
783  }
784  // End call triggers
785  }
786 
787  if (!$error) {
788  $this->statut = self::STATUS_CLOSED;
789 
790  $this->db->commit();
791  return 1;
792  } else {
793  $this->db->rollback();
794  return -1;
795  }
796  } else {
797  $this->error = $this->db->lasterror();
798 
799  $this->db->rollback();
800  return -1;
801  }
802  }
803  return 0;
804  }
805 
813  public function cancel($idwarehouse = -1)
814  {
815  global $conf, $user, $langs;
816 
817  $error = 0;
818 
819  $this->db->begin();
820 
821  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
822  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
823  $sql .= " fk_user_modif = ".((int) $user->id);
824  $sql .= " WHERE rowid = ".((int) $this->id);
825  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
826 
827  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
828  if ($this->db->query($sql)) {
829  // If stock is decremented on validate order, we must reincrement it
830  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
831  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
832  $langs->load("agenda");
833 
834  $num = count($this->lines);
835  for ($i = 0; $i < $num; $i++) {
836  if ($this->lines[$i]->fk_product > 0) {
837  $mouvP = new MouvementStock($this->db);
838  $mouvP->setOrigin($this->element, $this->id);
839  // We increment stock of product (and sub-products)
840  $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
841  if ($result < 0) {
842  $error++;
843  $this->error = $mouvP->error;
844  break;
845  }
846  }
847  }
848  }
849 
850  if (!$error) {
851  // Call trigger
852  $result = $this->call_trigger('ORDER_CANCEL', $user);
853  if ($result < 0) {
854  $error++;
855  }
856  // End call triggers
857  }
858 
859  if (!$error) {
860  $this->statut = self::STATUS_CANCELED;
861  $this->db->commit();
862  return 1;
863  } else {
864  foreach ($this->errors as $errmsg) {
865  dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
866  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
867  }
868  $this->db->rollback();
869  return -1 * $error;
870  }
871  } else {
872  $this->error = $this->db->error();
873  $this->db->rollback();
874  return -1;
875  }
876  }
877 
886  public function create($user, $notrigger = 0)
887  {
888  global $conf, $langs, $mysoc;
889  $error = 0;
890 
891  // Clean parameters
892  $this->brouillon = 1; // set command as draft
893 
894  // Set tmp vars
895  $date = ($this->date_commande ? $this->date_commande : $this->date);
896  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
897 
898  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
899  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
900  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
901  } else {
902  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
903  }
904  if (empty($this->fk_multicurrency)) {
905  $this->multicurrency_code = $conf->currency;
906  $this->fk_multicurrency = 0;
907  $this->multicurrency_tx = 1;
908  }
909 
910  dol_syslog(get_class($this)."::create user=".$user->id);
911 
912  // Check parameters
913  if (!empty($this->ref)) { // We check that ref is not already used
914  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
915  if ($result > 0) {
916  $this->error = 'ErrorRefAlreadyExists';
917  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
918  $this->db->rollback();
919  return -1;
920  }
921  }
922 
923  $soc = new Societe($this->db);
924  $result = $soc->fetch($this->socid);
925  if ($result < 0) {
926  $this->error = "Failed to fetch company";
927  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
928  return -2;
929  }
930  if (!empty($conf->global->ORDER_REQUIRE_SOURCE) && $this->source < 0) {
931  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
932  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
933  return -1;
934  }
935 
936  $now = dol_now();
937 
938  $this->db->begin();
939 
940  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
941  $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
942  $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
943  $sql .= ", fk_shipping_method";
944  $sql .= ", fk_warehouse";
945  $sql .= ", remise_absolue, remise_percent";
946  $sql .= ", fk_incoterms, location_incoterms";
947  $sql .= ", entity, module_source, pos_source";
948  $sql .= ", fk_multicurrency";
949  $sql .= ", multicurrency_code";
950  $sql .= ", multicurrency_tx";
951  $sql .= ")";
952  $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
953  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
954  $sql .= ", '".$this->db->idate($date)."'";
955  $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
956  $sql .= ", '".$this->db->escape($this->note_private)."'";
957  $sql .= ", '".$this->db->escape($this->note_public)."'";
958  $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
959  $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
960  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
961  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
962  $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
963  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
964  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
965  $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
966  $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
967  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
968  $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
969  $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
970  $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
971  $sql .= ", ".($this->remise_absolue > 0 ? $this->db->escape($this->remise_absolue) : 'NULL');
972  $sql .= ", ".($this->remise_percent > 0 ? $this->db->escape($this->remise_percent) : 0);
973  $sql .= ", ".(int) $this->fk_incoterms;
974  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
975  $sql .= ", ".setEntity($this);
976  $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
977  $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
978  $sql .= ", ".(int) $this->fk_multicurrency;
979  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
980  $sql .= ", ".(float) $this->multicurrency_tx;
981  $sql .= ")";
982 
983  dol_syslog(get_class($this)."::create", LOG_DEBUG);
984  $resql = $this->db->query($sql);
985  if ($resql) {
986  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
987 
988  if ($this->id) {
989  $fk_parent_line = 0;
990  $num = count($this->lines);
991 
992  /*
993  * Insert products details into db
994  */
995  for ($i = 0; $i < $num; $i++) {
996  $line = $this->lines[$i];
997 
998  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
999  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1000  if (!is_object($line)) {
1001  $line = (object) $line;
1002  }
1003 
1004  // Reset fk_parent_line for no child products and special product
1005  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1006  $fk_parent_line = 0;
1007  }
1008 
1009  // Complete vat rate with code
1010  $vatrate = $line->tva_tx;
1011  if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1012  $vatrate .= ' ('.$line->vat_src_code.')';
1013  }
1014 
1015  if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1016  $originid = $line->origin_id;
1017  $origintype = $line->origin;
1018  } else {
1019  $originid = $line->id;
1020  $origintype = $this->element;
1021  }
1022 
1023  // ref_ext
1024  if (empty($line->ref_ext)) {
1025  $line->ref_ext = '';
1026  }
1027 
1028  $result = $this->addline(
1029  $line->desc,
1030  $line->subprice,
1031  $line->qty,
1032  $vatrate,
1033  $line->localtax1_tx,
1034  $line->localtax2_tx,
1035  $line->fk_product,
1036  $line->remise_percent,
1037  $line->info_bits,
1038  $line->fk_remise_except,
1039  'HT',
1040  0,
1041  $line->date_start,
1042  $line->date_end,
1043  $line->product_type,
1044  $line->rang,
1045  $line->special_code,
1046  $fk_parent_line,
1047  $line->fk_fournprice,
1048  $line->pa_ht,
1049  $line->label,
1050  $line->array_options,
1051  $line->fk_unit,
1052  $origintype,
1053  $originid,
1054  0,
1055  $line->ref_ext,
1056  1
1057  );
1058  if ($result < 0) {
1059  if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1060  $this->error = $this->db->lasterror();
1061  $this->errors[] = $this->error;
1062  dol_print_error($this->db);
1063  }
1064  $this->db->rollback();
1065  return -1;
1066  }
1067  // Defined the new fk_parent_line
1068  if ($result > 0 && $line->product_type == 9) {
1069  $fk_parent_line = $result;
1070  }
1071  }
1072 
1073  $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.
1074 
1075  // update ref
1076  $initialref = '(PROV'.$this->id.')';
1077  if (!empty($this->ref)) {
1078  $initialref = $this->ref;
1079  }
1080 
1081  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1082  if ($this->db->query($sql)) {
1083  $this->ref = $initialref;
1084 
1085  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1086  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1087  }
1088 
1089  // Add object linked
1090  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1091  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1092  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, ...))
1093  foreach ($tmp_origin_id as $origin_id) {
1094  $ret = $this->add_object_linked($origin, $origin_id);
1095  if (!$ret) {
1096  $this->error = $this->db->lasterror();
1097  $error++;
1098  }
1099  }
1100  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1101  {
1102  $origin_id = $tmp_origin_id;
1103  $ret = $this->add_object_linked($origin, $origin_id);
1104  if (!$ret) {
1105  $this->error = $this->db->lasterror();
1106  $error++;
1107  }
1108  }
1109  }
1110  }
1111 
1112  if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1113  $originforcontact = $this->origin;
1114  $originidforcontact = $this->origin_id;
1115  if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1116  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1117  $exp = new Expedition($this->db);
1118  $exp->fetch($this->origin_id);
1119  $exp->fetchObjectLinked();
1120  if (count($exp->linkedObjectsIds['commande']) > 0) {
1121  foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1122  $originforcontact = 'commande';
1123  if (is_object($value)) {
1124  $originidforcontact = $value->id;
1125  } else {
1126  $originidforcontact = $value;
1127  }
1128  break; // We take first one
1129  }
1130  }
1131  }
1132 
1133  $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";
1134  $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1135 
1136  $resqlcontact = $this->db->query($sqlcontact);
1137  if ($resqlcontact) {
1138  while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1139  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1140  $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
1141  }
1142  } else {
1143  dol_print_error($resqlcontact);
1144  }
1145  }
1146 
1147  if (!$error) {
1148  $result = $this->insertExtraFields();
1149  if ($result < 0) {
1150  $error++;
1151  }
1152  }
1153 
1154  if (!$error && !$notrigger) {
1155  // Call trigger
1156  $result = $this->call_trigger('ORDER_CREATE', $user);
1157  if ($result < 0) {
1158  $error++;
1159  }
1160  // End call triggers
1161  }
1162 
1163  if (!$error) {
1164  $this->db->commit();
1165  return $this->id;
1166  } else {
1167  $this->db->rollback();
1168  return -1 * $error;
1169  }
1170  } else {
1171  $this->error = $this->db->lasterror();
1172  $this->db->rollback();
1173  return -1;
1174  }
1175  }
1176  } else {
1177  dol_print_error($this->db);
1178  $this->db->rollback();
1179  return -1;
1180  }
1181  }
1182 
1183 
1191  public function createFromClone(User $user, $socid = 0)
1192  {
1193  global $conf, $user, $hookmanager;
1194 
1195  $error = 0;
1196 
1197  $this->db->begin();
1198 
1199  // get lines so they will be clone
1200  foreach ($this->lines as $line) {
1201  $line->fetch_optionals();
1202  }
1203 
1204  // Load source object
1205  $objFrom = clone $this;
1206 
1207  // Change socid if needed
1208  if (!empty($socid) && $socid != $this->socid) {
1209  $objsoc = new Societe($this->db);
1210 
1211  if ($objsoc->fetch($socid) > 0) {
1212  $this->socid = $objsoc->id;
1213  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1214  $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1215  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1216  $this->fk_project = 0;
1217  $this->fk_delivery_address = 0;
1218  }
1219 
1220  // TODO Change product price if multi-prices
1221  }
1222 
1223  $this->id = 0;
1224  $this->ref = '';
1225  $this->statut = self::STATUS_DRAFT;
1226 
1227  // Clear fields
1228  $this->user_author_id = $user->id;
1229  $this->user_valid = 0; // deprecated
1230  $this->user_validation_id = 0;
1231  $this->date = dol_now();
1232  $this->date_commande = dol_now();
1233  $this->date_creation = '';
1234  $this->date_validation = '';
1235  if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) {
1236  $this->ref_client = '';
1237  }
1238 
1239  // Do not clone ref_ext
1240  $num = count($this->lines);
1241  for ($i = 0; $i < $num; $i++) {
1242  $this->lines[$i]->ref_ext = '';
1243  }
1244 
1245  // Create clone
1246  $this->context['createfromclone'] = 'createfromclone';
1247  $result = $this->create($user);
1248  if ($result < 0) {
1249  $error++;
1250  }
1251 
1252  if (!$error) {
1253  // copy internal contacts
1254  if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1255  $error++;
1256  }
1257  }
1258 
1259  if (!$error) {
1260  // copy external contacts if same company
1261  if ($this->socid == $objFrom->socid) {
1262  if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1263  $error++;
1264  }
1265  }
1266  }
1267 
1268  if (!$error) {
1269  // Hook of thirdparty module
1270  if (is_object($hookmanager)) {
1271  $parameters = array('objFrom'=>$objFrom);
1272  $action = '';
1273  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1274  if ($reshook < 0) {
1275  $error++;
1276  }
1277  }
1278  }
1279 
1280  unset($this->context['createfromclone']);
1281 
1282  // End
1283  if (!$error) {
1284  $this->db->commit();
1285  return $this->id;
1286  } else {
1287  $this->db->rollback();
1288  return -1;
1289  }
1290  }
1291 
1292 
1300  public function createFromProposal($object, User $user)
1301  {
1302  global $conf, $hookmanager;
1303 
1304  dol_include_once('/multicurrency/class/multicurrency.class.php');
1305  dol_include_once('/core/class/extrafields.class.php');
1306 
1307  $error = 0;
1308 
1309 
1310  $this->date_commande = dol_now();
1311  $this->source = 0;
1312 
1313  $num = count($object->lines);
1314  for ($i = 0; $i < $num; $i++) {
1315  $line = new OrderLine($this->db);
1316 
1317  $line->libelle = $object->lines[$i]->libelle;
1318  $line->label = $object->lines[$i]->label;
1319  $line->desc = $object->lines[$i]->desc;
1320  $line->price = $object->lines[$i]->price;
1321  $line->subprice = $object->lines[$i]->subprice;
1322  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1323  $line->tva_tx = $object->lines[$i]->tva_tx;
1324  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1325  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1326  $line->qty = $object->lines[$i]->qty;
1327  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1328  $line->remise_percent = $object->lines[$i]->remise_percent;
1329  $line->fk_product = $object->lines[$i]->fk_product;
1330  $line->info_bits = $object->lines[$i]->info_bits;
1331  $line->product_type = $object->lines[$i]->product_type;
1332  $line->rang = $object->lines[$i]->rang;
1333  $line->special_code = $object->lines[$i]->special_code;
1334  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1335  $line->fk_unit = $object->lines[$i]->fk_unit;
1336 
1337  $line->date_start = $object->lines[$i]->date_start;
1338  $line->date_end = $object->lines[$i]->date_end;
1339 
1340  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1341  $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);
1342  $line->pa_ht = $marginInfos[0];
1343  $line->marge_tx = $marginInfos[1];
1344  $line->marque_tx = $marginInfos[2];
1345 
1346  // get extrafields from original line
1347  $object->lines[$i]->fetch_optionals();
1348  foreach ($object->lines[$i]->array_options as $options_key => $value) {
1349  $line->array_options[$options_key] = $value;
1350  }
1351 
1352  $this->lines[$i] = $line;
1353  }
1354 
1355  $this->entity = $object->entity;
1356  $this->socid = $object->socid;
1357  $this->fk_project = $object->fk_project;
1358  $this->cond_reglement_id = $object->cond_reglement_id;
1359  $this->deposit_percent = $object->deposit_percent;
1360  $this->mode_reglement_id = $object->mode_reglement_id;
1361  $this->fk_account = $object->fk_account;
1362  $this->availability_id = $object->availability_id;
1363  $this->demand_reason_id = $object->demand_reason_id;
1364  $this->date_livraison = $object->date_livraison; // deprecated
1365  $this->delivery_date = $object->date_livraison;
1366  $this->shipping_method_id = $object->shipping_method_id;
1367  $this->warehouse_id = $object->warehouse_id;
1368  $this->fk_delivery_address = $object->fk_delivery_address;
1369  $this->contact_id = $object->contact_id;
1370  $this->ref_client = $object->ref_client;
1371 
1372  if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) {
1373  $this->note_private = $object->note_private;
1374  $this->note_public = $object->note_public;
1375  }
1376 
1377  $this->origin = $object->element;
1378  $this->origin_id = $object->id;
1379 
1380  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1381  if (!empty($conf->multicurrency->enabled)) {
1382  if (!empty($object->multicurrency_code)) {
1383  $this->multicurrency_code = $object->multicurrency_code;
1384  }
1385  if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($object->multicurrency_tx)) {
1386  $this->multicurrency_tx = $object->multicurrency_tx;
1387  }
1388 
1389  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1390  $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1391  $this->fk_multicurrency = $tmparray[0];
1392  $this->multicurrency_tx = $tmparray[1];
1393  } else {
1394  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1395  }
1396  if (empty($this->fk_multicurrency)) {
1397  $this->multicurrency_code = $conf->currency;
1398  $this->fk_multicurrency = 0;
1399  $this->multicurrency_tx = 1;
1400  }
1401  }
1402 
1403  // get extrafields from original line
1404  $object->fetch_optionals();
1405 
1406  $e = new ExtraFields($this->db);
1407  $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1408 
1409  foreach ($object->array_options as $options_key => $value) {
1410  if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1411  $this->array_options[$options_key] = $value;
1412  }
1413  }
1414  // Possibility to add external linked objects with hooks
1415  $this->linked_objects[$this->origin] = $this->origin_id;
1416  if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1417  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1418  }
1419 
1420  $ret = $this->create($user);
1421 
1422  if ($ret > 0) {
1423  // Actions hooked (by external module)
1424  $hookmanager->initHooks(array('orderdao'));
1425 
1426  $parameters = array('objFrom'=>$object);
1427  $action = '';
1428  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1429  if ($reshook < 0) {
1430  $error++;
1431  }
1432 
1433  if (!$error) {
1434  // Validate immediatly the order
1435  if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL)) {
1436  $this->fetch($ret);
1437  $this->valid($user);
1438  }
1439  return $ret;
1440  } else {
1441  return -1;
1442  }
1443  } else {
1444  return -1;
1445  }
1446  }
1447 
1448 
1489  public 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, $ref_ext = '', $noupdateafterinsertline = 0)
1490  {
1491  global $mysoc, $conf, $langs, $user;
1492 
1493  $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1494  $logtext .= ", info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, date_start=$date_start";
1495  $logtext .= ", 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, ref_ext=$ref_ext";
1496  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1497 
1498  if ($this->statut == self::STATUS_DRAFT) {
1499  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1500 
1501  // Clean parameters
1502 
1503  if (empty($remise_percent)) {
1504  $remise_percent = 0;
1505  }
1506  if (empty($qty)) {
1507  $qty = 0;
1508  }
1509  if (empty($info_bits)) {
1510  $info_bits = 0;
1511  }
1512  if (empty($rang)) {
1513  $rang = 0;
1514  }
1515  if (empty($txtva)) {
1516  $txtva = 0;
1517  }
1518  if (empty($txlocaltax1)) {
1519  $txlocaltax1 = 0;
1520  }
1521  if (empty($txlocaltax2)) {
1522  $txlocaltax2 = 0;
1523  }
1524  if (empty($fk_parent_line) || $fk_parent_line < 0) {
1525  $fk_parent_line = 0;
1526  }
1527  if (empty($this->fk_multicurrency)) {
1528  $this->fk_multicurrency = 0;
1529  }
1530  if (empty($ref_ext)) {
1531  $ref_ext = '';
1532  }
1533 
1534  $remise_percent = price2num($remise_percent);
1535  $qty = price2num($qty);
1536  $pu_ht = price2num($pu_ht);
1537  $pu_ht_devise = price2num($pu_ht_devise);
1538  $pu_ttc = price2num($pu_ttc);
1539  $pa_ht = price2num($pa_ht);
1540  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1541  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1542  }
1543  $txlocaltax1 = price2num($txlocaltax1);
1544  $txlocaltax2 = price2num($txlocaltax2);
1545  if ($price_base_type == 'HT') {
1546  $pu = $pu_ht;
1547  } else {
1548  $pu = $pu_ttc;
1549  }
1550  $label = trim($label);
1551  $desc = trim($desc);
1552 
1553  // Check parameters
1554  if ($type < 0) {
1555  return -1;
1556  }
1557 
1558  if ($date_start && $date_end && $date_start > $date_end) {
1559  $langs->load("errors");
1560  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1561  return -1;
1562  }
1563 
1564  $this->db->begin();
1565 
1566  $product_type = $type;
1567  if (!empty($fk_product) && $fk_product > 0) {
1568  $product = new Product($this->db);
1569  $result = $product->fetch($fk_product);
1570  $product_type = $product->type;
1571 
1572  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
1573  $langs->load("errors");
1574  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1575  $this->errors[] = $this->error;
1576  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1577  $this->db->rollback();
1579  }
1580  }
1581  // Calcul du total TTC et de la TVA pour la ligne a partir de
1582  // qty, pu, remise_percent et txtva
1583  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1584  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1585 
1586  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1587 
1588  // Clean vat code
1589  $reg = array();
1590  $vat_src_code = '';
1591  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1592  $vat_src_code = $reg[1];
1593  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1594  }
1595 
1596  $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);
1597 
1598  /*var_dump($txlocaltax1);
1599  var_dump($txlocaltax2);
1600  var_dump($localtaxes_type);
1601  var_dump($tabprice);
1602  var_dump($tabprice[9]);
1603  var_dump($tabprice[10]);
1604  exit;*/
1605 
1606  $total_ht = $tabprice[0];
1607  $total_tva = $tabprice[1];
1608  $total_ttc = $tabprice[2];
1609  $total_localtax1 = $tabprice[9];
1610  $total_localtax2 = $tabprice[10];
1611  $pu_ht = $tabprice[3];
1612 
1613  // MultiCurrency
1614  $multicurrency_total_ht = $tabprice[16];
1615  $multicurrency_total_tva = $tabprice[17];
1616  $multicurrency_total_ttc = $tabprice[18];
1617  $pu_ht_devise = $tabprice[19];
1618 
1619  // Rang to use
1620  $ranktouse = $rang;
1621  if ($ranktouse == -1) {
1622  $rangmax = $this->line_max($fk_parent_line);
1623  $ranktouse = $rangmax + 1;
1624  }
1625 
1626  // TODO A virer
1627  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1628  $price = $pu;
1629  $remise = 0;
1630  if ($remise_percent > 0) {
1631  $remise = round(($pu * $remise_percent / 100), 2);
1632  $price = $pu - $remise;
1633  }
1634 
1635  // Insert line
1636  $this->line = new OrderLine($this->db);
1637 
1638  $this->line->context = $this->context;
1639 
1640  $this->line->fk_commande = $this->id;
1641  $this->line->label = $label;
1642  $this->line->desc = $desc;
1643  $this->line->qty = $qty;
1644  $this->line->ref_ext = $ref_ext;
1645 
1646  $this->line->vat_src_code = $vat_src_code;
1647  $this->line->tva_tx = $txtva;
1648  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1649  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1650  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1651  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1652  $this->line->fk_product = $fk_product;
1653  $this->line->product_type = $product_type;
1654  $this->line->fk_remise_except = $fk_remise_except;
1655  $this->line->remise_percent = $remise_percent;
1656  $this->line->subprice = $pu_ht;
1657  $this->line->rang = $ranktouse;
1658  $this->line->info_bits = $info_bits;
1659  $this->line->total_ht = $total_ht;
1660  $this->line->total_tva = $total_tva;
1661  $this->line->total_localtax1 = $total_localtax1;
1662  $this->line->total_localtax2 = $total_localtax2;
1663  $this->line->total_ttc = $total_ttc;
1664  $this->line->special_code = $special_code;
1665  $this->line->origin = $origin;
1666  $this->line->origin_id = $origin_id;
1667  $this->line->fk_parent_line = $fk_parent_line;
1668  $this->line->fk_unit = $fk_unit;
1669 
1670  $this->line->date_start = $date_start;
1671  $this->line->date_end = $date_end;
1672 
1673  $this->line->fk_fournprice = $fk_fournprice;
1674  $this->line->pa_ht = $pa_ht;
1675 
1676  // Multicurrency
1677  $this->line->fk_multicurrency = $this->fk_multicurrency;
1678  $this->line->multicurrency_code = $this->multicurrency_code;
1679  $this->line->multicurrency_subprice = $pu_ht_devise;
1680  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1681  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1682  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1683 
1684  // TODO Ne plus utiliser
1685  $this->line->price = $price;
1686 
1687  if (is_array($array_options) && count($array_options) > 0) {
1688  $this->line->array_options = $array_options;
1689  }
1690 
1691  $result = $this->line->insert($user);
1692  if ($result > 0) {
1693  // Reorder if child line
1694  if (!empty($fk_parent_line)) {
1695  $this->line_order(true, 'DESC');
1696  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1697  $linecount = count($this->lines);
1698  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1699  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1700  }
1701  }
1702 
1703  // Mise a jour informations denormalisees au niveau de la commande meme
1704  if (empty($noupdateafterinsertline)) {
1705  $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.
1706  }
1707 
1708  if ($result > 0) {
1709  $this->db->commit();
1710  return $this->line->id;
1711  } else {
1712  $this->db->rollback();
1713  return -1;
1714  }
1715  } else {
1716  $this->error = $this->line->error;
1717  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1718  $this->db->rollback();
1719  return -2;
1720  }
1721  } else {
1722  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1723  return -3;
1724  }
1725  }
1726 
1727 
1728  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1742  public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1743  {
1744  // phpcs:enable
1745  global $conf, $mysoc;
1746 
1747  if (!$qty) {
1748  $qty = 1;
1749  }
1750 
1751  if ($idproduct > 0) {
1752  $prod = new Product($this->db);
1753  $prod->fetch($idproduct);
1754 
1755  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1756  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1757  if (empty($tva_tx)) {
1758  $tva_npr = 0;
1759  }
1760  $vat_src_code = ''; // May be defined into tva_tx
1761 
1762  $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1763  $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1764 
1765  // multiprix
1766  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1767  $price = $prod->multiprices[$this->thirdparty->price_level];
1768  } else {
1769  $price = $prod->price;
1770  }
1771 
1772  $line = new OrderLine($this->db);
1773 
1774  $line->context = $this->context;
1775 
1776  $line->fk_product = $idproduct;
1777  $line->desc = $prod->description;
1778  $line->qty = $qty;
1779  $line->subprice = $price;
1780  $line->remise_percent = $remise_percent;
1781  $line->vat_src_code = $vat_src_code;
1782  $line->tva_tx = $tva_tx;
1783  $line->localtax1_tx = $localtax1_tx;
1784  $line->localtax2_tx = $localtax2_tx;
1785  $line->ref = $prod->ref;
1786  $line->libelle = $prod->label;
1787  $line->product_desc = $prod->description;
1788  $line->fk_unit = $prod->fk_unit;
1789 
1790  // Save the start and end date of the line in the object
1791  if ($date_start) {
1792  $line->date_start = $date_start;
1793  }
1794  if ($date_end) {
1795  $line->date_end = $date_end;
1796  }
1797 
1798  $this->lines[] = $line;
1799 
1818  }
1819  }
1820 
1821 
1831  public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1832  {
1833  // Check parameters
1834  if (empty($id) && empty($ref) && empty($ref_ext)) {
1835  return -1;
1836  }
1837 
1838  $sql = 'SELECT c.rowid, c.entity, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_user_modif, c.fk_statut';
1839  $sql .= ', c.amount_ht, c.total_ht, c.total_ttc, c.total_tva, c.localtax1 as total_localtax1, c.localtax2 as total_localtax2, c.fk_cond_reglement, c.deposit_percent, c.fk_mode_reglement, c.fk_availability, c.fk_input_reason';
1840  $sql .= ', c.fk_account';
1841  $sql .= ', c.date_commande, c.date_valid, c.tms';
1842  $sql .= ', c.date_livraison as delivery_date';
1843  $sql .= ', c.fk_shipping_method';
1844  $sql .= ', c.fk_warehouse';
1845  $sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1846  $sql .= ', c.note_private, c.note_public, c.ref_client, c.ref_ext, c.model_pdf, c.last_main_doc, c.fk_delivery_address, c.extraparams';
1847  $sql .= ', c.fk_incoterms, c.location_incoterms';
1848  $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1849  $sql .= ", c.module_source, c.pos_source";
1850  $sql .= ", i.libelle as label_incoterms";
1851  $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1852  $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1853  $sql .= ', ca.code as availability_code, ca.label as availability_label';
1854  $sql .= ', dr.code as demand_reason_code';
1855  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1856  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1857  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1858  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1859  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1860  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1861 
1862  if ($id) {
1863  $sql .= " WHERE c.rowid=".((int) $id);
1864  } else {
1865  $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1866  }
1867 
1868  if ($ref) {
1869  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1870  }
1871  if ($ref_ext) {
1872  $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1873  }
1874 
1875  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1876  $result = $this->db->query($sql);
1877  if ($result) {
1878  $obj = $this->db->fetch_object($result);
1879  if ($obj) {
1880  $this->id = $obj->rowid;
1881  $this->entity = $obj->entity;
1882 
1883  $this->ref = $obj->ref;
1884  $this->ref_client = $obj->ref_client;
1885  $this->ref_customer = $obj->ref_client;
1886  $this->ref_ext = $obj->ref_ext;
1887 
1888  $this->socid = $obj->fk_soc;
1889  $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1890 
1891  $this->fk_project = $obj->fk_project;
1892  $this->project = null; // Clear if another value was already set by fetch_projet
1893 
1894  $this->statut = $obj->fk_statut;
1895  $this->status = $obj->fk_statut;
1896 
1897  $this->user_author_id = $obj->fk_user_author;
1898  $this->user_creation_id = $obj->fk_user_author;
1899  $this->user_validation_id = $obj->fk_user_valid;
1900  $this->user_valid = $obj->fk_user_valid; // deprecated
1901  $this->user_modification_id = $obj->fk_user_modif;
1902  $this->user_modification = $obj->fk_user_modif;
1903  $this->total_ht = $obj->total_ht;
1904  $this->total_tva = $obj->total_tva;
1905  $this->total_localtax1 = $obj->total_localtax1;
1906  $this->total_localtax2 = $obj->total_localtax2;
1907  $this->total_ttc = $obj->total_ttc;
1908  $this->date = $this->db->jdate($obj->date_commande);
1909  $this->date_commande = $this->db->jdate($obj->date_commande);
1910  $this->date_creation = $this->db->jdate($obj->date_creation);
1911  $this->date_validation = $this->db->jdate($obj->date_valid);
1912  $this->date_modification = $this->db->jdate($obj->tms);
1913  $this->remise = $obj->remise;
1914  $this->remise_percent = $obj->remise_percent;
1915  $this->remise_absolue = $obj->remise_absolue;
1916  $this->source = $obj->source;
1917  $this->billed = $obj->billed;
1918  $this->note = $obj->note_private; // deprecated
1919  $this->note_private = $obj->note_private;
1920  $this->note_public = $obj->note_public;
1921  $this->model_pdf = $obj->model_pdf;
1922  $this->modelpdf = $obj->model_pdf; // deprecated
1923  $this->last_main_doc = $obj->last_main_doc;
1924  $this->mode_reglement_id = $obj->fk_mode_reglement;
1925  $this->mode_reglement_code = $obj->mode_reglement_code;
1926  $this->mode_reglement = $obj->mode_reglement_libelle;
1927  $this->cond_reglement_id = $obj->fk_cond_reglement;
1928  $this->cond_reglement_code = $obj->cond_reglement_code;
1929  $this->cond_reglement = $obj->cond_reglement_libelle;
1930  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1931  $this->deposit_percent = $obj->deposit_percent;
1932  $this->fk_account = $obj->fk_account;
1933  $this->availability_id = $obj->fk_availability;
1934  $this->availability_code = $obj->availability_code;
1935  $this->availability = $obj->availability_label;
1936  $this->demand_reason_id = $obj->fk_input_reason;
1937  $this->demand_reason_code = $obj->demand_reason_code;
1938  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1939  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1940  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1941  $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1942  $this->fk_delivery_address = $obj->fk_delivery_address;
1943  $this->module_source = $obj->module_source;
1944  $this->pos_source = $obj->pos_source;
1945 
1946  //Incoterms
1947  $this->fk_incoterms = $obj->fk_incoterms;
1948  $this->location_incoterms = $obj->location_incoterms;
1949  $this->label_incoterms = $obj->label_incoterms;
1950 
1951  // Multicurrency
1952  $this->fk_multicurrency = $obj->fk_multicurrency;
1953  $this->multicurrency_code = $obj->multicurrency_code;
1954  $this->multicurrency_tx = $obj->multicurrency_tx;
1955  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1956  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1957  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1958 
1959  $this->extraparams = (array) json_decode($obj->extraparams, true);
1960 
1961  $this->lines = array();
1962 
1963  if ($this->statut == self::STATUS_DRAFT) {
1964  $this->brouillon = 1;
1965  }
1966 
1967  // Retrieve all extrafield
1968  // fetch optionals attributes and labels
1969  $this->fetch_optionals();
1970 
1971  $this->db->free($result);
1972 
1973  // Lines
1974  $result = $this->fetch_lines();
1975  if ($result < 0) {
1976  return -3;
1977  }
1978  return 1;
1979  } else {
1980  $this->error = 'Order with id '.$id.' not found sql='.$sql;
1981  return 0;
1982  }
1983  } else {
1984  $this->error = $this->db->error();
1985  return -1;
1986  }
1987  }
1988 
1989 
1990  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1997  public function insert_discount($idremise)
1998  {
1999  // phpcs:enable
2000  global $langs;
2001 
2002  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2003  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2004 
2005  $this->db->begin();
2006 
2007  $remise = new DiscountAbsolute($this->db);
2008  $result = $remise->fetch($idremise);
2009 
2010  if ($result > 0) {
2011  if ($remise->fk_facture) { // Protection against multiple submission
2012  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2013  $this->db->rollback();
2014  return -5;
2015  }
2016 
2017  $line = new OrderLine($this->db);
2018 
2019  $line->fk_commande = $this->id;
2020  $line->fk_remise_except = $remise->id;
2021  $line->desc = $remise->description; // Description ligne
2022  $line->vat_src_code = $remise->vat_src_code;
2023  $line->tva_tx = $remise->tva_tx;
2024  $line->subprice = -$remise->amount_ht;
2025  $line->price = -$remise->amount_ht;
2026  $line->fk_product = 0; // Id produit predefini
2027  $line->qty = 1;
2028  $line->remise_percent = 0;
2029  $line->rang = -1;
2030  $line->info_bits = 2;
2031 
2032  $line->total_ht = -$remise->amount_ht;
2033  $line->total_tva = -$remise->amount_tva;
2034  $line->total_ttc = -$remise->amount_ttc;
2035 
2036  $result = $line->insert();
2037  if ($result > 0) {
2038  $result = $this->update_price(1);
2039  if ($result > 0) {
2040  $this->db->commit();
2041  return 1;
2042  } else {
2043  $this->db->rollback();
2044  return -1;
2045  }
2046  } else {
2047  $this->error = $line->error;
2048  $this->errors = $line->errors;
2049  $this->db->rollback();
2050  return -2;
2051  }
2052  } else {
2053  $this->db->rollback();
2054  return -2;
2055  }
2056  }
2057 
2058 
2059  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2067  public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2068  {
2069  // phpcs:enable
2070  global $langs, $conf;
2071 
2072  $this->lines = array();
2073 
2074  $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, l.ref_ext,';
2075  $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,';
2076  $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2077  $sql .= ' l.fk_unit,';
2078  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2079  $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch, p.barcode as product_barcode,';
2080  $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2081  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2082  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2083  $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2084  if ($only_product) {
2085  $sql .= ' AND p.fk_product_type = 0';
2086  }
2087  $sql .= ' ORDER BY l.rang, l.rowid';
2088 
2089  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2090  $result = $this->db->query($sql);
2091  if ($result) {
2092  $num = $this->db->num_rows($result);
2093 
2094  $i = 0;
2095  while ($i < $num) {
2096  $objp = $this->db->fetch_object($result);
2097 
2098  $line = new OrderLine($this->db);
2099 
2100  $line->rowid = $objp->rowid;
2101  $line->id = $objp->rowid;
2102  $line->fk_commande = $objp->fk_commande;
2103  $line->commande_id = $objp->fk_commande;
2104  $line->label = $objp->custom_label;
2105  $line->desc = $objp->description;
2106  $line->description = $objp->description; // Description line
2107  $line->product_type = $objp->product_type;
2108  $line->qty = $objp->qty;
2109  $line->ref_ext = $objp->ref_ext;
2110 
2111  $line->vat_src_code = $objp->vat_src_code;
2112  $line->tva_tx = $objp->tva_tx;
2113  $line->localtax1_tx = $objp->localtax1_tx;
2114  $line->localtax2_tx = $objp->localtax2_tx;
2115  $line->localtax1_type = $objp->localtax1_type;
2116  $line->localtax2_type = $objp->localtax2_type;
2117  $line->total_ht = $objp->total_ht;
2118  $line->total_ttc = $objp->total_ttc;
2119  $line->total_tva = $objp->total_tva;
2120  $line->total_localtax1 = $objp->total_localtax1;
2121  $line->total_localtax2 = $objp->total_localtax2;
2122  $line->subprice = $objp->subprice;
2123  $line->fk_remise_except = $objp->fk_remise_except;
2124  $line->remise_percent = $objp->remise_percent;
2125  $line->price = $objp->price;
2126  $line->fk_product = $objp->fk_product;
2127  $line->fk_fournprice = $objp->fk_fournprice;
2128  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2129  $line->pa_ht = $marginInfos[0];
2130  $line->marge_tx = $marginInfos[1];
2131  $line->marque_tx = $marginInfos[2];
2132  $line->rang = $objp->rang;
2133  $line->info_bits = $objp->info_bits;
2134  $line->special_code = $objp->special_code;
2135  $line->fk_parent_line = $objp->fk_parent_line;
2136 
2137  $line->ref = $objp->product_ref;
2138  $line->libelle = $objp->product_label;
2139 
2140  $line->product_ref = $objp->product_ref;
2141  $line->product_label = $objp->product_label;
2142  $line->product_tosell = $objp->product_tosell;
2143  $line->product_tobuy = $objp->product_tobuy;
2144  $line->product_desc = $objp->product_desc;
2145  $line->product_tobatch = $objp->product_tobatch;
2146  $line->product_barcode = $objp->product_barcode;
2147 
2148  $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2149  $line->fk_unit = $objp->fk_unit;
2150 
2151  $line->weight = $objp->weight;
2152  $line->weight_units = $objp->weight_units;
2153  $line->volume = $objp->volume;
2154  $line->volume_units = $objp->volume_units;
2155 
2156  $line->date_start = $this->db->jdate($objp->date_start);
2157  $line->date_end = $this->db->jdate($objp->date_end);
2158 
2159  // Multicurrency
2160  $line->fk_multicurrency = $objp->fk_multicurrency;
2161  $line->multicurrency_code = $objp->multicurrency_code;
2162  $line->multicurrency_subprice = $objp->multicurrency_subprice;
2163  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2164  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2165  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2166 
2167  $line->fetch_optionals();
2168 
2169  // multilangs
2170  if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2171  $tmpproduct = new Product($this->db);
2172  $tmpproduct->fetch($objp->fk_product);
2173  $tmpproduct->getMultiLangs();
2174 
2175  $line->multilangs = $tmpproduct->multilangs;
2176  }
2177 
2178  $this->lines[$i] = $line;
2179 
2180  $i++;
2181  }
2182 
2183  $this->db->free($result);
2184 
2185  return 1;
2186  } else {
2187  $this->error = $this->db->error();
2188  return -3;
2189  }
2190  }
2191 
2192 
2198  public function getNbOfProductsLines()
2199  {
2200  $nb = 0;
2201  foreach ($this->lines as $line) {
2202  if ($line->product_type == 0) {
2203  $nb++;
2204  }
2205  }
2206  return $nb;
2207  }
2208 
2214  public function getNbOfServicesLines()
2215  {
2216  $nb = 0;
2217  foreach ($this->lines as $line) {
2218  if ($line->product_type == 1) {
2219  $nb++;
2220  }
2221  }
2222  return $nb;
2223  }
2224 
2230  public function getNbOfShipments()
2231  {
2232  $nb = 0;
2233 
2234  $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2235  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2236  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2237  $sql .= ' WHERE';
2238  $sql .= ' ed.fk_origin_line = cd.rowid';
2239  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2240  //print $sql;
2241 
2242  dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2243  $resql = $this->db->query($sql);
2244  if ($resql) {
2245  $obj = $this->db->fetch_object($resql);
2246  if ($obj) {
2247  $nb = $obj->nb;
2248  }
2249 
2250  $this->db->free($resql);
2251  return $nb;
2252  } else {
2253  $this->error = $this->db->lasterror();
2254  return -1;
2255  }
2256  }
2257 
2266  public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2267  {
2268  $this->expeditions = array();
2269 
2270  $sql = 'SELECT cd.rowid, cd.fk_product,';
2271  $sql .= ' sum(ed.qty) as qty';
2272  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2273  if ($filtre_statut >= 0) {
2274  $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2275  }
2276  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2277  $sql .= ' WHERE';
2278  if ($filtre_statut >= 0) {
2279  $sql .= ' ed.fk_expedition = e.rowid AND';
2280  }
2281  $sql .= ' ed.fk_origin_line = cd.rowid';
2282  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2283  if ($fk_product > 0) {
2284  $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2285  }
2286  if ($filtre_statut >= 0) {
2287  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2288  }
2289  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2290  //print $sql;
2291 
2292  dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2293  $resql = $this->db->query($sql);
2294  if ($resql) {
2295  $num = $this->db->num_rows($resql);
2296  $i = 0;
2297  while ($i < $num) {
2298  $obj = $this->db->fetch_object($resql);
2299  $this->expeditions[$obj->rowid] = $obj->qty;
2300  $i++;
2301  }
2302  $this->db->free($resql);
2303  return $num;
2304  } else {
2305  $this->error = $this->db->lasterror();
2306  return -1;
2307  }
2308  }
2309 
2310  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2318  public function nb_expedition()
2319  {
2320  // phpcs:enable
2321  $sql = 'SELECT count(*)';
2322  $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2323  $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2324  $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2325  $sql .= " AND el.sourcetype = 'commande'";
2326  $sql .= " AND el.fk_target = e.rowid";
2327  $sql .= " AND el.targettype = 'shipping'";
2328 
2329  $resql = $this->db->query($sql);
2330  if ($resql) {
2331  $row = $this->db->fetch_row($resql);
2332  return $row[0];
2333  } else {
2334  dol_print_error($this->db);
2335  }
2336  }
2337 
2338  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2347  public function stock_array($filtre_statut = self::STATUS_CANCELED)
2348  {
2349  // phpcs:enable
2350  $this->stocks = array();
2351 
2352  // Tableau des id de produit de la commande
2353  $array_of_product = array();
2354 
2355  // Recherche total en stock pour chaque produit
2356  // TODO $array_of_product est défini vide juste au dessus !!
2357  if (count($array_of_product)) {
2358  $sql = "SELECT fk_product, sum(ps.reel) as total";
2359  $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2360  $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2361  $sql .= ' GROUP BY fk_product';
2362  $resql = $this->db->query($sql);
2363  if ($resql) {
2364  $num = $this->db->num_rows($resql);
2365  $i = 0;
2366  while ($i < $num) {
2367  $obj = $this->db->fetch_object($resql);
2368  $this->stocks[$obj->fk_product] = $obj->total;
2369  $i++;
2370  }
2371  $this->db->free($resql);
2372  }
2373  }
2374  return 0;
2375  }
2376 
2385  public function deleteline($user = null, $lineid = 0, $id = 0)
2386  {
2387  if ($this->statut == self::STATUS_DRAFT) {
2388  $this->db->begin();
2389 
2390  // Delete line
2391  $line = new OrderLine($this->db);
2392 
2393  $line->context = $this->context;
2394 
2395  // Load data
2396  $line->fetch($lineid);
2397 
2398  if ($id > 0 && $line->fk_commande != $id) {
2399  $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2400  return -1;
2401  }
2402 
2403  // Memorize previous line for triggers
2404  $staticline = clone $line;
2405  $line->oldline = $staticline;
2406 
2407  if ($line->delete($user) > 0) {
2408  $result = $this->update_price(1);
2409 
2410  if ($result > 0) {
2411  $this->db->commit();
2412  return 1;
2413  } else {
2414  $this->db->rollback();
2415  $this->error = $this->db->lasterror();
2416  return -1;
2417  }
2418  } else {
2419  $this->db->rollback();
2420  $this->error = $line->error;
2421  return -1;
2422  }
2423  } else {
2424  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2425  return -1;
2426  }
2427  }
2428 
2429  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2440  public function set_remise($user, $remise, $notrigger = 0)
2441  {
2442  // phpcs:enable
2443  dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2444  return $this->setDiscount($user, $remise, $notrigger);
2445  }
2446 
2455  public function setDiscount($user, $remise, $notrigger = 0)
2456  {
2457  $remise = trim($remise) ?trim($remise) : 0;
2458 
2459  if ($user->rights->commande->creer) {
2460  $error = 0;
2461 
2462  $this->db->begin();
2463 
2464  $remise = price2num($remise, 2);
2465 
2466  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2467  $sql .= ' SET remise_percent = '.((float) $remise);
2468  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2469 
2470  dol_syslog(__METHOD__, LOG_DEBUG);
2471  $resql = $this->db->query($sql);
2472  if (!$resql) {
2473  $this->errors[] = $this->db->error();
2474  $error++;
2475  }
2476 
2477  if (!$error) {
2478  $this->oldcopy = clone $this;
2479  $this->remise_percent = $remise;
2480  $this->update_price(1);
2481  }
2482 
2483  if (!$notrigger && empty($error)) {
2484  // Call trigger
2485  $result = $this->call_trigger('ORDER_MODIFY', $user);
2486  if ($result < 0) {
2487  $error++;
2488  }
2489  // End call triggers
2490  }
2491 
2492  if (!$error) {
2493  $this->db->commit();
2494  return 1;
2495  } else {
2496  foreach ($this->errors as $errmsg) {
2497  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2498  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2499  }
2500  $this->db->rollback();
2501  return -1 * $error;
2502  }
2503  }
2504  }
2505 
2506 
2507  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2516  public function set_remise_absolue($user, $remise, $notrigger = 0)
2517  {
2518  // phpcs:enable
2519  if (empty($remise)) {
2520  $remise = 0;
2521  }
2522 
2523  $remise = price2num($remise);
2524 
2525  if ($user->rights->commande->creer) {
2526  $error = 0;
2527 
2528  $this->db->begin();
2529 
2530  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2531  $sql .= ' SET remise_absolue = '.((float) $remise);
2532  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2533 
2534  dol_syslog(__METHOD__, LOG_DEBUG);
2535  $resql = $this->db->query($sql);
2536  if (!$resql) {
2537  $this->errors[] = $this->db->error();
2538  $error++;
2539  }
2540 
2541  if (!$error) {
2542  $this->oldcopy = clone $this;
2543  $this->remise_absolue = $remise;
2544  $this->update_price(1);
2545  }
2546 
2547  if (!$notrigger && empty($error)) {
2548  // Call trigger
2549  $result = $this->call_trigger('ORDER_MODIFY', $user);
2550  if ($result < 0) {
2551  $error++;
2552  }
2553  // End call triggers
2554  }
2555 
2556  if (!$error) {
2557  $this->db->commit();
2558  return 1;
2559  } else {
2560  foreach ($this->errors as $errmsg) {
2561  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2562  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2563  }
2564  $this->db->rollback();
2565  return -1 * $error;
2566  }
2567  }
2568  }
2569 
2570 
2571  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2580  public function set_date($user, $date, $notrigger = 0)
2581  {
2582  // phpcs:enable
2583  if ($user->rights->commande->creer) {
2584  $error = 0;
2585 
2586  $this->db->begin();
2587 
2588  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2589  $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2590  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2591 
2592  dol_syslog(__METHOD__, LOG_DEBUG);
2593  $resql = $this->db->query($sql);
2594  if (!$resql) {
2595  $this->errors[] = $this->db->error();
2596  $error++;
2597  }
2598 
2599  if (!$error) {
2600  $this->oldcopy = clone $this;
2601  $this->date = $date;
2602  }
2603 
2604  if (!$notrigger && empty($error)) {
2605  // Call trigger
2606  $result = $this->call_trigger('ORDER_MODIFY', $user);
2607  if ($result < 0) {
2608  $error++;
2609  }
2610  // End call triggers
2611  }
2612 
2613  if (!$error) {
2614  $this->db->commit();
2615  return 1;
2616  } else {
2617  foreach ($this->errors as $errmsg) {
2618  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2619  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2620  }
2621  $this->db->rollback();
2622  return -1 * $error;
2623  }
2624  } else {
2625  return -2;
2626  }
2627  }
2628 
2629  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2639  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2640  {
2641  // phpcs:enable
2642  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2643  }
2644 
2653  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2654  {
2655  if ($user->rights->commande->creer) {
2656  $error = 0;
2657 
2658  $this->db->begin();
2659 
2660  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2661  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2662  $sql .= " WHERE rowid = ".((int) $this->id);
2663 
2664  dol_syslog(__METHOD__, LOG_DEBUG);
2665  $resql = $this->db->query($sql);
2666  if (!$resql) {
2667  $this->errors[] = $this->db->error();
2668  $error++;
2669  }
2670 
2671  if (!$error) {
2672  $this->oldcopy = clone $this;
2673  $this->date_livraison = $delivery_date;
2674  $this->delivery_date = $delivery_date;
2675  }
2676 
2677  if (!$notrigger && empty($error)) {
2678  // Call trigger
2679  $result = $this->call_trigger('ORDER_MODIFY', $user);
2680  if ($result < 0) {
2681  $error++;
2682  }
2683  // End call triggers
2684  }
2685 
2686  if (!$error) {
2687  $this->db->commit();
2688  return 1;
2689  } else {
2690  foreach ($this->errors as $errmsg) {
2691  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2692  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2693  }
2694  $this->db->rollback();
2695  return -1 * $error;
2696  }
2697  } else {
2698  return -2;
2699  }
2700  }
2701 
2702  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2716  public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2717  {
2718  // phpcs:enable
2719  global $user;
2720 
2721  $ga = array();
2722 
2723  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2724  $sql .= " c.rowid as cid, c.ref";
2725  if (empty($user->rights->societe->client->voir) && !$socid) {
2726  $sql .= ", sc.fk_soc, sc.fk_user";
2727  }
2728  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2729  if (empty($user->rights->societe->client->voir) && !$socid) {
2730  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2731  }
2732  $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2733  $sql .= " AND c.fk_soc = s.rowid";
2734  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2735  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2736  }
2737  if ($socid) {
2738  $sql .= " AND s.rowid = ".((int) $socid);
2739  }
2740  if ($draft) {
2741  $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2742  }
2743  if (is_object($excluser)) {
2744  $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2745  }
2746  $sql .= $this->db->order($sortfield, $sortorder);
2747  $sql .= $this->db->plimit($limit, $offset);
2748 
2749  $result = $this->db->query($sql);
2750  if ($result) {
2751  $numc = $this->db->num_rows($result);
2752  if ($numc) {
2753  $i = 0;
2754  while ($i < $numc) {
2755  $obj = $this->db->fetch_object($result);
2756 
2757  if ($shortlist == 1) {
2758  $ga[$obj->cid] = $obj->ref;
2759  } elseif ($shortlist == 2) {
2760  $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2761  } else {
2762  $ga[$i]['id'] = $obj->cid;
2763  $ga[$i]['ref'] = $obj->ref;
2764  $ga[$i]['name'] = $obj->name;
2765  }
2766  $i++;
2767  }
2768  }
2769  return $ga;
2770  } else {
2771  dol_print_error($this->db);
2772  return -1;
2773  }
2774  }
2775 
2783  public function availability($availability_id, $notrigger = 0)
2784  {
2785  global $user;
2786 
2787  dol_syslog('Commande::availability('.$availability_id.')');
2788  if ($this->statut >= self::STATUS_DRAFT) {
2789  $error = 0;
2790 
2791  $this->db->begin();
2792 
2793  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2794  $sql .= ' SET fk_availability = '.((int) $availability_id);
2795  $sql .= ' WHERE rowid='.((int) $this->id);
2796 
2797  dol_syslog(__METHOD__, LOG_DEBUG);
2798  $resql = $this->db->query($sql);
2799  if (!$resql) {
2800  $this->errors[] = $this->db->error();
2801  $error++;
2802  }
2803 
2804  if (!$error) {
2805  $this->oldcopy = clone $this;
2806  $this->availability_id = $availability_id;
2807  }
2808 
2809  if (!$notrigger && empty($error)) {
2810  // Call trigger
2811  $result = $this->call_trigger('ORDER_MODIFY', $user);
2812  if ($result < 0) {
2813  $error++;
2814  }
2815  // End call triggers
2816  }
2817 
2818  if (!$error) {
2819  $this->db->commit();
2820  return 1;
2821  } else {
2822  foreach ($this->errors as $errmsg) {
2823  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2824  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2825  }
2826  $this->db->rollback();
2827  return -1 * $error;
2828  }
2829  } else {
2830  $error_str = 'Command status do not meet requirement '.$this->statut;
2831  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2832  $this->error = $error_str;
2833  $this->errors[] = $this->error;
2834  return -2;
2835  }
2836  }
2837 
2838  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2846  public function demand_reason($demand_reason_id, $notrigger = 0)
2847  {
2848  // phpcs:enable
2849  global $user;
2850 
2851  dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2852  if ($this->statut >= self::STATUS_DRAFT) {
2853  $error = 0;
2854 
2855  $this->db->begin();
2856 
2857  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2858  $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2859  $sql .= ' WHERE rowid='.((int) $this->id);
2860 
2861  dol_syslog(__METHOD__, LOG_DEBUG);
2862  $resql = $this->db->query($sql);
2863  if (!$resql) {
2864  $this->errors[] = $this->db->error();
2865  $error++;
2866  }
2867 
2868  if (!$error) {
2869  $this->oldcopy = clone $this;
2870  $this->demand_reason_id = $demand_reason_id;
2871  }
2872 
2873  if (!$notrigger && empty($error)) {
2874  // Call trigger
2875  $result = $this->call_trigger('ORDER_MODIFY', $user);
2876  if ($result < 0) {
2877  $error++;
2878  }
2879  // End call triggers
2880  }
2881 
2882  if (!$error) {
2883  $this->db->commit();
2884  return 1;
2885  } else {
2886  foreach ($this->errors as $errmsg) {
2887  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2888  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2889  }
2890  $this->db->rollback();
2891  return -1 * $error;
2892  }
2893  } else {
2894  $error_str = 'order status do not meet requirement '.$this->statut;
2895  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2896  $this->error = $error_str;
2897  $this->errors[] = $this->error;
2898  return -2;
2899  }
2900  }
2901 
2902  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2911  public function set_ref_client($user, $ref_client, $notrigger = 0)
2912  {
2913  // phpcs:enable
2914  if ($user->rights->commande->creer) {
2915  $error = 0;
2916 
2917  $this->db->begin();
2918 
2919  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2920  $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2921  $sql .= ' WHERE rowid = '.((int) $this->id);
2922 
2923  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2924  $resql = $this->db->query($sql);
2925  if (!$resql) {
2926  $this->errors[] = $this->db->error();
2927  $error++;
2928  }
2929 
2930  if (!$error) {
2931  $this->oldcopy = clone $this;
2932  $this->ref_client = $ref_client;
2933  }
2934 
2935  if (!$notrigger && empty($error)) {
2936  // Call trigger
2937  $result = $this->call_trigger('ORDER_MODIFY', $user);
2938  if ($result < 0) {
2939  $error++;
2940  }
2941  // End call triggers
2942  }
2943  if (!$error) {
2944  $this->db->commit();
2945  return 1;
2946  } else {
2947  foreach ($this->errors as $errmsg) {
2948  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2949  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2950  }
2951  $this->db->rollback();
2952  return -1 * $error;
2953  }
2954  } else {
2955  return -1;
2956  }
2957  }
2958 
2966  public function classifyBilled(User $user, $notrigger = 0)
2967  {
2968  $error = 0;
2969 
2970  if ($this->billed) {
2971  return 0;
2972  }
2973 
2974  $this->db->begin();
2975 
2976  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2977  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
2978 
2979  dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2980  if ($this->db->query($sql)) {
2981  if (!$error) {
2982  $this->oldcopy = clone $this;
2983  $this->billed = 1;
2984  }
2985 
2986  if (!$notrigger && empty($error)) {
2987  // Call trigger
2988  $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
2989  if ($result < 0) {
2990  $error++;
2991  }
2992  // End call triggers
2993  }
2994 
2995  if (!$error) {
2996  $this->db->commit();
2997  return 1;
2998  } else {
2999  foreach ($this->errors as $errmsg) {
3000  dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3001  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3002  }
3003  $this->db->rollback();
3004  return -1 * $error;
3005  }
3006  } else {
3007  $this->error = $this->db->error();
3008  $this->db->rollback();
3009  return -1;
3010  }
3011  }
3012 
3020  public function classifyUnBilled(User $user, $notrigger = 0)
3021  {
3022  $error = 0;
3023 
3024  $this->db->begin();
3025 
3026  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3027  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3028 
3029  dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3030  if ($this->db->query($sql)) {
3031  if (!$error) {
3032  $this->oldcopy = clone $this;
3033  $this->billed = 1;
3034  }
3035 
3036  if (!$notrigger && empty($error)) {
3037  // Call trigger
3038  $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3039  if ($result < 0) {
3040  $error++;
3041  }
3042  // End call triggers
3043  }
3044 
3045  if (!$error) {
3046  $this->billed = 0;
3047 
3048  $this->db->commit();
3049  return 1;
3050  } else {
3051  foreach ($this->errors as $errmsg) {
3052  dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3053  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3054  }
3055  $this->db->rollback();
3056  return -1 * $error;
3057  }
3058  } else {
3059  $this->error = $this->db->error();
3060  $this->db->rollback();
3061  return -1;
3062  }
3063  }
3064 
3065 
3096  public 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, $ref_ext = '', $rang = 0)
3097  {
3098  global $conf, $mysoc, $langs, $user;
3099 
3100  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, ref_ext=$ref_ext");
3101  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3102 
3103  if ($this->statut == Commande::STATUS_DRAFT) {
3104  // Clean parameters
3105  if (empty($qty)) {
3106  $qty = 0;
3107  }
3108  if (empty($info_bits)) {
3109  $info_bits = 0;
3110  }
3111  if (empty($txtva)) {
3112  $txtva = 0;
3113  }
3114  if (empty($txlocaltax1)) {
3115  $txlocaltax1 = 0;
3116  }
3117  if (empty($txlocaltax2)) {
3118  $txlocaltax2 = 0;
3119  }
3120  if (empty($remise_percent)) {
3121  $remise_percent = 0;
3122  }
3123  if (empty($special_code) || $special_code == 3) {
3124  $special_code = 0;
3125  }
3126  if (empty($ref_ext)) {
3127  $ref_ext = '';
3128  }
3129 
3130  if ($date_start && $date_end && $date_start > $date_end) {
3131  $langs->load("errors");
3132  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3133  return -1;
3134  }
3135 
3136  $remise_percent = price2num($remise_percent);
3137  $qty = price2num($qty);
3138  $pu = price2num($pu);
3139  $pa_ht = price2num($pa_ht);
3140  $pu_ht_devise = price2num($pu_ht_devise);
3141  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
3142  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3143  }
3144  $txlocaltax1 = price2num($txlocaltax1);
3145  $txlocaltax2 = price2num($txlocaltax2);
3146 
3147  $this->db->begin();
3148 
3149  // Calcul du total TTC et de la TVA pour la ligne a partir de
3150  // qty, pu, remise_percent et txtva
3151  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3152  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3153 
3154  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3155 
3156  // Clean vat code
3157  $vat_src_code = '';
3158  $reg = array();
3159  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3160  $vat_src_code = $reg[1];
3161  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3162  }
3163 
3164  $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);
3165 
3166  $total_ht = $tabprice[0];
3167  $total_tva = $tabprice[1];
3168  $total_ttc = $tabprice[2];
3169  $total_localtax1 = $tabprice[9];
3170  $total_localtax2 = $tabprice[10];
3171  $pu_ht = $tabprice[3];
3172  $pu_tva = $tabprice[4];
3173  $pu_ttc = $tabprice[5];
3174 
3175  // MultiCurrency
3176  $multicurrency_total_ht = $tabprice[16];
3177  $multicurrency_total_tva = $tabprice[17];
3178  $multicurrency_total_ttc = $tabprice[18];
3179  $pu_ht_devise = $tabprice[19];
3180 
3181  // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3182  $price = $pu_ht;
3183  if ($price_base_type == 'TTC') {
3184  $subprice = $pu_ttc;
3185  } else {
3186  $subprice = $pu_ht;
3187  }
3188  $remise = 0;
3189  if ($remise_percent > 0) {
3190  $remise = round(($pu * $remise_percent / 100), 2);
3191  $price = ($pu - $remise);
3192  }
3193 
3194  //Fetch current line from the database and then clone the object and set it in $oldline property
3195  $line = new OrderLine($this->db);
3196  $line->fetch($rowid);
3197  $line->fetch_optionals();
3198 
3199  if (!empty($line->fk_product)) {
3200  $product = new Product($this->db);
3201  $result = $product->fetch($line->fk_product);
3202  $product_type = $product->type;
3203 
3204  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
3205  $langs->load("errors");
3206  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3207  $this->errors[] = $this->error;
3208 
3209  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3210 
3211  $this->db->rollback();
3213  }
3214  }
3215 
3216  $staticline = clone $line;
3217 
3218  $line->oldline = $staticline;
3219  $this->line = $line;
3220  $this->line->context = $this->context;
3221  $this->line->rang = $rang;
3222 
3223  // Reorder if fk_parent_line change
3224  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3225  $rangmax = $this->line_max($fk_parent_line);
3226  $this->line->rang = $rangmax + 1;
3227  }
3228 
3229  $this->line->id = $rowid;
3230  $this->line->label = $label;
3231  $this->line->desc = $desc;
3232  $this->line->qty = $qty;
3233  $this->line->ref_ext = $ref_ext;
3234 
3235  $this->line->vat_src_code = $vat_src_code;
3236  $this->line->tva_tx = $txtva;
3237  $this->line->localtax1_tx = $txlocaltax1;
3238  $this->line->localtax2_tx = $txlocaltax2;
3239  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3240  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3241  $this->line->remise_percent = $remise_percent;
3242  $this->line->subprice = $subprice;
3243  $this->line->info_bits = $info_bits;
3244  $this->line->special_code = $special_code;
3245  $this->line->total_ht = $total_ht;
3246  $this->line->total_tva = $total_tva;
3247  $this->line->total_localtax1 = $total_localtax1;
3248  $this->line->total_localtax2 = $total_localtax2;
3249  $this->line->total_ttc = $total_ttc;
3250  $this->line->date_start = $date_start;
3251  $this->line->date_end = $date_end;
3252  $this->line->product_type = $type;
3253  $this->line->fk_parent_line = $fk_parent_line;
3254  $this->line->skip_update_total = $skip_update_total;
3255  $this->line->fk_unit = $fk_unit;
3256 
3257  $this->line->fk_fournprice = $fk_fournprice;
3258  $this->line->pa_ht = $pa_ht;
3259 
3260  // Multicurrency
3261  $this->line->multicurrency_subprice = $pu_ht_devise;
3262  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3263  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3264  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3265 
3266  // TODO deprecated
3267  $this->line->price = $price;
3268 
3269  if (is_array($array_options) && count($array_options) > 0) {
3270  // We replace values in this->line->array_options only for entries defined into $array_options
3271  foreach ($array_options as $key => $value) {
3272  $this->line->array_options[$key] = $array_options[$key];
3273  }
3274  }
3275 
3276  $result = $this->line->update($user, $notrigger);
3277  if ($result > 0) {
3278  // Reorder if child line
3279  if (!empty($fk_parent_line)) {
3280  $this->line_order(true, 'DESC');
3281  }
3282 
3283  // Mise a jour info denormalisees
3284  $this->update_price(1);
3285 
3286  $this->db->commit();
3287  return $result;
3288  } else {
3289  $this->error = $this->line->error;
3290 
3291  $this->db->rollback();
3292  return -1;
3293  }
3294  } else {
3295  $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3296  $this->errors = array('OrderStatusMakeOperationForbidden');
3297  return -2;
3298  }
3299  }
3300 
3308  public function update(User $user, $notrigger = 0)
3309  {
3310  global $conf;
3311 
3312  $error = 0;
3313 
3314  // Clean parameters
3315  if (isset($this->ref)) {
3316  $this->ref = trim($this->ref);
3317  }
3318  if (isset($this->ref_client)) {
3319  $this->ref_client = trim($this->ref_client);
3320  }
3321  if (isset($this->note) || isset($this->note_private)) {
3322  $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3323  }
3324  if (isset($this->note_public)) {
3325  $this->note_public = trim($this->note_public);
3326  }
3327  if (isset($this->model_pdf)) {
3328  $this->model_pdf = trim($this->model_pdf);
3329  }
3330  if (isset($this->import_key)) {
3331  $this->import_key = trim($this->import_key);
3332  }
3333  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
3334 
3335  // Check parameters
3336  // Put here code to add control on parameters values
3337 
3338  // Update request
3339  $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3340 
3341  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3342  $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3343  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3344  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3345  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3346  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3347  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3348  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3349  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3350  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3351  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3352  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3353  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3354  $sql .= " fk_user_valid=".((isset($this->user_valid) && $this->user_valid > 0) ? $this->user_valid : "null").",";
3355  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3356  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3357  $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3358  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3359  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3360  $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3361  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3362  $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3363  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3364  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3365  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3366  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3367 
3368  $sql .= " WHERE rowid=".((int) $this->id);
3369 
3370  $this->db->begin();
3371 
3372  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3373  $resql = $this->db->query($sql);
3374  if (!$resql) {
3375  $error++; $this->errors[] = "Error ".$this->db->lasterror();
3376  }
3377 
3378  if (!$error) {
3379  $result = $this->insertExtraFields();
3380  if ($result < 0) {
3381  $error++;
3382  }
3383  }
3384 
3385  if (!$error && !$notrigger) {
3386  // Call trigger
3387  $result = $this->call_trigger('ORDER_MODIFY', $user);
3388  if ($result < 0) {
3389  $error++;
3390  }
3391  // End call triggers
3392  }
3393 
3394  // Commit or rollback
3395  if ($error) {
3396  foreach ($this->errors as $errmsg) {
3397  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3398  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3399  }
3400  $this->db->rollback();
3401  return -1 * $error;
3402  } else {
3403  $this->db->commit();
3404  return 1;
3405  }
3406  }
3407 
3415  public function delete($user, $notrigger = 0)
3416  {
3417  global $conf, $langs;
3418  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3419 
3420  $error = 0;
3421 
3422  dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3423 
3424  $this->db->begin();
3425 
3426  if (!$notrigger) {
3427  // Call trigger
3428  $result = $this->call_trigger('ORDER_DELETE', $user);
3429  if ($result < 0) {
3430  $error++;
3431  }
3432  // End call triggers
3433  }
3434 
3435  // Test we can delete
3436  if ($this->nb_expedition() != 0) {
3437  $this->errors[] = $langs->trans('SomeShipmentExists');
3438  $error++;
3439  }
3440 
3441  // Delete extrafields of lines and lines
3442  if (!$error && !empty($this->table_element_line)) {
3443  $tabletodelete = $this->table_element_line;
3444  $sqlef = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete."_extrafields WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id).")";
3445  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3446  if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3447  $error++;
3448  $this->error = $this->db->lasterror();
3449  $this->errors[] = $this->error;
3450  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3451  }
3452  }
3453 
3454  if (!$error) {
3455  // Delete linked object
3456  $res = $this->deleteObjectLinked();
3457  if ($res < 0) {
3458  $error++;
3459  }
3460  }
3461 
3462  if (!$error) {
3463  // Delete linked contacts
3464  $res = $this->delete_linked_contact();
3465  if ($res < 0) {
3466  $error++;
3467  }
3468  }
3469 
3470  // Removed extrafields of object
3471  if (!$error) {
3472  $result = $this->deleteExtraFields();
3473  if ($result < 0) {
3474  $error++;
3475  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3476  }
3477  }
3478 
3479  // Delete main record
3480  if (!$error) {
3481  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3482  $res = $this->db->query($sql);
3483  if (!$res) {
3484  $error++;
3485  $this->error = $this->db->lasterror();
3486  $this->errors[] = $this->error;
3487  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3488  }
3489  }
3490 
3491  // Delete record into ECM index and physically
3492  if (!$error) {
3493  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3494  if (!$res) {
3495  $error++;
3496  }
3497  }
3498 
3499  if (!$error) {
3500  // We remove directory
3501  $ref = dol_sanitizeFileName($this->ref);
3502  if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3503  $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3504  $file = $dir."/".$ref.".pdf";
3505  if (file_exists($file)) {
3506  dol_delete_preview($this);
3507 
3508  if (!dol_delete_file($file, 0, 0, 0, $this)) {
3509  $this->error = 'ErrorFailToDeleteFile';
3510  $this->errors[] = $this->error;
3511  $this->db->rollback();
3512  return 0;
3513  }
3514  }
3515  if (file_exists($dir)) {
3516  $res = @dol_delete_dir_recursive($dir);
3517  if (!$res) {
3518  $this->error = 'ErrorFailToDeleteDir';
3519  $this->errors[] = $this->error;
3520  $this->db->rollback();
3521  return 0;
3522  }
3523  }
3524  }
3525  }
3526 
3527  if (!$error) {
3528  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3529  $this->db->commit();
3530  return 1;
3531  } else {
3532  $this->db->rollback();
3533  return -1;
3534  }
3535  }
3536 
3537 
3538  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3545  public function load_board($user)
3546  {
3547  // phpcs:enable
3548  global $conf, $langs;
3549 
3550  $clause = " WHERE";
3551 
3552  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3553  $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3554  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3555  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3556  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3557  $clause = " AND";
3558  }
3559  $sql .= $clause." c.entity IN (".getEntity('commande').")";
3560  //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3561  $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
3562  if ($user->socid) {
3563  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3564  }
3565 
3566  $resql = $this->db->query($sql);
3567  if ($resql) {
3568  $response = new WorkboardResponse();
3569  $response->warning_delay = $conf->commande->client->warning_delay / 60 / 60 / 24;
3570  $response->label = $langs->trans("OrdersToProcess");
3571  $response->labelShort = $langs->trans("Opened");
3572  $response->url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&mainmenu=commercial&leftmenu=orders';
3573  $response->img = img_object('', "order");
3574 
3575  $generic_commande = new Commande($this->db);
3576 
3577  while ($obj = $this->db->fetch_object($resql)) {
3578  $response->nbtodo++;
3579  $response->total += $obj->total_ht;
3580 
3581  $generic_commande->statut = $obj->fk_statut;
3582  $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3583  $generic_commande->date = $this->db->jdate($obj->date_commande);
3584  $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3585  $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3586 
3587  if ($generic_commande->hasDelay()) {
3588  $response->nbtodolate++;
3589  }
3590  }
3591 
3592  return $response;
3593  } else {
3594  $this->error = $this->db->error();
3595  return -1;
3596  }
3597  }
3598 
3604  public function getLabelSource()
3605  {
3606  global $langs;
3607 
3608  $label = $langs->trans('OrderSource'.$this->source);
3609 
3610  if ($label == 'OrderSource') {
3611  return '';
3612  }
3613  return $label;
3614  }
3615 
3622  public function getLibStatut($mode)
3623  {
3624  return $this->LibStatut($this->statut, $this->billed, $mode);
3625  }
3626 
3627  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3637  public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3638  {
3639  // phpcs:enable
3640  global $langs, $conf, $hookmanager;
3641 
3642  $billedtext = '';
3643  if (empty($donotshowbilled)) {
3644  $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3645  }
3646 
3647  $labelTooltip = '';
3648 
3649  if ($status == self::STATUS_CANCELED) {
3650  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3651  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3652  $statusType = 'status9';
3653  } elseif ($status == self::STATUS_DRAFT) {
3654  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3655  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3656  $statusType = 'status0';
3657  } elseif ($status == self::STATUS_VALIDATED) {
3658  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3659  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3660  $statusType = 'status1';
3661  } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3662  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3663  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3664  $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3665  if (!empty($this->delivery_date)) {
3666  $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3667  }
3668  $statusType = 'status4';
3669  } elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3670  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderToBill'); // translated into Delivered
3671  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderToBillShort'); // translated into Delivered
3672  $statusType = 'status4';
3673  } elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3674  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderProcessed').$billedtext;
3675  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderProcessedShort').$billedtext;
3676  $statusType = 'status6';
3677  } elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3678  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered');
3679  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort');
3680  $statusType = 'status6';
3681  } else {
3682  $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3683  $labelStatusShort = '';
3684  $statusType = '';
3685  $mode = 0;
3686  }
3687 
3688  $parameters = array(
3689  'status' => $status,
3690  'mode' => $mode,
3691  'billed' => $billed,
3692  'donotshowbilled' => $donotshowbilled
3693  );
3694 
3695  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3696 
3697  if ($reshook > 0) {
3698  return $hookmanager->resPrint;
3699  }
3700 
3701  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3702  }
3703 
3704 
3718  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3719  {
3720  global $conf, $langs, $user, $hookmanager;
3721 
3722  if (!empty($conf->dol_no_mouse_hover)) {
3723  $notooltip = 1; // Force disable tooltips
3724  }
3725 
3726  $result = '';
3727 
3728  if (isModEnabled("expedition") && ($option == '1' || $option == '2')) {
3729  $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3730  } else {
3731  $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3732  }
3733 
3734  if (!$user->rights->commande->lire) {
3735  $option = 'nolink';
3736  }
3737 
3738  if ($option !== 'nolink') {
3739  // Add param to save lastsearch_values or not
3740  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3741  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3742  $add_save_lastsearch_values = 1;
3743  }
3744  if ($add_save_lastsearch_values) {
3745  $url .= '&save_lastsearch_values=1';
3746  }
3747  }
3748 
3749  if ($short) {
3750  return $url;
3751  }
3752 
3753  $label = '';
3754 
3755  if ($user->rights->commande->lire) {
3756  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3757  if (isset($this->statut)) {
3758  $label .= ' '.$this->getLibStatut(5);
3759  }
3760  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3761  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3762  if (!empty($this->total_ht)) {
3763  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3764  }
3765  if (!empty($this->total_tva)) {
3766  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3767  }
3768  if (!empty($this->total_ttc)) {
3769  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3770  }
3771  if (!empty($this->date)) {
3772  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3773  }
3774  if (!empty($this->delivery_date)) {
3775  $label .= '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3776  }
3777  }
3778 
3779  $linkclose = '';
3780  if (empty($notooltip) && $user->rights->commande->lire) {
3781  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3782  $label = $langs->trans("Order");
3783  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3784  }
3785  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
3786  $linkclose .= ' class="classfortooltip"';
3787 
3788  $target_value=array('_self', '_blank', '_parent', '_top');
3789  if (in_array($target, $target_value)) {
3790  $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3791  }
3792  }
3793 
3794  $linkstart = '<a href="'.$url.'"';
3795  $linkstart .= $linkclose.'>';
3796  $linkend = '</a>';
3797 
3798  if ($option === 'nolink') {
3799  $linkstart = '';
3800  $linkend = '';
3801  }
3802 
3803  $result .= $linkstart;
3804  if ($withpicto) {
3805  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
3806  }
3807  if ($withpicto != 2) {
3808  $result .= $this->ref;
3809  }
3810  $result .= $linkend;
3811 
3812  if ($addlinktonotes) {
3813  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3814  if ($txttoshow) {
3815  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3816  $result .= ' <span class="note inline-block">';
3817  $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3818  $result .= img_picto('', 'note');
3819  $result .= '</a>';
3820  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3821  //$result.='</a>';
3822  $result .= '</span>';
3823  }
3824  }
3825 
3826  global $action;
3827  $hookmanager->initHooks(array($this->element . 'dao'));
3828  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3829  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3830  if ($reshook > 0) {
3831  $result = $hookmanager->resPrint;
3832  } else {
3833  $result .= $hookmanager->resPrint;
3834  }
3835  return $result;
3836  }
3837 
3838 
3845  public function info($id)
3846  {
3847  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3848  $sql .= ' date_valid as datev,';
3849  $sql .= ' date_cloture as datecloture,';
3850  $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3851  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3852  $sql .= ' WHERE c.rowid = '.((int) $id);
3853  $result = $this->db->query($sql);
3854  if ($result) {
3855  if ($this->db->num_rows($result)) {
3856  $obj = $this->db->fetch_object($result);
3857  $this->id = $obj->rowid;
3858  if ($obj->fk_user_author) {
3859  $this->user_creation_id = $obj->fk_user_author;
3860  }
3861  if ($obj->fk_user_valid) {
3862  $this->user_validation_id = $obj->fk_user_valid;
3863  }
3864  if ($obj->fk_user_cloture) {
3865  $this->user_closing_id = $obj->fk_user_cloture;
3866  }
3867 
3868  $this->date_creation = $this->db->jdate($obj->datec);
3869  $this->date_modification = $this->db->jdate($obj->datem);
3870  $this->date_validation = $this->db->jdate($obj->datev);
3871  $this->date_cloture = $this->db->jdate($obj->datecloture);
3872  }
3873 
3874  $this->db->free($result);
3875  } else {
3876  dol_print_error($this->db);
3877  }
3878  }
3879 
3880 
3888  public function initAsSpecimen()
3889  {
3890  global $conf, $langs;
3891 
3892  dol_syslog(get_class($this)."::initAsSpecimen");
3893 
3894  // Load array of products prodids
3895  $num_prods = 0;
3896  $prodids = array();
3897  $sql = "SELECT rowid";
3898  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3899  $sql .= " WHERE entity IN (".getEntity('product').")";
3900  $sql .= $this->db->plimit(100);
3901 
3902  $resql = $this->db->query($sql);
3903  if ($resql) {
3904  $num_prods = $this->db->num_rows($resql);
3905  $i = 0;
3906  while ($i < $num_prods) {
3907  $i++;
3908  $row = $this->db->fetch_row($resql);
3909  $prodids[$i] = $row[0];
3910  }
3911  }
3912 
3913  // Initialise parametres
3914  $this->id = 0;
3915  $this->ref = 'SPECIMEN';
3916  $this->specimen = 1;
3917  $this->socid = 1;
3918  $this->date = time();
3919  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3920  $this->cond_reglement_code = 'RECEP';
3921  $this->mode_reglement_code = 'CHQ';
3922  $this->availability_code = 'DSP';
3923  $this->demand_reason_code = 'SRC_00';
3924 
3925  $this->note_public = 'This is a comment (public)';
3926  $this->note_private = 'This is a comment (private)';
3927 
3928  $this->multicurrency_tx = 1;
3929  $this->multicurrency_code = $conf->currency;
3930 
3931  // Lines
3932  $nbp = 5;
3933  $xnbp = 0;
3934  while ($xnbp < $nbp) {
3935  $line = new OrderLine($this->db);
3936 
3937  $line->desc = $langs->trans("Description")." ".$xnbp;
3938  $line->qty = 1;
3939  $line->subprice = 100;
3940  $line->price = 100;
3941  $line->tva_tx = 20;
3942  if ($xnbp == 2) {
3943  $line->total_ht = 50;
3944  $line->total_ttc = 60;
3945  $line->total_tva = 10;
3946  $line->remise_percent = 50;
3947  } else {
3948  $line->total_ht = 100;
3949  $line->total_ttc = 120;
3950  $line->total_tva = 20;
3951  $line->remise_percent = 0;
3952  }
3953  if ($num_prods > 0) {
3954  $prodid = mt_rand(1, $num_prods);
3955  $line->fk_product = $prodids[$prodid];
3956  $line->product_ref = 'SPECIMEN';
3957  }
3958 
3959  $this->lines[$xnbp] = $line;
3960 
3961  $this->total_ht += $line->total_ht;
3962  $this->total_tva += $line->total_tva;
3963  $this->total_ttc += $line->total_ttc;
3964 
3965  $xnbp++;
3966  }
3967  }
3968 
3969 
3970  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3976  public function load_state_board()
3977  {
3978  // phpcs:enable
3979  global $user;
3980 
3981  $this->nb = array();
3982  $clause = "WHERE";
3983 
3984  $sql = "SELECT count(co.rowid) as nb";
3985  $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
3986  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3987  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3988  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3989  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3990  $clause = "AND";
3991  }
3992  $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
3993 
3994  $resql = $this->db->query($sql);
3995  if ($resql) {
3996  while ($obj = $this->db->fetch_object($resql)) {
3997  $this->nb["orders"] = $obj->nb;
3998  }
3999  $this->db->free($resql);
4000  return 1;
4001  } else {
4002  dol_print_error($this->db);
4003  $this->error = $this->db->error();
4004  return -1;
4005  }
4006  }
4007 
4013  public function getLinesArray()
4014  {
4015  return $this->fetch_lines();
4016  }
4017 
4029  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4030  {
4031  global $conf, $langs;
4032 
4033  $langs->load("orders");
4034  $outputlangs->load("products");
4035 
4036  if (!dol_strlen($modele)) {
4037  $modele = 'einstein';
4038 
4039  if (!empty($this->model_pdf)) {
4040  $modele = $this->model_pdf;
4041  } elseif (!empty($this->modelpdf)) { // deprecated
4042  $modele = $this->modelpdf;
4043  } elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
4044  $modele = $conf->global->COMMANDE_ADDON_PDF;
4045  }
4046  }
4047 
4048  $modelpath = "core/modules/commande/doc/";
4049 
4050  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4051  }
4052 
4053 
4062  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4063  {
4064  $tables = array(
4065  'commande'
4066  );
4067 
4068  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4069  }
4070 
4079  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4080  {
4081  $tables = array(
4082  'commandedet',
4083  );
4084 
4085  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4086  }
4087 
4093  public function hasDelay()
4094  {
4095  global $conf;
4096 
4097  if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4098  return false; // Never late if not inside this status range
4099  }
4100 
4101  $now = dol_now();
4102 
4103  return max($this->date, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
4104  }
4105 
4111  public function showDelay()
4112  {
4113  global $conf, $langs;
4114 
4115  if (empty($this->date_livraison)) {
4116  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
4117  } else {
4118  $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
4119  }
4120  $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4121 
4122  return $text;
4123  }
4124 }
4125 
4126 
4131 {
4135  public $element = 'commandedet';
4136 
4137  public $table_element = 'commandedet';
4138 
4139  public $oldline;
4140 
4145  public $fk_commande;
4146 
4153  public $commande_id;
4154 
4155  public $fk_parent_line;
4156 
4160  public $fk_facture;
4161 
4165  public $ref_ext;
4166 
4167  public $fk_remise_except;
4168 
4172  public $rang = 0;
4173  public $fk_fournprice;
4174 
4179  public $pa_ht;
4180  public $marge_tx;
4181  public $marque_tx;
4182 
4187  public $remise;
4188 
4189  // Start and end date of the line
4190  public $date_start;
4191  public $date_end;
4192 
4193  public $skip_update_total; // Skip update price total for special lines
4194 
4195 
4201  public function __construct($db)
4202  {
4203  $this->db = $db;
4204  }
4205 
4212  public function fetch($rowid)
4213  {
4214  $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,';
4215  $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4216  $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,';
4217  $sql .= ' cd.fk_unit,';
4218  $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4219  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4220  $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4221  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4222  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4223  $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4224  $result = $this->db->query($sql);
4225  if ($result) {
4226  $objp = $this->db->fetch_object($result);
4227 
4228  if (!$objp) {
4229  $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4230  return 0;
4231  }
4232 
4233  $this->rowid = $objp->rowid;
4234  $this->id = $objp->rowid;
4235  $this->fk_commande = $objp->fk_commande;
4236  $this->fk_parent_line = $objp->fk_parent_line;
4237  $this->label = $objp->custom_label;
4238  $this->desc = $objp->description;
4239  $this->qty = $objp->qty;
4240  $this->price = $objp->price;
4241  $this->subprice = $objp->subprice;
4242  $this->ref_ext = $objp->ref_ext;
4243  $this->vat_src_code = $objp->vat_src_code;
4244  $this->tva_tx = $objp->tva_tx;
4245  $this->localtax1_tx = $objp->localtax1_tx;
4246  $this->localtax2_tx = $objp->localtax2_tx;
4247  $this->remise = $objp->remise;
4248  $this->remise_percent = $objp->remise_percent;
4249  $this->fk_remise_except = $objp->fk_remise_except;
4250  $this->fk_product = $objp->fk_product;
4251  $this->product_type = $objp->product_type;
4252  $this->info_bits = $objp->info_bits;
4253  $this->special_code = $objp->special_code;
4254  $this->total_ht = $objp->total_ht;
4255  $this->total_tva = $objp->total_tva;
4256  $this->total_localtax1 = $objp->total_localtax1;
4257  $this->total_localtax2 = $objp->total_localtax2;
4258  $this->total_ttc = $objp->total_ttc;
4259  $this->fk_fournprice = $objp->fk_fournprice;
4260  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4261  $this->pa_ht = $marginInfos[0];
4262  $this->marge_tx = $marginInfos[1];
4263  $this->marque_tx = $marginInfos[2];
4264  $this->special_code = $objp->special_code;
4265  $this->rang = $objp->rang;
4266 
4267  $this->ref = $objp->product_ref; // deprecated
4268 
4269  $this->product_ref = $objp->product_ref;
4270  $this->product_label = $objp->product_label;
4271  $this->product_desc = $objp->product_desc;
4272  $this->product_tobatch = $objp->product_tobatch;
4273  $this->fk_unit = $objp->fk_unit;
4274 
4275  $this->date_start = $this->db->jdate($objp->date_start);
4276  $this->date_end = $this->db->jdate($objp->date_end);
4277 
4278  $this->fk_multicurrency = $objp->fk_multicurrency;
4279  $this->multicurrency_code = $objp->multicurrency_code;
4280  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4281  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4282  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4283  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4284 
4285  $this->db->free($result);
4286 
4287  return 1;
4288  } else {
4289  $this->error = $this->db->lasterror();
4290  return -1;
4291  }
4292  }
4293 
4301  public function delete(User $user, $notrigger = 0)
4302  {
4303  global $conf, $langs;
4304 
4305  $error = 0;
4306 
4307  if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4308  $this->id = $this->rowid;
4309  }
4310 
4311  // check if order line is not in a shipment line before deleting
4312  $sqlCheckShipmentLine = "SELECT";
4313  $sqlCheckShipmentLine .= " ed.rowid";
4314  $sqlCheckShipmentLine .= " FROM ".MAIN_DB_PREFIX."expeditiondet ed";
4315  $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = ".((int) $this->id);
4316 
4317  $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4318  if (!$resqlCheckShipmentLine) {
4319  $error++;
4320  $this->error = $this->db->lasterror();
4321  $this->errors[] = $this->error;
4322  } else {
4323  $langs->load('errors');
4324  $num = $this->db->num_rows($resqlCheckShipmentLine);
4325  if ($num > 0) {
4326  $error++;
4327  $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4328  $this->error = $langs->trans('ErrorRecordAlreadyExists').' : '.$langs->trans('ShipmentLine').' '.$objCheckShipmentLine->rowid;
4329  $this->errors[] = $this->error;
4330  }
4331  $this->db->free($resqlCheckShipmentLine);
4332  }
4333  if ($error) {
4334  dol_syslog(__METHOD__.'Error ; '.$this->error, LOG_ERR);
4335  return -1;
4336  }
4337 
4338  $this->db->begin();
4339 
4340  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid = ".((int) $this->id);
4341 
4342  dol_syslog("OrderLine::delete", LOG_DEBUG);
4343  $resql = $this->db->query($sql);
4344  if ($resql) {
4345  if (!$error && !$notrigger) {
4346  // Call trigger
4347  $result = $this->call_trigger('LINEORDER_DELETE', $user);
4348  if ($result < 0) {
4349  $error++;
4350  }
4351  // End call triggers
4352  }
4353 
4354  // Remove extrafields
4355  if (!$error) {
4356  $result = $this->deleteExtraFields();
4357  if ($result < 0) {
4358  $error++;
4359  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4360  }
4361  }
4362 
4363  if (!$error) {
4364  $this->db->commit();
4365  return 1;
4366  }
4367 
4368  foreach ($this->errors as $errmsg) {
4369  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4370  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4371  }
4372  $this->db->rollback();
4373  return -1 * $error;
4374  } else {
4375  $this->error = $this->db->lasterror();
4376  return -1;
4377  }
4378  }
4379 
4387  public function insert($user = null, $notrigger = 0)
4388  {
4389  global $langs, $conf;
4390 
4391  $error = 0;
4392 
4393  $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'.
4394 
4395  dol_syslog(get_class($this)."::insert rang=".$this->rang);
4396 
4397  // Clean parameters
4398  if (empty($this->tva_tx)) {
4399  $this->tva_tx = 0;
4400  }
4401  if (empty($this->localtax1_tx)) {
4402  $this->localtax1_tx = 0;
4403  }
4404  if (empty($this->localtax2_tx)) {
4405  $this->localtax2_tx = 0;
4406  }
4407  if (empty($this->localtax1_type)) {
4408  $this->localtax1_type = 0;
4409  }
4410  if (empty($this->localtax2_type)) {
4411  $this->localtax2_type = 0;
4412  }
4413  if (empty($this->total_localtax1)) {
4414  $this->total_localtax1 = 0;
4415  }
4416  if (empty($this->total_localtax2)) {
4417  $this->total_localtax2 = 0;
4418  }
4419  if (empty($this->rang)) {
4420  $this->rang = 0;
4421  }
4422  if (empty($this->remise_percent)) {
4423  $this->remise_percent = 0;
4424  }
4425  if (empty($this->info_bits)) {
4426  $this->info_bits = 0;
4427  }
4428  if (empty($this->special_code)) {
4429  $this->special_code = 0;
4430  }
4431  if (empty($this->fk_parent_line)) {
4432  $this->fk_parent_line = 0;
4433  }
4434  if (empty($this->pa_ht)) {
4435  $this->pa_ht = 0;
4436  }
4437  if (empty($this->ref_ext)) {
4438  $this->ref_ext = '';
4439  }
4440 
4441  // if buy price not defined, define buyprice as configured in margin admin
4442  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4443  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4444  if ($result < 0) {
4445  return $result;
4446  } else {
4447  $this->pa_ht = $result;
4448  }
4449  }
4450 
4451  // Check parameters
4452  if ($this->product_type < 0) {
4453  return -1;
4454  }
4455 
4456  $this->db->begin();
4457 
4458  // Insertion dans base de la ligne
4459  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4460  $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4461  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4462  $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4463  $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4464  $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4465  $sql .= ' fk_unit';
4466  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4467  $sql .= ')';
4468  $sql .= " VALUES (".$this->fk_commande.",";
4469  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4470  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4471  $sql .= " '".$this->db->escape($this->desc)."',";
4472  $sql .= " '".price2num($this->qty)."',";
4473  $sql .= " '".$this->db->escape($this->ref_ext)."',";
4474  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4475  $sql .= " '".price2num($this->tva_tx)."',";
4476  $sql .= " '".price2num($this->localtax1_tx)."',";
4477  $sql .= " '".price2num($this->localtax2_tx)."',";
4478  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4479  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4480  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4481  $sql .= " '".$this->db->escape($this->product_type)."',";
4482  $sql .= " '".price2num($this->remise_percent)."',";
4483  $sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4484  $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4485  $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4486  $sql .= ' '.((int) $this->special_code).',';
4487  $sql .= ' '.((int) $this->rang).',';
4488  $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4489  $sql .= ' '.price2num($this->pa_ht).',';
4490  $sql .= " ".((int) $this->info_bits).",";
4491  $sql .= " ".price2num($this->total_ht, 'MT').",";
4492  $sql .= " ".price2num($this->total_tva, 'MT').",";
4493  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4494  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4495  $sql .= " ".price2num($this->total_ttc, 'MT').",";
4496  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4497  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4498  $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4499  $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4500  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4501  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4502  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4503  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4504  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4505  $sql .= ')';
4506 
4507  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4508  $resql = $this->db->query($sql);
4509  if ($resql) {
4510  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4511  $this->rowid = $this->id;
4512 
4513  if (!$error) {
4514  $result = $this->insertExtraFields();
4515  if ($result < 0) {
4516  $error++;
4517  }
4518  }
4519 
4520  if (!$error && !$notrigger) {
4521  // Call trigger
4522  $result = $this->call_trigger('LINEORDER_INSERT', $user);
4523  if ($result < 0) {
4524  $error++;
4525  }
4526  // End call triggers
4527  }
4528 
4529  if (!$error) {
4530  $this->db->commit();
4531  return 1;
4532  }
4533 
4534  foreach ($this->errors as $errmsg) {
4535  dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4536  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4537  }
4538  $this->db->rollback();
4539  return -1 * $error;
4540  } else {
4541  $this->error = $this->db->error();
4542  $this->db->rollback();
4543  return -2;
4544  }
4545  }
4546 
4554  public function update(User $user, $notrigger = 0)
4555  {
4556  global $conf, $langs;
4557 
4558  $error = 0;
4559 
4560  $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'.
4561 
4562  // Clean parameters
4563  if (empty($this->tva_tx)) {
4564  $this->tva_tx = 0;
4565  }
4566  if (empty($this->localtax1_tx)) {
4567  $this->localtax1_tx = 0;
4568  }
4569  if (empty($this->localtax2_tx)) {
4570  $this->localtax2_tx = 0;
4571  }
4572  if (empty($this->localtax1_type)) {
4573  $this->localtax1_type = 0;
4574  }
4575  if (empty($this->localtax2_type)) {
4576  $this->localtax2_type = 0;
4577  }
4578  if (empty($this->qty)) {
4579  $this->qty = 0;
4580  }
4581  if (empty($this->total_localtax1)) {
4582  $this->total_localtax1 = 0;
4583  }
4584  if (empty($this->total_localtax2)) {
4585  $this->total_localtax2 = 0;
4586  }
4587  if (empty($this->marque_tx)) {
4588  $this->marque_tx = 0;
4589  }
4590  if (empty($this->marge_tx)) {
4591  $this->marge_tx = 0;
4592  }
4593  if (empty($this->remise_percent)) {
4594  $this->remise_percent = 0;
4595  }
4596  if (empty($this->info_bits)) {
4597  $this->info_bits = 0;
4598  }
4599  if (empty($this->special_code)) {
4600  $this->special_code = 0;
4601  }
4602  if (empty($this->product_type)) {
4603  $this->product_type = 0;
4604  }
4605  if (empty($this->fk_parent_line)) {
4606  $this->fk_parent_line = 0;
4607  }
4608  if (empty($this->pa_ht)) {
4609  $this->pa_ht = 0;
4610  }
4611  if (empty($this->ref_ext)) {
4612  $this->ref_ext = '';
4613  }
4614 
4615  // if buy price not defined, define buyprice as configured in margin admin
4616  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4617  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4618  if ($result < 0) {
4619  return $result;
4620  } else {
4621  $this->pa_ht = $result;
4622  }
4623  }
4624 
4625  $this->db->begin();
4626 
4627  // Mise a jour ligne en base
4628  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4629  $sql .= " description='".$this->db->escape($this->desc)."'";
4630  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4631  $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4632  $sql .= " , tva_tx=".price2num($this->tva_tx);
4633  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4634  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4635  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4636  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4637  $sql .= " , qty=".price2num($this->qty);
4638  $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4639  $sql .= " , subprice=".price2num($this->subprice);
4640  $sql .= " , remise_percent=".price2num($this->remise_percent);
4641  $sql .= " , price=".price2num($this->price); // TODO A virer
4642  $sql .= " , remise=".price2num($this->remise); // TODO A virer
4643  if (empty($this->skip_update_total)) {
4644  $sql .= " , total_ht=".price2num($this->total_ht);
4645  $sql .= " , total_tva=".price2num($this->total_tva);
4646  $sql .= " , total_ttc=".price2num($this->total_ttc);
4647  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4648  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4649  }
4650  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4651  $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4652  $sql .= " , info_bits=".((int) $this->info_bits);
4653  $sql .= " , special_code=".((int) $this->special_code);
4654  $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4655  $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4656  $sql .= " , product_type=".$this->product_type;
4657  $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4658  if (!empty($this->rang)) {
4659  $sql .= ", rang=".((int) $this->rang);
4660  }
4661  $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4662 
4663  // Multicurrency
4664  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4665  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4666  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4667  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4668 
4669  $sql .= " WHERE rowid = ".((int) $this->rowid);
4670 
4671  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4672  $resql = $this->db->query($sql);
4673  if ($resql) {
4674  if (!$error) {
4675  $this->id = $this->rowid;
4676  $result = $this->insertExtraFields();
4677  if ($result < 0) {
4678  $error++;
4679  }
4680  }
4681 
4682  if (!$error && !$notrigger) {
4683  // Call trigger
4684  $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4685  if ($result < 0) {
4686  $error++;
4687  }
4688  // End call triggers
4689  }
4690 
4691  if (!$error) {
4692  $this->db->commit();
4693  return 1;
4694  }
4695 
4696  foreach ($this->errors as $errmsg) {
4697  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4698  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4699  }
4700  $this->db->rollback();
4701  return -1 * $error;
4702  } else {
4703  $this->error = $this->db->error();
4704  $this->db->rollback();
4705  return -2;
4706  }
4707  }
4708 
4709  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4716  public function update_total()
4717  {
4718  // phpcs:enable
4719  $this->db->begin();
4720 
4721  // Clean parameters
4722  if (empty($this->total_localtax1)) {
4723  $this->total_localtax1 = 0;
4724  }
4725  if (empty($this->total_localtax2)) {
4726  $this->total_localtax2 = 0;
4727  }
4728 
4729  // Mise a jour ligne en base
4730  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4731  $sql .= " total_ht='".price2num($this->total_ht)."'";
4732  $sql .= ",total_tva='".price2num($this->total_tva)."'";
4733  $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4734  $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4735  $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4736  $sql .= " WHERE rowid = ".((int) $this->rowid);
4737 
4738  dol_syslog("OrderLine::update_total", LOG_DEBUG);
4739 
4740  $resql = $this->db->query($sql);
4741  if ($resql) {
4742  $this->db->commit();
4743  return 1;
4744  } else {
4745  $this->error = $this->db->error();
4746  $this->db->rollback();
4747  return -2;
4748  }
4749  }
4750 }
$object ref
Definition: info.php:78
Class to manage customers orders.
getNbOfServicesLines()
Return number of line with type service.
getNbOfShipments()
Count number of shipments for this order.
deleteline($user=null, $lineid=0, $id=0)
Delete an order line.
createFromProposal($object, User $user)
Load an object from a proposal and create a new order into database.
setDraft($user, $idwarehouse=-1)
Set draft status.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
getLinesArray()
Create an array of order lines.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
showDelay()
Show the customer delayed info.
set_date($user, $date, $notrigger=0)
Set the order date.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetch_lines($only_product=0, $loadalsotranslation=0)
Load array lines.
$pos_source
key of pos source ('0', '1', ...)
createFromClone(User $user, $socid=0)
Load an object from its id and create a new one in database.
const STATUS_SHIPMENTONPROCESS
Shipment on process.
LibStatut($status, $billed, $mode, $donotshowbilled=0)
Return label of status.
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.
getLibStatut($mode)
Return status label of Order.
hasDelay()
Is the sales order delayed?
set_remise_absolue($user, $remise, $notrigger=0)
Set a fixed amount discount.
const STATUS_CLOSED
Closed (Sent, billed or not)
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, $ref_ext='', $rang=0)
Update a line in database.
valid($user, $idwarehouse=0, $notrigger=0)
Validate order.
getLabelSource()
Return source label of order.
set_remise($user, $remise, $notrigger=0)
Applique une remise relative.
const STATUS_CANCELED
Canceled status.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0, $target='')
Return clicable link of object (with eventually picto)
loadExpeditions($filtre_statut=-1, $fk_product=0)
Load array this->expeditions of lines of shipments with nb of products sent for each order line Note:...
__construct($db)
Constructor.
availability($availability_id, $notrigger=0)
Update delivery delay.
set_reopen($user)
Tag the order as validated (opened) Function used when order is reopend after being closed.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
set_ref_client($user, $ref_client, $notrigger=0)
Set customer ref.
getNextNumRef($soc)
Returns the reference to the following non used Order depending on the active numbering module define...
create($user, $notrigger=0)
Create order Note that this->ref can be set or empty.
demand_reason($demand_reason_id, $notrigger=0)
Update order demand_reason.
const STATUS_DRAFT
Draft status.
const STOCK_NOT_ENOUGH_FOR_ORDER
ERR Not enough stock.
initAsSpecimen()
Initialise an instance with random values.
insert_discount($idremise)
Adding line of fixed discount in the order in DB.
getNbOfProductsLines()
Return number of line with type product.
update(User $user, $notrigger=0)
Update database.
$module_source
key of module source when order generated from a dedicated module ('cashdesk', 'takepos',...
classifyUnBilled(User $user, $notrigger=0)
Classify the order as not invoiced.
setDiscount($user, $remise, $notrigger=0)
Set a percentage discount.
stock_array($filtre_statut=self::STATUS_CANCELED)
Return a array with the pending stock by product.
cloture($user, $notrigger=0)
Close order.
nb_expedition()
Returns a array with expeditions lines number.
cancel($idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
classifyBilled(User $user, $notrigger=0)
Classify the order as invoiced.
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, $ref_ext='', $noupdateafterinsertline=0)
Add an order line into database (linked to product/service or not)
info($id)
Charge les informations d'ordre info dans l'objet commande.
const STATUS_VALIDATED
Validated status.
fetch($id, $ref='', $ref_ext='', $notused='')
Get object from database.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
Add line into array $this->client must be loaded.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
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...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
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).
deleteExtraFields()
Delete all extra fields values for the current object.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Superclass for orders classes.
Superclass for orders classes.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class to manage shipments.
Class to manage standard extra fields.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage order lines.
insert($user=null, $notrigger=0)
Insert line into database.
update(User $user, $notrigger=0)
Update the line object into db.
update_total()
Update DB line fields total_xxx Used by migration.
__construct($db)
Constructor.
fetch($rowid)
Load line order.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:45
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1401
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1250
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:61
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1453
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
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='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
isModEnabled($module)
Is Dolibarr module enabled.
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) VAT...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86
$conf db
API class for accounts.
Definition: inc.php:41