dolibarr  17.0.4
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  $this->errors += $hookmanager->errors;
1276  $this->error = $hookmanager->error;
1277  $error++;
1278  }
1279  }
1280  }
1281 
1282  unset($this->context['createfromclone']);
1283 
1284  // End
1285  if (!$error) {
1286  $this->db->commit();
1287  return $this->id;
1288  } else {
1289  $this->db->rollback();
1290  return -1;
1291  }
1292  }
1293 
1294 
1302  public function createFromProposal($object, User $user)
1303  {
1304  global $conf, $hookmanager;
1305 
1306  dol_include_once('/multicurrency/class/multicurrency.class.php');
1307  dol_include_once('/core/class/extrafields.class.php');
1308 
1309  $error = 0;
1310 
1311 
1312  $this->date_commande = dol_now();
1313  $this->source = 0;
1314 
1315  $num = count($object->lines);
1316  for ($i = 0; $i < $num; $i++) {
1317  $line = new OrderLine($this->db);
1318 
1319  $line->libelle = $object->lines[$i]->libelle;
1320  $line->label = $object->lines[$i]->label;
1321  $line->desc = $object->lines[$i]->desc;
1322  $line->price = $object->lines[$i]->price;
1323  $line->subprice = $object->lines[$i]->subprice;
1324  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1325  $line->tva_tx = $object->lines[$i]->tva_tx;
1326  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1327  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1328  $line->qty = $object->lines[$i]->qty;
1329  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1330  $line->remise_percent = $object->lines[$i]->remise_percent;
1331  $line->fk_product = $object->lines[$i]->fk_product;
1332  $line->info_bits = $object->lines[$i]->info_bits;
1333  $line->product_type = $object->lines[$i]->product_type;
1334  $line->rang = $object->lines[$i]->rang;
1335  $line->special_code = $object->lines[$i]->special_code;
1336  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1337  $line->fk_unit = $object->lines[$i]->fk_unit;
1338 
1339  $line->date_start = $object->lines[$i]->date_start;
1340  $line->date_end = $object->lines[$i]->date_end;
1341 
1342  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1343  $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);
1344  $line->pa_ht = $marginInfos[0];
1345  $line->marge_tx = $marginInfos[1];
1346  $line->marque_tx = $marginInfos[2];
1347 
1348  $line->origin = $object->element;
1349  $line->origin_id = $object->lines[$i]->id;
1350 
1351  // get extrafields from original line
1352  $object->lines[$i]->fetch_optionals();
1353  foreach ($object->lines[$i]->array_options as $options_key => $value) {
1354  $line->array_options[$options_key] = $value;
1355  }
1356 
1357  $this->lines[$i] = $line;
1358  }
1359 
1360  $this->entity = $object->entity;
1361  $this->socid = $object->socid;
1362  $this->fk_project = $object->fk_project;
1363  $this->cond_reglement_id = $object->cond_reglement_id;
1364  $this->deposit_percent = $object->deposit_percent;
1365  $this->mode_reglement_id = $object->mode_reglement_id;
1366  $this->fk_account = $object->fk_account;
1367  $this->availability_id = $object->availability_id;
1368  $this->demand_reason_id = $object->demand_reason_id;
1369  $this->date_livraison = $object->date_livraison; // deprecated
1370  $this->delivery_date = $object->date_livraison;
1371  $this->shipping_method_id = $object->shipping_method_id;
1372  $this->warehouse_id = $object->warehouse_id;
1373  $this->fk_delivery_address = $object->fk_delivery_address;
1374  $this->contact_id = $object->contact_id;
1375  $this->ref_client = $object->ref_client;
1376 
1377  if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) {
1378  $this->note_private = $object->note_private;
1379  $this->note_public = $object->note_public;
1380  }
1381 
1382  $this->origin = $object->element;
1383  $this->origin_id = $object->id;
1384 
1385  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1386  if (!empty($conf->multicurrency->enabled)) {
1387  if (!empty($object->multicurrency_code)) {
1388  $this->multicurrency_code = $object->multicurrency_code;
1389  }
1390  if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($object->multicurrency_tx)) {
1391  $this->multicurrency_tx = $object->multicurrency_tx;
1392  }
1393 
1394  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1395  $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1396  $this->fk_multicurrency = $tmparray[0];
1397  $this->multicurrency_tx = $tmparray[1];
1398  } else {
1399  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1400  }
1401  if (empty($this->fk_multicurrency)) {
1402  $this->multicurrency_code = $conf->currency;
1403  $this->fk_multicurrency = 0;
1404  $this->multicurrency_tx = 1;
1405  }
1406  }
1407 
1408  // get extrafields from original line
1409  $object->fetch_optionals();
1410 
1411  $e = new ExtraFields($this->db);
1412  $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1413 
1414  foreach ($object->array_options as $options_key => $value) {
1415  if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1416  $this->array_options[$options_key] = $value;
1417  }
1418  }
1419  // Possibility to add external linked objects with hooks
1420  $this->linked_objects[$this->origin] = $this->origin_id;
1421  if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1422  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1423  }
1424 
1425  $ret = $this->create($user);
1426 
1427  if ($ret > 0) {
1428  // Actions hooked (by external module)
1429  $hookmanager->initHooks(array('orderdao'));
1430 
1431  $parameters = array('objFrom'=>$object);
1432  $action = '';
1433  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1434  if ($reshook < 0) {
1435  $this->errors += $hookmanager->errors;
1436  $this->error = $hookmanager->error;
1437  $error++;
1438  }
1439 
1440  if (!$error) {
1441  // Validate immediatly the order
1442  if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL)) {
1443  $this->fetch($ret);
1444  $this->valid($user);
1445  }
1446  return $ret;
1447  } else {
1448  return -1;
1449  }
1450  } else {
1451  return -1;
1452  }
1453  }
1454 
1455 
1496  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)
1497  {
1498  global $mysoc, $conf, $langs, $user;
1499 
1500  $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1501  $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";
1502  $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";
1503  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1504 
1505  if ($this->statut == self::STATUS_DRAFT) {
1506  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1507 
1508  // Clean parameters
1509 
1510  if (empty($remise_percent)) {
1511  $remise_percent = 0;
1512  }
1513  if (empty($qty)) {
1514  $qty = 0;
1515  }
1516  if (empty($info_bits)) {
1517  $info_bits = 0;
1518  }
1519  if (empty($rang)) {
1520  $rang = 0;
1521  }
1522  if (empty($txtva)) {
1523  $txtva = 0;
1524  }
1525  if (empty($txlocaltax1)) {
1526  $txlocaltax1 = 0;
1527  }
1528  if (empty($txlocaltax2)) {
1529  $txlocaltax2 = 0;
1530  }
1531  if (empty($fk_parent_line) || $fk_parent_line < 0) {
1532  $fk_parent_line = 0;
1533  }
1534  if (empty($this->fk_multicurrency)) {
1535  $this->fk_multicurrency = 0;
1536  }
1537  if (empty($ref_ext)) {
1538  $ref_ext = '';
1539  }
1540 
1541  $remise_percent = price2num($remise_percent);
1542  $qty = price2num($qty);
1543  $pu_ht = price2num($pu_ht);
1544  $pu_ht_devise = price2num($pu_ht_devise);
1545  $pu_ttc = price2num($pu_ttc);
1546  $pa_ht = price2num($pa_ht);
1547  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1548  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1549  }
1550  $txlocaltax1 = price2num($txlocaltax1);
1551  $txlocaltax2 = price2num($txlocaltax2);
1552  if ($price_base_type == 'HT') {
1553  $pu = $pu_ht;
1554  } else {
1555  $pu = $pu_ttc;
1556  }
1557  $label = trim($label);
1558  $desc = trim($desc);
1559 
1560  // Check parameters
1561  if ($type < 0) {
1562  return -1;
1563  }
1564 
1565  if ($date_start && $date_end && $date_start > $date_end) {
1566  $langs->load("errors");
1567  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1568  return -1;
1569  }
1570 
1571  $this->db->begin();
1572 
1573  $product_type = $type;
1574  if (!empty($fk_product) && $fk_product > 0) {
1575  $product = new Product($this->db);
1576  $result = $product->fetch($fk_product);
1577  $product_type = $product->type;
1578 
1579  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
1580  $langs->load("errors");
1581  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1582  $this->errors[] = $this->error;
1583  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1584  $this->db->rollback();
1586  }
1587  }
1588  // Calcul du total TTC et de la TVA pour la ligne a partir de
1589  // qty, pu, remise_percent et txtva
1590  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1591  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1592 
1593  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1594 
1595  // Clean vat code
1596  $reg = array();
1597  $vat_src_code = '';
1598  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1599  $vat_src_code = $reg[1];
1600  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1601  }
1602 
1603  $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);
1604 
1605  /*var_dump($txlocaltax1);
1606  var_dump($txlocaltax2);
1607  var_dump($localtaxes_type);
1608  var_dump($tabprice);
1609  var_dump($tabprice[9]);
1610  var_dump($tabprice[10]);
1611  exit;*/
1612 
1613  $total_ht = $tabprice[0];
1614  $total_tva = $tabprice[1];
1615  $total_ttc = $tabprice[2];
1616  $total_localtax1 = $tabprice[9];
1617  $total_localtax2 = $tabprice[10];
1618  $pu_ht = $tabprice[3];
1619 
1620  // MultiCurrency
1621  $multicurrency_total_ht = $tabprice[16];
1622  $multicurrency_total_tva = $tabprice[17];
1623  $multicurrency_total_ttc = $tabprice[18];
1624  $pu_ht_devise = $tabprice[19];
1625 
1626  // Rang to use
1627  $ranktouse = $rang;
1628  if ($ranktouse == -1) {
1629  $rangmax = $this->line_max($fk_parent_line);
1630  $ranktouse = $rangmax + 1;
1631  }
1632 
1633  // TODO A virer
1634  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1635  $price = $pu;
1636  $remise = 0;
1637  if ($remise_percent > 0) {
1638  $remise = round(($pu * $remise_percent / 100), 2);
1639  $price = $pu - $remise;
1640  }
1641 
1642  // Insert line
1643  $this->line = new OrderLine($this->db);
1644 
1645  $this->line->context = $this->context;
1646 
1647  $this->line->fk_commande = $this->id;
1648  $this->line->label = $label;
1649  $this->line->desc = $desc;
1650  $this->line->qty = $qty;
1651  $this->line->ref_ext = $ref_ext;
1652 
1653  $this->line->vat_src_code = $vat_src_code;
1654  $this->line->tva_tx = $txtva;
1655  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1656  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1657  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1658  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1659  $this->line->fk_product = $fk_product;
1660  $this->line->product_type = $product_type;
1661  $this->line->fk_remise_except = $fk_remise_except;
1662  $this->line->remise_percent = $remise_percent;
1663  $this->line->subprice = $pu_ht;
1664  $this->line->rang = $ranktouse;
1665  $this->line->info_bits = $info_bits;
1666  $this->line->total_ht = $total_ht;
1667  $this->line->total_tva = $total_tva;
1668  $this->line->total_localtax1 = $total_localtax1;
1669  $this->line->total_localtax2 = $total_localtax2;
1670  $this->line->total_ttc = $total_ttc;
1671  $this->line->special_code = $special_code;
1672  $this->line->origin = $origin;
1673  $this->line->origin_id = $origin_id;
1674  $this->line->fk_parent_line = $fk_parent_line;
1675  $this->line->fk_unit = $fk_unit;
1676 
1677  $this->line->date_start = $date_start;
1678  $this->line->date_end = $date_end;
1679 
1680  $this->line->fk_fournprice = $fk_fournprice;
1681  $this->line->pa_ht = $pa_ht;
1682 
1683  // Multicurrency
1684  $this->line->fk_multicurrency = $this->fk_multicurrency;
1685  $this->line->multicurrency_code = $this->multicurrency_code;
1686  $this->line->multicurrency_subprice = $pu_ht_devise;
1687  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1688  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1689  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1690 
1691  // TODO Ne plus utiliser
1692  $this->line->price = $price;
1693 
1694  if (is_array($array_options) && count($array_options) > 0) {
1695  $this->line->array_options = $array_options;
1696  }
1697 
1698  $result = $this->line->insert($user);
1699  if ($result > 0) {
1700  // Reorder if child line
1701  if (!empty($fk_parent_line)) {
1702  $this->line_order(true, 'DESC');
1703  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1704  $linecount = count($this->lines);
1705  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1706  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1707  }
1708  }
1709 
1710  // Mise a jour informations denormalisees au niveau de la commande meme
1711  if (empty($noupdateafterinsertline)) {
1712  $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.
1713  }
1714 
1715  if ($result > 0) {
1716  $this->db->commit();
1717  return $this->line->id;
1718  } else {
1719  $this->db->rollback();
1720  return -1;
1721  }
1722  } else {
1723  $this->error = $this->line->error;
1724  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1725  $this->db->rollback();
1726  return -2;
1727  }
1728  } else {
1729  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1730  return -3;
1731  }
1732  }
1733 
1734 
1735  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1749  public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1750  {
1751  // phpcs:enable
1752  global $conf, $mysoc;
1753 
1754  if (!$qty) {
1755  $qty = 1;
1756  }
1757 
1758  if ($idproduct > 0) {
1759  $prod = new Product($this->db);
1760  $prod->fetch($idproduct);
1761 
1762  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1763  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1764  if (empty($tva_tx)) {
1765  $tva_npr = 0;
1766  }
1767  $vat_src_code = ''; // May be defined into tva_tx
1768 
1769  $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1770  $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1771 
1772  // multiprix
1773  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1774  $price = $prod->multiprices[$this->thirdparty->price_level];
1775  } else {
1776  $price = $prod->price;
1777  }
1778 
1779  $line = new OrderLine($this->db);
1780 
1781  $line->context = $this->context;
1782 
1783  $line->fk_product = $idproduct;
1784  $line->desc = $prod->description;
1785  $line->qty = $qty;
1786  $line->subprice = $price;
1787  $line->remise_percent = $remise_percent;
1788  $line->vat_src_code = $vat_src_code;
1789  $line->tva_tx = $tva_tx;
1790  $line->localtax1_tx = $localtax1_tx;
1791  $line->localtax2_tx = $localtax2_tx;
1792  $line->ref = $prod->ref;
1793  $line->libelle = $prod->label;
1794  $line->product_desc = $prod->description;
1795  $line->fk_unit = $prod->fk_unit;
1796 
1797  // Save the start and end date of the line in the object
1798  if ($date_start) {
1799  $line->date_start = $date_start;
1800  }
1801  if ($date_end) {
1802  $line->date_end = $date_end;
1803  }
1804 
1805  $this->lines[] = $line;
1806 
1825  }
1826  }
1827 
1828 
1838  public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1839  {
1840  // Check parameters
1841  if (empty($id) && empty($ref) && empty($ref_ext)) {
1842  return -1;
1843  }
1844 
1845  $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';
1846  $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';
1847  $sql .= ', c.fk_account';
1848  $sql .= ', c.date_commande, c.date_valid, c.tms';
1849  $sql .= ', c.date_livraison as delivery_date';
1850  $sql .= ', c.fk_shipping_method';
1851  $sql .= ', c.fk_warehouse';
1852  $sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1853  $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';
1854  $sql .= ', c.fk_incoterms, c.location_incoterms';
1855  $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1856  $sql .= ", c.module_source, c.pos_source";
1857  $sql .= ", i.libelle as label_incoterms";
1858  $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1859  $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1860  $sql .= ', ca.code as availability_code, ca.label as availability_label';
1861  $sql .= ', dr.code as demand_reason_code';
1862  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1863  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1864  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1865  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1866  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1867  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1868 
1869  if ($id) {
1870  $sql .= " WHERE c.rowid=".((int) $id);
1871  } else {
1872  $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1873  }
1874 
1875  if ($ref) {
1876  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1877  }
1878  if ($ref_ext) {
1879  $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1880  }
1881 
1882  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1883  $result = $this->db->query($sql);
1884  if ($result) {
1885  $obj = $this->db->fetch_object($result);
1886  if ($obj) {
1887  $this->id = $obj->rowid;
1888  $this->entity = $obj->entity;
1889 
1890  $this->ref = $obj->ref;
1891  $this->ref_client = $obj->ref_client;
1892  $this->ref_customer = $obj->ref_client;
1893  $this->ref_ext = $obj->ref_ext;
1894 
1895  $this->socid = $obj->fk_soc;
1896  $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1897 
1898  $this->fk_project = $obj->fk_project;
1899  $this->project = null; // Clear if another value was already set by fetch_projet
1900 
1901  $this->statut = $obj->fk_statut;
1902  $this->status = $obj->fk_statut;
1903 
1904  $this->user_author_id = $obj->fk_user_author;
1905  $this->user_creation_id = $obj->fk_user_author;
1906  $this->user_validation_id = $obj->fk_user_valid;
1907  $this->user_valid = $obj->fk_user_valid; // deprecated
1908  $this->user_modification_id = $obj->fk_user_modif;
1909  $this->user_modification = $obj->fk_user_modif;
1910  $this->total_ht = $obj->total_ht;
1911  $this->total_tva = $obj->total_tva;
1912  $this->total_localtax1 = $obj->total_localtax1;
1913  $this->total_localtax2 = $obj->total_localtax2;
1914  $this->total_ttc = $obj->total_ttc;
1915  $this->date = $this->db->jdate($obj->date_commande);
1916  $this->date_commande = $this->db->jdate($obj->date_commande);
1917  $this->date_creation = $this->db->jdate($obj->date_creation);
1918  $this->date_validation = $this->db->jdate($obj->date_valid);
1919  $this->date_modification = $this->db->jdate($obj->tms);
1920  $this->remise = $obj->remise;
1921  $this->remise_percent = $obj->remise_percent;
1922  $this->remise_absolue = $obj->remise_absolue;
1923  $this->source = $obj->source;
1924  $this->billed = $obj->billed;
1925  $this->note = $obj->note_private; // deprecated
1926  $this->note_private = $obj->note_private;
1927  $this->note_public = $obj->note_public;
1928  $this->model_pdf = $obj->model_pdf;
1929  $this->modelpdf = $obj->model_pdf; // deprecated
1930  $this->last_main_doc = $obj->last_main_doc;
1931  $this->mode_reglement_id = $obj->fk_mode_reglement;
1932  $this->mode_reglement_code = $obj->mode_reglement_code;
1933  $this->mode_reglement = $obj->mode_reglement_libelle;
1934  $this->cond_reglement_id = $obj->fk_cond_reglement;
1935  $this->cond_reglement_code = $obj->cond_reglement_code;
1936  $this->cond_reglement = $obj->cond_reglement_libelle;
1937  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1938  $this->deposit_percent = $obj->deposit_percent;
1939  $this->fk_account = $obj->fk_account;
1940  $this->availability_id = $obj->fk_availability;
1941  $this->availability_code = $obj->availability_code;
1942  $this->availability = $obj->availability_label;
1943  $this->demand_reason_id = $obj->fk_input_reason;
1944  $this->demand_reason_code = $obj->demand_reason_code;
1945  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1946  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1947  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1948  $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1949  $this->fk_delivery_address = $obj->fk_delivery_address;
1950  $this->module_source = $obj->module_source;
1951  $this->pos_source = $obj->pos_source;
1952 
1953  //Incoterms
1954  $this->fk_incoterms = $obj->fk_incoterms;
1955  $this->location_incoterms = $obj->location_incoterms;
1956  $this->label_incoterms = $obj->label_incoterms;
1957 
1958  // Multicurrency
1959  $this->fk_multicurrency = $obj->fk_multicurrency;
1960  $this->multicurrency_code = $obj->multicurrency_code;
1961  $this->multicurrency_tx = $obj->multicurrency_tx;
1962  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1963  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1964  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1965 
1966  $this->extraparams = (array) json_decode($obj->extraparams, true);
1967 
1968  $this->lines = array();
1969 
1970  if ($this->statut == self::STATUS_DRAFT) {
1971  $this->brouillon = 1;
1972  }
1973 
1974  // Retrieve all extrafield
1975  // fetch optionals attributes and labels
1976  $this->fetch_optionals();
1977 
1978  $this->db->free($result);
1979 
1980  // Lines
1981  $result = $this->fetch_lines();
1982  if ($result < 0) {
1983  return -3;
1984  }
1985  return 1;
1986  } else {
1987  $this->error = 'Order with id '.$id.' not found sql='.$sql;
1988  return 0;
1989  }
1990  } else {
1991  $this->error = $this->db->error();
1992  return -1;
1993  }
1994  }
1995 
1996 
1997  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2004  public function insert_discount($idremise)
2005  {
2006  // phpcs:enable
2007  global $langs;
2008 
2009  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2010  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2011 
2012  $this->db->begin();
2013 
2014  $remise = new DiscountAbsolute($this->db);
2015  $result = $remise->fetch($idremise);
2016 
2017  if ($result > 0) {
2018  if ($remise->fk_facture) { // Protection against multiple submission
2019  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2020  $this->db->rollback();
2021  return -5;
2022  }
2023 
2024  $line = new OrderLine($this->db);
2025 
2026  $line->fk_commande = $this->id;
2027  $line->fk_remise_except = $remise->id;
2028  $line->desc = $remise->description; // Description ligne
2029  $line->vat_src_code = $remise->vat_src_code;
2030  $line->tva_tx = $remise->tva_tx;
2031  $line->subprice = -$remise->amount_ht;
2032  $line->price = -$remise->amount_ht;
2033  $line->fk_product = 0; // Id produit predefini
2034  $line->qty = 1;
2035  $line->remise_percent = 0;
2036  $line->rang = -1;
2037  $line->info_bits = 2;
2038 
2039  $line->total_ht = -$remise->amount_ht;
2040  $line->total_tva = -$remise->amount_tva;
2041  $line->total_ttc = -$remise->amount_ttc;
2042 
2043  $result = $line->insert();
2044  if ($result > 0) {
2045  $result = $this->update_price(1);
2046  if ($result > 0) {
2047  $this->db->commit();
2048  return 1;
2049  } else {
2050  $this->db->rollback();
2051  return -1;
2052  }
2053  } else {
2054  $this->error = $line->error;
2055  $this->errors = $line->errors;
2056  $this->db->rollback();
2057  return -2;
2058  }
2059  } else {
2060  $this->db->rollback();
2061  return -2;
2062  }
2063  }
2064 
2065 
2066  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2074  public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2075  {
2076  // phpcs:enable
2077  global $langs, $conf;
2078 
2079  $this->lines = array();
2080 
2081  $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,';
2082  $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,';
2083  $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2084  $sql .= ' l.fk_unit,';
2085  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2086  $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,';
2087  $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2088  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2089  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2090  $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2091  if ($only_product) {
2092  $sql .= ' AND p.fk_product_type = 0';
2093  }
2094  $sql .= ' ORDER BY l.rang, l.rowid';
2095 
2096  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2097  $result = $this->db->query($sql);
2098  if ($result) {
2099  $num = $this->db->num_rows($result);
2100 
2101  $i = 0;
2102  while ($i < $num) {
2103  $objp = $this->db->fetch_object($result);
2104 
2105  $line = new OrderLine($this->db);
2106 
2107  $line->rowid = $objp->rowid;
2108  $line->id = $objp->rowid;
2109  $line->fk_commande = $objp->fk_commande;
2110  $line->commande_id = $objp->fk_commande;
2111  $line->label = $objp->custom_label;
2112  $line->desc = $objp->description;
2113  $line->description = $objp->description; // Description line
2114  $line->product_type = $objp->product_type;
2115  $line->qty = $objp->qty;
2116  $line->ref_ext = $objp->ref_ext;
2117 
2118  $line->vat_src_code = $objp->vat_src_code;
2119  $line->tva_tx = $objp->tva_tx;
2120  $line->localtax1_tx = $objp->localtax1_tx;
2121  $line->localtax2_tx = $objp->localtax2_tx;
2122  $line->localtax1_type = $objp->localtax1_type;
2123  $line->localtax2_type = $objp->localtax2_type;
2124  $line->total_ht = $objp->total_ht;
2125  $line->total_ttc = $objp->total_ttc;
2126  $line->total_tva = $objp->total_tva;
2127  $line->total_localtax1 = $objp->total_localtax1;
2128  $line->total_localtax2 = $objp->total_localtax2;
2129  $line->subprice = $objp->subprice;
2130  $line->fk_remise_except = $objp->fk_remise_except;
2131  $line->remise_percent = $objp->remise_percent;
2132  $line->price = $objp->price;
2133  $line->fk_product = $objp->fk_product;
2134  $line->fk_fournprice = $objp->fk_fournprice;
2135  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2136  $line->pa_ht = $marginInfos[0];
2137  $line->marge_tx = $marginInfos[1];
2138  $line->marque_tx = $marginInfos[2];
2139  $line->rang = $objp->rang;
2140  $line->info_bits = $objp->info_bits;
2141  $line->special_code = $objp->special_code;
2142  $line->fk_parent_line = $objp->fk_parent_line;
2143 
2144  $line->ref = $objp->product_ref;
2145  $line->libelle = $objp->product_label;
2146 
2147  $line->product_ref = $objp->product_ref;
2148  $line->product_label = $objp->product_label;
2149  $line->product_tosell = $objp->product_tosell;
2150  $line->product_tobuy = $objp->product_tobuy;
2151  $line->product_desc = $objp->product_desc;
2152  $line->product_tobatch = $objp->product_tobatch;
2153  $line->product_barcode = $objp->product_barcode;
2154 
2155  $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2156  $line->fk_unit = $objp->fk_unit;
2157 
2158  $line->weight = $objp->weight;
2159  $line->weight_units = $objp->weight_units;
2160  $line->volume = $objp->volume;
2161  $line->volume_units = $objp->volume_units;
2162 
2163  $line->date_start = $this->db->jdate($objp->date_start);
2164  $line->date_end = $this->db->jdate($objp->date_end);
2165 
2166  // Multicurrency
2167  $line->fk_multicurrency = $objp->fk_multicurrency;
2168  $line->multicurrency_code = $objp->multicurrency_code;
2169  $line->multicurrency_subprice = $objp->multicurrency_subprice;
2170  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2171  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2172  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2173 
2174  $line->fetch_optionals();
2175 
2176  // multilangs
2177  if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2178  $tmpproduct = new Product($this->db);
2179  $tmpproduct->fetch($objp->fk_product);
2180  $tmpproduct->getMultiLangs();
2181 
2182  $line->multilangs = $tmpproduct->multilangs;
2183  }
2184 
2185  $this->lines[$i] = $line;
2186 
2187  $i++;
2188  }
2189 
2190  $this->db->free($result);
2191 
2192  return 1;
2193  } else {
2194  $this->error = $this->db->error();
2195  return -3;
2196  }
2197  }
2198 
2199 
2205  public function getNbOfProductsLines()
2206  {
2207  $nb = 0;
2208  foreach ($this->lines as $line) {
2209  if ($line->product_type == 0) {
2210  $nb++;
2211  }
2212  }
2213  return $nb;
2214  }
2215 
2221  public function getNbOfServicesLines()
2222  {
2223  $nb = 0;
2224  foreach ($this->lines as $line) {
2225  if ($line->product_type == 1) {
2226  $nb++;
2227  }
2228  }
2229  return $nb;
2230  }
2231 
2237  public function getNbOfShipments()
2238  {
2239  $nb = 0;
2240 
2241  $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2242  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2243  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2244  $sql .= ' WHERE';
2245  $sql .= ' ed.fk_origin_line = cd.rowid';
2246  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2247  //print $sql;
2248 
2249  dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2250  $resql = $this->db->query($sql);
2251  if ($resql) {
2252  $obj = $this->db->fetch_object($resql);
2253  if ($obj) {
2254  $nb = $obj->nb;
2255  }
2256 
2257  $this->db->free($resql);
2258  return $nb;
2259  } else {
2260  $this->error = $this->db->lasterror();
2261  return -1;
2262  }
2263  }
2264 
2273  public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2274  {
2275  $this->expeditions = array();
2276 
2277  $sql = 'SELECT cd.rowid, cd.fk_product,';
2278  $sql .= ' sum(ed.qty) as qty';
2279  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2280  if ($filtre_statut >= 0) {
2281  $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2282  }
2283  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2284  $sql .= ' WHERE';
2285  if ($filtre_statut >= 0) {
2286  $sql .= ' ed.fk_expedition = e.rowid AND';
2287  }
2288  $sql .= ' ed.fk_origin_line = cd.rowid';
2289  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2290  if ($fk_product > 0) {
2291  $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2292  }
2293  if ($filtre_statut >= 0) {
2294  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2295  }
2296  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2297  //print $sql;
2298 
2299  dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2300  $resql = $this->db->query($sql);
2301  if ($resql) {
2302  $num = $this->db->num_rows($resql);
2303  $i = 0;
2304  while ($i < $num) {
2305  $obj = $this->db->fetch_object($resql);
2306  $this->expeditions[$obj->rowid] = $obj->qty;
2307  $i++;
2308  }
2309  $this->db->free($resql);
2310  return $num;
2311  } else {
2312  $this->error = $this->db->lasterror();
2313  return -1;
2314  }
2315  }
2316 
2317  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2325  public function nb_expedition()
2326  {
2327  // phpcs:enable
2328  $sql = 'SELECT count(*)';
2329  $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2330  $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2331  $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2332  $sql .= " AND el.sourcetype = 'commande'";
2333  $sql .= " AND el.fk_target = e.rowid";
2334  $sql .= " AND el.targettype = 'shipping'";
2335 
2336  $resql = $this->db->query($sql);
2337  if ($resql) {
2338  $row = $this->db->fetch_row($resql);
2339  return $row[0];
2340  } else {
2341  dol_print_error($this->db);
2342  }
2343  }
2344 
2345  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2354  public function stock_array($filtre_statut = self::STATUS_CANCELED)
2355  {
2356  // phpcs:enable
2357  $this->stocks = array();
2358 
2359  // Tableau des id de produit de la commande
2360  $array_of_product = array();
2361 
2362  // Recherche total en stock pour chaque produit
2363  // TODO $array_of_product est défini vide juste au dessus !!
2364  if (count($array_of_product)) {
2365  $sql = "SELECT fk_product, sum(ps.reel) as total";
2366  $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2367  $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2368  $sql .= ' GROUP BY fk_product';
2369  $resql = $this->db->query($sql);
2370  if ($resql) {
2371  $num = $this->db->num_rows($resql);
2372  $i = 0;
2373  while ($i < $num) {
2374  $obj = $this->db->fetch_object($resql);
2375  $this->stocks[$obj->fk_product] = $obj->total;
2376  $i++;
2377  }
2378  $this->db->free($resql);
2379  }
2380  }
2381  return 0;
2382  }
2383 
2391  public function deleteline($user = null, $lineid = 0)
2392  {
2393  if ($this->statut == self::STATUS_DRAFT) {
2394  $this->db->begin();
2395 
2396  $sql = "SELECT fk_product, qty";
2397  $sql .= " FROM ".MAIN_DB_PREFIX."commandedet";
2398  $sql .= " WHERE rowid = ".((int) $lineid);
2399 
2400  $result = $this->db->query($sql);
2401  if ($result) {
2402  $obj = $this->db->fetch_object($result);
2403 
2404  if ($obj) {
2405  $product = new Product($this->db);
2406  $product->id = $obj->fk_product;
2407 
2408  // Delete line
2409  $line = new OrderLine($this->db);
2410 
2411  // For triggers
2412  $line->fetch($lineid);
2413 
2414  // Memorize previous line for triggers
2415  $staticline = clone $line;
2416  $line->oldline = $staticline;
2417 
2418  if ($line->delete($user) > 0) {
2419  $result = $this->update_price(1);
2420 
2421  if ($result > 0) {
2422  $this->db->commit();
2423  return 1;
2424  } else {
2425  $this->db->rollback();
2426  $this->error = $this->db->lasterror();
2427  return -1;
2428  }
2429  } else {
2430  $this->db->rollback();
2431  $this->error = $line->error;
2432  return -1;
2433  }
2434  } else {
2435  $this->db->rollback();
2436  return 0;
2437  }
2438  } else {
2439  $this->db->rollback();
2440  $this->error = $this->db->lasterror();
2441  return -1;
2442  }
2443  } else {
2444  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2445  return -1;
2446  }
2447  }
2448 
2449  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2460  public function set_remise($user, $remise, $notrigger = 0)
2461  {
2462  // phpcs:enable
2463  dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2464  return $this->setDiscount($user, $remise, $notrigger);
2465  }
2466 
2475  public function setDiscount($user, $remise, $notrigger = 0)
2476  {
2477  $remise = trim($remise) ?trim($remise) : 0;
2478 
2479  if ($user->rights->commande->creer) {
2480  $error = 0;
2481 
2482  $this->db->begin();
2483 
2484  $remise = price2num($remise, 2);
2485 
2486  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2487  $sql .= ' SET remise_percent = '.((float) $remise);
2488  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2489 
2490  dol_syslog(__METHOD__, LOG_DEBUG);
2491  $resql = $this->db->query($sql);
2492  if (!$resql) {
2493  $this->errors[] = $this->db->error();
2494  $error++;
2495  }
2496 
2497  if (!$error) {
2498  $this->oldcopy = clone $this;
2499  $this->remise_percent = $remise;
2500  $this->update_price(1);
2501  }
2502 
2503  if (!$notrigger && empty($error)) {
2504  // Call trigger
2505  $result = $this->call_trigger('ORDER_MODIFY', $user);
2506  if ($result < 0) {
2507  $error++;
2508  }
2509  // End call triggers
2510  }
2511 
2512  if (!$error) {
2513  $this->db->commit();
2514  return 1;
2515  } else {
2516  foreach ($this->errors as $errmsg) {
2517  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2518  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2519  }
2520  $this->db->rollback();
2521  return -1 * $error;
2522  }
2523  }
2524  }
2525 
2526 
2527  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2536  public function set_remise_absolue($user, $remise, $notrigger = 0)
2537  {
2538  // phpcs:enable
2539  if (empty($remise)) {
2540  $remise = 0;
2541  }
2542 
2543  $remise = price2num($remise);
2544 
2545  if ($user->rights->commande->creer) {
2546  $error = 0;
2547 
2548  $this->db->begin();
2549 
2550  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2551  $sql .= ' SET remise_absolue = '.((float) $remise);
2552  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2553 
2554  dol_syslog(__METHOD__, LOG_DEBUG);
2555  $resql = $this->db->query($sql);
2556  if (!$resql) {
2557  $this->errors[] = $this->db->error();
2558  $error++;
2559  }
2560 
2561  if (!$error) {
2562  $this->oldcopy = clone $this;
2563  $this->remise_absolue = $remise;
2564  $this->update_price(1);
2565  }
2566 
2567  if (!$notrigger && empty($error)) {
2568  // Call trigger
2569  $result = $this->call_trigger('ORDER_MODIFY', $user);
2570  if ($result < 0) {
2571  $error++;
2572  }
2573  // End call triggers
2574  }
2575 
2576  if (!$error) {
2577  $this->db->commit();
2578  return 1;
2579  } else {
2580  foreach ($this->errors as $errmsg) {
2581  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2582  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2583  }
2584  $this->db->rollback();
2585  return -1 * $error;
2586  }
2587  }
2588  }
2589 
2590 
2591  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2600  public function set_date($user, $date, $notrigger = 0)
2601  {
2602  // phpcs:enable
2603  if ($user->rights->commande->creer) {
2604  $error = 0;
2605 
2606  $this->db->begin();
2607 
2608  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2609  $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2610  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2611 
2612  dol_syslog(__METHOD__, LOG_DEBUG);
2613  $resql = $this->db->query($sql);
2614  if (!$resql) {
2615  $this->errors[] = $this->db->error();
2616  $error++;
2617  }
2618 
2619  if (!$error) {
2620  $this->oldcopy = clone $this;
2621  $this->date = $date;
2622  }
2623 
2624  if (!$notrigger && empty($error)) {
2625  // Call trigger
2626  $result = $this->call_trigger('ORDER_MODIFY', $user);
2627  if ($result < 0) {
2628  $error++;
2629  }
2630  // End call triggers
2631  }
2632 
2633  if (!$error) {
2634  $this->db->commit();
2635  return 1;
2636  } else {
2637  foreach ($this->errors as $errmsg) {
2638  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2639  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2640  }
2641  $this->db->rollback();
2642  return -1 * $error;
2643  }
2644  } else {
2645  return -2;
2646  }
2647  }
2648 
2649  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2659  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2660  {
2661  // phpcs:enable
2662  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2663  }
2664 
2673  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2674  {
2675  if ($user->rights->commande->creer) {
2676  $error = 0;
2677 
2678  $this->db->begin();
2679 
2680  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2681  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2682  $sql .= " WHERE rowid = ".((int) $this->id);
2683 
2684  dol_syslog(__METHOD__, LOG_DEBUG);
2685  $resql = $this->db->query($sql);
2686  if (!$resql) {
2687  $this->errors[] = $this->db->error();
2688  $error++;
2689  }
2690 
2691  if (!$error) {
2692  $this->oldcopy = clone $this;
2693  $this->date_livraison = $delivery_date;
2694  $this->delivery_date = $delivery_date;
2695  }
2696 
2697  if (!$notrigger && empty($error)) {
2698  // Call trigger
2699  $result = $this->call_trigger('ORDER_MODIFY', $user);
2700  if ($result < 0) {
2701  $error++;
2702  }
2703  // End call triggers
2704  }
2705 
2706  if (!$error) {
2707  $this->db->commit();
2708  return 1;
2709  } else {
2710  foreach ($this->errors as $errmsg) {
2711  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2712  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2713  }
2714  $this->db->rollback();
2715  return -1 * $error;
2716  }
2717  } else {
2718  return -2;
2719  }
2720  }
2721 
2722  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2736  public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2737  {
2738  // phpcs:enable
2739  global $user;
2740 
2741  $ga = array();
2742 
2743  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2744  $sql .= " c.rowid as cid, c.ref";
2745  if (empty($user->rights->societe->client->voir) && !$socid) {
2746  $sql .= ", sc.fk_soc, sc.fk_user";
2747  }
2748  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2749  if (empty($user->rights->societe->client->voir) && !$socid) {
2750  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2751  }
2752  $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2753  $sql .= " AND c.fk_soc = s.rowid";
2754  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2755  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2756  }
2757  if ($socid) {
2758  $sql .= " AND s.rowid = ".((int) $socid);
2759  }
2760  if ($draft) {
2761  $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2762  }
2763  if (is_object($excluser)) {
2764  $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2765  }
2766  $sql .= $this->db->order($sortfield, $sortorder);
2767  $sql .= $this->db->plimit($limit, $offset);
2768 
2769  $result = $this->db->query($sql);
2770  if ($result) {
2771  $numc = $this->db->num_rows($result);
2772  if ($numc) {
2773  $i = 0;
2774  while ($i < $numc) {
2775  $obj = $this->db->fetch_object($result);
2776 
2777  if ($shortlist == 1) {
2778  $ga[$obj->cid] = $obj->ref;
2779  } elseif ($shortlist == 2) {
2780  $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2781  } else {
2782  $ga[$i]['id'] = $obj->cid;
2783  $ga[$i]['ref'] = $obj->ref;
2784  $ga[$i]['name'] = $obj->name;
2785  }
2786  $i++;
2787  }
2788  }
2789  return $ga;
2790  } else {
2791  dol_print_error($this->db);
2792  return -1;
2793  }
2794  }
2795 
2803  public function availability($availability_id, $notrigger = 0)
2804  {
2805  global $user;
2806 
2807  dol_syslog('Commande::availability('.$availability_id.')');
2808  if ($this->statut >= self::STATUS_DRAFT) {
2809  $error = 0;
2810 
2811  $this->db->begin();
2812 
2813  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2814  $sql .= ' SET fk_availability = '.((int) $availability_id);
2815  $sql .= ' WHERE rowid='.((int) $this->id);
2816 
2817  dol_syslog(__METHOD__, LOG_DEBUG);
2818  $resql = $this->db->query($sql);
2819  if (!$resql) {
2820  $this->errors[] = $this->db->error();
2821  $error++;
2822  }
2823 
2824  if (!$error) {
2825  $this->oldcopy = clone $this;
2826  $this->availability_id = $availability_id;
2827  }
2828 
2829  if (!$notrigger && empty($error)) {
2830  // Call trigger
2831  $result = $this->call_trigger('ORDER_MODIFY', $user);
2832  if ($result < 0) {
2833  $error++;
2834  }
2835  // End call triggers
2836  }
2837 
2838  if (!$error) {
2839  $this->db->commit();
2840  return 1;
2841  } else {
2842  foreach ($this->errors as $errmsg) {
2843  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2844  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2845  }
2846  $this->db->rollback();
2847  return -1 * $error;
2848  }
2849  } else {
2850  $error_str = 'Command status do not meet requirement '.$this->statut;
2851  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2852  $this->error = $error_str;
2853  $this->errors[] = $this->error;
2854  return -2;
2855  }
2856  }
2857 
2858  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2866  public function demand_reason($demand_reason_id, $notrigger = 0)
2867  {
2868  // phpcs:enable
2869  global $user;
2870 
2871  dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2872  if ($this->statut >= self::STATUS_DRAFT) {
2873  $error = 0;
2874 
2875  $this->db->begin();
2876 
2877  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2878  $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2879  $sql .= ' WHERE rowid='.((int) $this->id);
2880 
2881  dol_syslog(__METHOD__, LOG_DEBUG);
2882  $resql = $this->db->query($sql);
2883  if (!$resql) {
2884  $this->errors[] = $this->db->error();
2885  $error++;
2886  }
2887 
2888  if (!$error) {
2889  $this->oldcopy = clone $this;
2890  $this->demand_reason_id = $demand_reason_id;
2891  }
2892 
2893  if (!$notrigger && empty($error)) {
2894  // Call trigger
2895  $result = $this->call_trigger('ORDER_MODIFY', $user);
2896  if ($result < 0) {
2897  $error++;
2898  }
2899  // End call triggers
2900  }
2901 
2902  if (!$error) {
2903  $this->db->commit();
2904  return 1;
2905  } else {
2906  foreach ($this->errors as $errmsg) {
2907  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2908  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2909  }
2910  $this->db->rollback();
2911  return -1 * $error;
2912  }
2913  } else {
2914  $error_str = 'order status do not meet requirement '.$this->statut;
2915  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2916  $this->error = $error_str;
2917  $this->errors[] = $this->error;
2918  return -2;
2919  }
2920  }
2921 
2922  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2931  public function set_ref_client($user, $ref_client, $notrigger = 0)
2932  {
2933  // phpcs:enable
2934  if ($user->rights->commande->creer) {
2935  $error = 0;
2936 
2937  $this->db->begin();
2938 
2939  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2940  $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2941  $sql .= ' WHERE rowid = '.((int) $this->id);
2942 
2943  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2944  $resql = $this->db->query($sql);
2945  if (!$resql) {
2946  $this->errors[] = $this->db->error();
2947  $error++;
2948  }
2949 
2950  if (!$error) {
2951  $this->oldcopy = clone $this;
2952  $this->ref_client = $ref_client;
2953  }
2954 
2955  if (!$notrigger && empty($error)) {
2956  // Call trigger
2957  $result = $this->call_trigger('ORDER_MODIFY', $user);
2958  if ($result < 0) {
2959  $error++;
2960  }
2961  // End call triggers
2962  }
2963  if (!$error) {
2964  $this->db->commit();
2965  return 1;
2966  } else {
2967  foreach ($this->errors as $errmsg) {
2968  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2969  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2970  }
2971  $this->db->rollback();
2972  return -1 * $error;
2973  }
2974  } else {
2975  return -1;
2976  }
2977  }
2978 
2986  public function classifyBilled(User $user, $notrigger = 0)
2987  {
2988  $error = 0;
2989 
2990  if ($this->billed) {
2991  return 0;
2992  }
2993 
2994  $this->db->begin();
2995 
2996  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2997  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
2998 
2999  dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3000  if ($this->db->query($sql)) {
3001  if (!$error) {
3002  $this->oldcopy = clone $this;
3003  $this->billed = 1;
3004  }
3005 
3006  if (!$notrigger && empty($error)) {
3007  // Call trigger
3008  $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3009  if ($result < 0) {
3010  $error++;
3011  }
3012  // End call triggers
3013  }
3014 
3015  if (!$error) {
3016  $this->db->commit();
3017  return 1;
3018  } else {
3019  foreach ($this->errors as $errmsg) {
3020  dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3021  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3022  }
3023  $this->db->rollback();
3024  return -1 * $error;
3025  }
3026  } else {
3027  $this->error = $this->db->error();
3028  $this->db->rollback();
3029  return -1;
3030  }
3031  }
3032 
3040  public function classifyUnBilled(User $user, $notrigger = 0)
3041  {
3042  $error = 0;
3043 
3044  $this->db->begin();
3045 
3046  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3047  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3048 
3049  dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3050  if ($this->db->query($sql)) {
3051  if (!$error) {
3052  $this->oldcopy = clone $this;
3053  $this->billed = 1;
3054  }
3055 
3056  if (!$notrigger && empty($error)) {
3057  // Call trigger
3058  $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3059  if ($result < 0) {
3060  $error++;
3061  }
3062  // End call triggers
3063  }
3064 
3065  if (!$error) {
3066  $this->billed = 0;
3067 
3068  $this->db->commit();
3069  return 1;
3070  } else {
3071  foreach ($this->errors as $errmsg) {
3072  dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3073  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3074  }
3075  $this->db->rollback();
3076  return -1 * $error;
3077  }
3078  } else {
3079  $this->error = $this->db->error();
3080  $this->db->rollback();
3081  return -1;
3082  }
3083  }
3084 
3085 
3116  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)
3117  {
3118  global $conf, $mysoc, $langs, $user;
3119 
3120  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");
3121  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3122 
3123  if ($this->statut == Commande::STATUS_DRAFT) {
3124  // Clean parameters
3125  if (empty($qty)) {
3126  $qty = 0;
3127  }
3128  if (empty($info_bits)) {
3129  $info_bits = 0;
3130  }
3131  if (empty($txtva)) {
3132  $txtva = 0;
3133  }
3134  if (empty($txlocaltax1)) {
3135  $txlocaltax1 = 0;
3136  }
3137  if (empty($txlocaltax2)) {
3138  $txlocaltax2 = 0;
3139  }
3140  if (empty($remise_percent)) {
3141  $remise_percent = 0;
3142  }
3143  if (empty($special_code) || $special_code == 3) {
3144  $special_code = 0;
3145  }
3146  if (empty($ref_ext)) {
3147  $ref_ext = '';
3148  }
3149 
3150  if ($date_start && $date_end && $date_start > $date_end) {
3151  $langs->load("errors");
3152  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3153  return -1;
3154  }
3155 
3156  $remise_percent = price2num($remise_percent);
3157  $qty = price2num($qty);
3158  $pu = price2num($pu);
3159  $pa_ht = price2num($pa_ht);
3160  $pu_ht_devise = price2num($pu_ht_devise);
3161  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
3162  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3163  }
3164  $txlocaltax1 = price2num($txlocaltax1);
3165  $txlocaltax2 = price2num($txlocaltax2);
3166 
3167  $this->db->begin();
3168 
3169  // Calcul du total TTC et de la TVA pour la ligne a partir de
3170  // qty, pu, remise_percent et txtva
3171  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3172  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3173 
3174  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3175 
3176  // Clean vat code
3177  $vat_src_code = '';
3178  $reg = array();
3179  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3180  $vat_src_code = $reg[1];
3181  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3182  }
3183 
3184  $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);
3185 
3186  $total_ht = $tabprice[0];
3187  $total_tva = $tabprice[1];
3188  $total_ttc = $tabprice[2];
3189  $total_localtax1 = $tabprice[9];
3190  $total_localtax2 = $tabprice[10];
3191  $pu_ht = $tabprice[3];
3192  $pu_tva = $tabprice[4];
3193  $pu_ttc = $tabprice[5];
3194 
3195  // MultiCurrency
3196  $multicurrency_total_ht = $tabprice[16];
3197  $multicurrency_total_tva = $tabprice[17];
3198  $multicurrency_total_ttc = $tabprice[18];
3199  $pu_ht_devise = $tabprice[19];
3200 
3201  // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3202  $price = $pu_ht;
3203  if ($price_base_type == 'TTC') {
3204  $subprice = $pu_ttc;
3205  } else {
3206  $subprice = $pu_ht;
3207  }
3208  $remise = 0;
3209  if ($remise_percent > 0) {
3210  $remise = round(($pu * $remise_percent / 100), 2);
3211  $price = ($pu - $remise);
3212  }
3213 
3214  //Fetch current line from the database and then clone the object and set it in $oldline property
3215  $line = new OrderLine($this->db);
3216  $line->fetch($rowid);
3217  $line->fetch_optionals();
3218 
3219  if (!empty($line->fk_product)) {
3220  $product = new Product($this->db);
3221  $result = $product->fetch($line->fk_product);
3222  $product_type = $product->type;
3223 
3224  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
3225  $langs->load("errors");
3226  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3227  $this->errors[] = $this->error;
3228 
3229  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3230 
3231  $this->db->rollback();
3233  }
3234  }
3235 
3236  $staticline = clone $line;
3237 
3238  $line->oldline = $staticline;
3239  $this->line = $line;
3240  $this->line->context = $this->context;
3241  $this->line->rang = $rang;
3242 
3243  // Reorder if fk_parent_line change
3244  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3245  $rangmax = $this->line_max($fk_parent_line);
3246  $this->line->rang = $rangmax + 1;
3247  }
3248 
3249  $this->line->id = $rowid;
3250  $this->line->label = $label;
3251  $this->line->desc = $desc;
3252  $this->line->qty = $qty;
3253  $this->line->ref_ext = $ref_ext;
3254 
3255  $this->line->vat_src_code = $vat_src_code;
3256  $this->line->tva_tx = $txtva;
3257  $this->line->localtax1_tx = $txlocaltax1;
3258  $this->line->localtax2_tx = $txlocaltax2;
3259  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3260  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3261  $this->line->remise_percent = $remise_percent;
3262  $this->line->subprice = $subprice;
3263  $this->line->info_bits = $info_bits;
3264  $this->line->special_code = $special_code;
3265  $this->line->total_ht = $total_ht;
3266  $this->line->total_tva = $total_tva;
3267  $this->line->total_localtax1 = $total_localtax1;
3268  $this->line->total_localtax2 = $total_localtax2;
3269  $this->line->total_ttc = $total_ttc;
3270  $this->line->date_start = $date_start;
3271  $this->line->date_end = $date_end;
3272  $this->line->product_type = $type;
3273  $this->line->fk_parent_line = $fk_parent_line;
3274  $this->line->skip_update_total = $skip_update_total;
3275  $this->line->fk_unit = $fk_unit;
3276 
3277  $this->line->fk_fournprice = $fk_fournprice;
3278  $this->line->pa_ht = $pa_ht;
3279 
3280  // Multicurrency
3281  $this->line->multicurrency_subprice = $pu_ht_devise;
3282  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3283  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3284  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3285 
3286  // TODO deprecated
3287  $this->line->price = $price;
3288 
3289  if (is_array($array_options) && count($array_options) > 0) {
3290  // We replace values in this->line->array_options only for entries defined into $array_options
3291  foreach ($array_options as $key => $value) {
3292  $this->line->array_options[$key] = $array_options[$key];
3293  }
3294  }
3295 
3296  $result = $this->line->update($user, $notrigger);
3297  if ($result > 0) {
3298  // Reorder if child line
3299  if (!empty($fk_parent_line)) {
3300  $this->line_order(true, 'DESC');
3301  }
3302 
3303  // Mise a jour info denormalisees
3304  $this->update_price(1, 'auto');
3305 
3306  $this->db->commit();
3307  return $result;
3308  } else {
3309  $this->error = $this->line->error;
3310 
3311  $this->db->rollback();
3312  return -1;
3313  }
3314  } else {
3315  $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3316  $this->errors = array('OrderStatusMakeOperationForbidden');
3317  return -2;
3318  }
3319  }
3320 
3328  public function update(User $user, $notrigger = 0)
3329  {
3330  global $conf;
3331 
3332  $error = 0;
3333 
3334  // Clean parameters
3335  if (isset($this->ref)) {
3336  $this->ref = trim($this->ref);
3337  }
3338  if (isset($this->ref_client)) {
3339  $this->ref_client = trim($this->ref_client);
3340  }
3341  if (isset($this->note) || isset($this->note_private)) {
3342  $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3343  }
3344  if (isset($this->note_public)) {
3345  $this->note_public = trim($this->note_public);
3346  }
3347  if (isset($this->model_pdf)) {
3348  $this->model_pdf = trim($this->model_pdf);
3349  }
3350  if (isset($this->import_key)) {
3351  $this->import_key = trim($this->import_key);
3352  }
3353  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
3354 
3355  // Check parameters
3356  // Put here code to add control on parameters values
3357 
3358  // Update request
3359  $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3360 
3361  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3362  $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3363  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3364  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3365  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3366  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3367  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3368  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3369  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3370  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3371  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3372  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3373  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3374  $sql .= " fk_user_valid=".((isset($this->user_valid) && $this->user_valid > 0) ? $this->user_valid : "null").",";
3375  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3376  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3377  $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3378  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3379  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3380  $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3381  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3382  $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3383  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3384  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3385  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3386  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
3387 
3388  $sql .= " WHERE rowid=".((int) $this->id);
3389 
3390  $this->db->begin();
3391 
3392  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3393  $resql = $this->db->query($sql);
3394  if (!$resql) {
3395  $error++; $this->errors[] = "Error ".$this->db->lasterror();
3396  }
3397 
3398  if (!$error) {
3399  $result = $this->insertExtraFields();
3400  if ($result < 0) {
3401  $error++;
3402  }
3403  }
3404 
3405  if (!$error && !$notrigger) {
3406  // Call trigger
3407  $result = $this->call_trigger('ORDER_MODIFY', $user);
3408  if ($result < 0) {
3409  $error++;
3410  }
3411  // End call triggers
3412  }
3413 
3414  // Commit or rollback
3415  if ($error) {
3416  foreach ($this->errors as $errmsg) {
3417  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3418  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3419  }
3420  $this->db->rollback();
3421  return -1 * $error;
3422  } else {
3423  $this->db->commit();
3424  return 1;
3425  }
3426  }
3427 
3435  public function delete($user, $notrigger = 0)
3436  {
3437  global $conf, $langs;
3438  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3439 
3440  $error = 0;
3441 
3442  dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3443 
3444  $this->db->begin();
3445 
3446  if (!$notrigger) {
3447  // Call trigger
3448  $result = $this->call_trigger('ORDER_DELETE', $user);
3449  if ($result < 0) {
3450  $error++;
3451  }
3452  // End call triggers
3453  }
3454 
3455  // Test we can delete
3456  if ($this->nb_expedition() != 0) {
3457  $this->errors[] = $langs->trans('SomeShipmentExists');
3458  $error++;
3459  }
3460 
3461  // Delete extrafields of lines and lines
3462  if (!$error && !empty($this->table_element_line)) {
3463  $tabletodelete = $this->table_element_line;
3464  $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).")";
3465  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3466  if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3467  $error++;
3468  $this->error = $this->db->lasterror();
3469  $this->errors[] = $this->error;
3470  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3471  }
3472  }
3473 
3474  if (!$error) {
3475  // Delete linked object
3476  $res = $this->deleteObjectLinked();
3477  if ($res < 0) {
3478  $error++;
3479  }
3480  }
3481 
3482  if (!$error) {
3483  // Delete linked contacts
3484  $res = $this->delete_linked_contact();
3485  if ($res < 0) {
3486  $error++;
3487  }
3488  }
3489 
3490  // Removed extrafields of object
3491  if (!$error) {
3492  $result = $this->deleteExtraFields();
3493  if ($result < 0) {
3494  $error++;
3495  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3496  }
3497  }
3498 
3499  // Delete main record
3500  if (!$error) {
3501  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3502  $res = $this->db->query($sql);
3503  if (!$res) {
3504  $error++;
3505  $this->error = $this->db->lasterror();
3506  $this->errors[] = $this->error;
3507  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3508  }
3509  }
3510 
3511  // Delete record into ECM index and physically
3512  if (!$error) {
3513  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3514  if (!$res) {
3515  $error++;
3516  }
3517  }
3518 
3519  if (!$error) {
3520  // We remove directory
3521  $ref = dol_sanitizeFileName($this->ref);
3522  if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3523  $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3524  $file = $dir."/".$ref.".pdf";
3525  if (file_exists($file)) {
3526  dol_delete_preview($this);
3527 
3528  if (!dol_delete_file($file, 0, 0, 0, $this)) {
3529  $this->error = 'ErrorFailToDeleteFile';
3530  $this->errors[] = $this->error;
3531  $this->db->rollback();
3532  return 0;
3533  }
3534  }
3535  if (file_exists($dir)) {
3536  $res = @dol_delete_dir_recursive($dir);
3537  if (!$res) {
3538  $this->error = 'ErrorFailToDeleteDir';
3539  $this->errors[] = $this->error;
3540  $this->db->rollback();
3541  return 0;
3542  }
3543  }
3544  }
3545  }
3546 
3547  if (!$error) {
3548  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3549  $this->db->commit();
3550  return 1;
3551  } else {
3552  $this->db->rollback();
3553  return -1;
3554  }
3555  }
3556 
3557 
3558  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3565  public function load_board($user)
3566  {
3567  // phpcs:enable
3568  global $conf, $langs;
3569 
3570  $clause = " WHERE";
3571 
3572  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3573  $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3574  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3575  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3576  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3577  $clause = " AND";
3578  }
3579  $sql .= $clause." c.entity IN (".getEntity('commande').")";
3580  //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3581  $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
3582  if ($user->socid) {
3583  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3584  }
3585 
3586  $resql = $this->db->query($sql);
3587  if ($resql) {
3588  $response = new WorkboardResponse();
3589  $response->warning_delay = $conf->commande->client->warning_delay / 60 / 60 / 24;
3590  $response->label = $langs->trans("OrdersToProcess");
3591  $response->labelShort = $langs->trans("Opened");
3592  $response->url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&mainmenu=commercial&leftmenu=orders';
3593  $response->img = img_object('', "order");
3594 
3595  $generic_commande = new Commande($this->db);
3596 
3597  while ($obj = $this->db->fetch_object($resql)) {
3598  $response->nbtodo++;
3599  $response->total += $obj->total_ht;
3600 
3601  $generic_commande->statut = $obj->fk_statut;
3602  $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3603  $generic_commande->date = $this->db->jdate($obj->date_commande);
3604  $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3605  $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3606 
3607  if ($generic_commande->hasDelay()) {
3608  $response->nbtodolate++;
3609  }
3610  }
3611 
3612  return $response;
3613  } else {
3614  $this->error = $this->db->error();
3615  return -1;
3616  }
3617  }
3618 
3624  public function getLabelSource()
3625  {
3626  global $langs;
3627 
3628  $label = $langs->trans('OrderSource'.$this->source);
3629 
3630  if ($label == 'OrderSource') {
3631  return '';
3632  }
3633  return $label;
3634  }
3635 
3642  public function getLibStatut($mode)
3643  {
3644  return $this->LibStatut($this->statut, $this->billed, $mode);
3645  }
3646 
3647  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3657  public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3658  {
3659  // phpcs:enable
3660  global $langs, $conf, $hookmanager;
3661 
3662  $billedtext = '';
3663  if (empty($donotshowbilled)) {
3664  $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3665  }
3666 
3667  $labelTooltip = '';
3668 
3669  if ($status == self::STATUS_CANCELED) {
3670  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3671  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3672  $statusType = 'status9';
3673  } elseif ($status == self::STATUS_DRAFT) {
3674  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3675  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3676  $statusType = 'status0';
3677  } elseif ($status == self::STATUS_VALIDATED) {
3678  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3679  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3680  $statusType = 'status1';
3681  } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3682  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3683  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3684  $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3685  if (!empty($this->delivery_date)) {
3686  $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3687  }
3688  $statusType = 'status4';
3689  } elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3690  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderToBill'); // translated into Delivered
3691  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderToBillShort'); // translated into Delivered
3692  $statusType = 'status4';
3693  } elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3694  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderProcessed').$billedtext;
3695  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderProcessedShort').$billedtext;
3696  $statusType = 'status6';
3697  } elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3698  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered');
3699  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort');
3700  $statusType = 'status6';
3701  } else {
3702  $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3703  $labelStatusShort = '';
3704  $statusType = '';
3705  $mode = 0;
3706  }
3707 
3708  $parameters = array(
3709  'status' => $status,
3710  'mode' => $mode,
3711  'billed' => $billed,
3712  'donotshowbilled' => $donotshowbilled
3713  );
3714 
3715  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3716 
3717  if ($reshook > 0) {
3718  return $hookmanager->resPrint;
3719  }
3720 
3721  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3722  }
3723 
3724 
3738  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3739  {
3740  global $conf, $langs, $user, $hookmanager;
3741 
3742  if (!empty($conf->dol_no_mouse_hover)) {
3743  $notooltip = 1; // Force disable tooltips
3744  }
3745 
3746  $result = '';
3747 
3748  if (isModEnabled("expedition") && ($option == '1' || $option == '2')) {
3749  $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3750  } else {
3751  $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3752  }
3753 
3754  if (!$user->rights->commande->lire) {
3755  $option = 'nolink';
3756  }
3757 
3758  if ($option !== 'nolink') {
3759  // Add param to save lastsearch_values or not
3760  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3761  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3762  $add_save_lastsearch_values = 1;
3763  }
3764  if ($add_save_lastsearch_values) {
3765  $url .= '&save_lastsearch_values=1';
3766  }
3767  }
3768 
3769  if ($short) {
3770  return $url;
3771  }
3772 
3773  $label = '';
3774 
3775  if ($user->rights->commande->lire) {
3776  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3777  if (isset($this->statut)) {
3778  $label .= ' '.$this->getLibStatut(5);
3779  }
3780  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3781  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3782  if (!empty($this->total_ht)) {
3783  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3784  }
3785  if (!empty($this->total_tva)) {
3786  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3787  }
3788  if (!empty($this->total_ttc)) {
3789  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3790  }
3791  if (!empty($this->date)) {
3792  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3793  }
3794  if (!empty($this->delivery_date)) {
3795  $label .= '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3796  }
3797  }
3798 
3799  $linkclose = '';
3800  if (empty($notooltip) && $user->rights->commande->lire) {
3801  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3802  $label = $langs->trans("Order");
3803  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3804  }
3805  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
3806  $linkclose .= ' class="classfortooltip"';
3807 
3808  $target_value=array('_self', '_blank', '_parent', '_top');
3809  if (in_array($target, $target_value)) {
3810  $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3811  }
3812  }
3813 
3814  $linkstart = '<a href="'.$url.'"';
3815  $linkstart .= $linkclose.'>';
3816  $linkend = '</a>';
3817 
3818  if ($option === 'nolink') {
3819  $linkstart = '';
3820  $linkend = '';
3821  }
3822 
3823  $result .= $linkstart;
3824  if ($withpicto) {
3825  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
3826  }
3827  if ($withpicto != 2) {
3828  $result .= $this->ref;
3829  }
3830  $result .= $linkend;
3831 
3832  if ($addlinktonotes) {
3833  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3834  if ($txttoshow) {
3835  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3836  $result .= ' <span class="note inline-block">';
3837  $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3838  $result .= img_picto('', 'note');
3839  $result .= '</a>';
3840  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3841  //$result.='</a>';
3842  $result .= '</span>';
3843  }
3844  }
3845 
3846  global $action;
3847  $hookmanager->initHooks(array($this->element . 'dao'));
3848  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3849  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3850  if ($reshook > 0) {
3851  $result = $hookmanager->resPrint;
3852  } else {
3853  $result .= $hookmanager->resPrint;
3854  }
3855  return $result;
3856  }
3857 
3858 
3865  public function info($id)
3866  {
3867  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3868  $sql .= ' date_valid as datev,';
3869  $sql .= ' date_cloture as datecloture,';
3870  $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3871  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3872  $sql .= ' WHERE c.rowid = '.((int) $id);
3873  $result = $this->db->query($sql);
3874  if ($result) {
3875  if ($this->db->num_rows($result)) {
3876  $obj = $this->db->fetch_object($result);
3877  $this->id = $obj->rowid;
3878  if ($obj->fk_user_author) {
3879  $this->user_creation_id = $obj->fk_user_author;
3880  }
3881  if ($obj->fk_user_valid) {
3882  $this->user_validation_id = $obj->fk_user_valid;
3883  }
3884  if ($obj->fk_user_cloture) {
3885  $this->user_closing_id = $obj->fk_user_cloture;
3886  }
3887 
3888  $this->date_creation = $this->db->jdate($obj->datec);
3889  $this->date_modification = $this->db->jdate($obj->datem);
3890  $this->date_validation = $this->db->jdate($obj->datev);
3891  $this->date_cloture = $this->db->jdate($obj->datecloture);
3892  }
3893 
3894  $this->db->free($result);
3895  } else {
3896  dol_print_error($this->db);
3897  }
3898  }
3899 
3900 
3908  public function initAsSpecimen()
3909  {
3910  global $conf, $langs;
3911 
3912  dol_syslog(get_class($this)."::initAsSpecimen");
3913 
3914  // Load array of products prodids
3915  $num_prods = 0;
3916  $prodids = array();
3917  $sql = "SELECT rowid";
3918  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3919  $sql .= " WHERE entity IN (".getEntity('product').")";
3920  $sql .= $this->db->plimit(100);
3921 
3922  $resql = $this->db->query($sql);
3923  if ($resql) {
3924  $num_prods = $this->db->num_rows($resql);
3925  $i = 0;
3926  while ($i < $num_prods) {
3927  $i++;
3928  $row = $this->db->fetch_row($resql);
3929  $prodids[$i] = $row[0];
3930  }
3931  }
3932 
3933  // Initialise parametres
3934  $this->id = 0;
3935  $this->ref = 'SPECIMEN';
3936  $this->specimen = 1;
3937  $this->socid = 1;
3938  $this->date = time();
3939  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3940  $this->cond_reglement_code = 'RECEP';
3941  $this->mode_reglement_code = 'CHQ';
3942  $this->availability_code = 'DSP';
3943  $this->demand_reason_code = 'SRC_00';
3944 
3945  $this->note_public = 'This is a comment (public)';
3946  $this->note_private = 'This is a comment (private)';
3947 
3948  $this->multicurrency_tx = 1;
3949  $this->multicurrency_code = $conf->currency;
3950 
3951  // Lines
3952  $nbp = 5;
3953  $xnbp = 0;
3954  while ($xnbp < $nbp) {
3955  $line = new OrderLine($this->db);
3956 
3957  $line->desc = $langs->trans("Description")." ".$xnbp;
3958  $line->qty = 1;
3959  $line->subprice = 100;
3960  $line->price = 100;
3961  $line->tva_tx = 20;
3962  if ($xnbp == 2) {
3963  $line->total_ht = 50;
3964  $line->total_ttc = 60;
3965  $line->total_tva = 10;
3966  $line->remise_percent = 50;
3967  } else {
3968  $line->total_ht = 100;
3969  $line->total_ttc = 120;
3970  $line->total_tva = 20;
3971  $line->remise_percent = 0;
3972  }
3973  if ($num_prods > 0) {
3974  $prodid = mt_rand(1, $num_prods);
3975  $line->fk_product = $prodids[$prodid];
3976  $line->product_ref = 'SPECIMEN';
3977  }
3978 
3979  $this->lines[$xnbp] = $line;
3980 
3981  $this->total_ht += $line->total_ht;
3982  $this->total_tva += $line->total_tva;
3983  $this->total_ttc += $line->total_ttc;
3984 
3985  $xnbp++;
3986  }
3987  }
3988 
3989 
3990  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3996  public function load_state_board()
3997  {
3998  // phpcs:enable
3999  global $user;
4000 
4001  $this->nb = array();
4002  $clause = "WHERE";
4003 
4004  $sql = "SELECT count(co.rowid) as nb";
4005  $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
4006  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4007  if (empty($user->rights->societe->client->voir) && !$user->socid) {
4008  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4009  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4010  $clause = "AND";
4011  }
4012  $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4013 
4014  $resql = $this->db->query($sql);
4015  if ($resql) {
4016  while ($obj = $this->db->fetch_object($resql)) {
4017  $this->nb["orders"] = $obj->nb;
4018  }
4019  $this->db->free($resql);
4020  return 1;
4021  } else {
4022  dol_print_error($this->db);
4023  $this->error = $this->db->error();
4024  return -1;
4025  }
4026  }
4027 
4033  public function getLinesArray()
4034  {
4035  return $this->fetch_lines();
4036  }
4037 
4049  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4050  {
4051  global $conf, $langs;
4052 
4053  $langs->load("orders");
4054  $outputlangs->load("products");
4055 
4056  if (!dol_strlen($modele)) {
4057  $modele = 'einstein';
4058 
4059  if (!empty($this->model_pdf)) {
4060  $modele = $this->model_pdf;
4061  } elseif (!empty($this->modelpdf)) { // deprecated
4062  $modele = $this->modelpdf;
4063  } elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
4064  $modele = $conf->global->COMMANDE_ADDON_PDF;
4065  }
4066  }
4067 
4068  $modelpath = "core/modules/commande/doc/";
4069 
4070  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4071  }
4072 
4073 
4082  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
4083  {
4084  $tables = array(
4085  'commande'
4086  );
4087 
4088  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
4089  }
4090 
4099  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4100  {
4101  $tables = array(
4102  'commandedet',
4103  );
4104 
4105  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4106  }
4107 
4113  public function hasDelay()
4114  {
4115  global $conf;
4116 
4117  if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4118  return false; // Never late if not inside this status range
4119  }
4120 
4121  $now = dol_now();
4122 
4123  return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4124  }
4125 
4131  public function showDelay()
4132  {
4133  global $conf, $langs;
4134 
4135  if (empty($this->delivery_date)) {
4136  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
4137  } else {
4138  $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
4139  }
4140  $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4141 
4142  return $text;
4143  }
4144 }
4145 
4146 
4151 {
4155  public $element = 'commandedet';
4156 
4157  public $table_element = 'commandedet';
4158 
4159  public $oldline;
4160 
4165  public $fk_commande;
4166 
4173  public $commande_id;
4174 
4175  public $fk_parent_line;
4176 
4180  public $fk_facture;
4181 
4185  public $ref_ext;
4186 
4187  public $fk_remise_except;
4188 
4192  public $rang = 0;
4193  public $fk_fournprice;
4194 
4199  public $pa_ht;
4200  public $marge_tx;
4201  public $marque_tx;
4202 
4207  public $remise;
4208 
4209  // Start and end date of the line
4210  public $date_start;
4211  public $date_end;
4212 
4213  public $skip_update_total; // Skip update price total for special lines
4214 
4215 
4221  public function __construct($db)
4222  {
4223  $this->db = $db;
4224  }
4225 
4232  public function fetch($rowid)
4233  {
4234  $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,';
4235  $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4236  $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,';
4237  $sql .= ' cd.fk_unit,';
4238  $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4239  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4240  $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4241  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4242  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4243  $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4244  $result = $this->db->query($sql);
4245  if ($result) {
4246  $objp = $this->db->fetch_object($result);
4247 
4248  if (!$objp) {
4249  $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4250  return 0;
4251  }
4252 
4253  $this->rowid = $objp->rowid;
4254  $this->id = $objp->rowid;
4255  $this->fk_commande = $objp->fk_commande;
4256  $this->fk_parent_line = $objp->fk_parent_line;
4257  $this->label = $objp->custom_label;
4258  $this->desc = $objp->description;
4259  $this->qty = $objp->qty;
4260  $this->price = $objp->price;
4261  $this->subprice = $objp->subprice;
4262  $this->ref_ext = $objp->ref_ext;
4263  $this->vat_src_code = $objp->vat_src_code;
4264  $this->tva_tx = $objp->tva_tx;
4265  $this->localtax1_tx = $objp->localtax1_tx;
4266  $this->localtax2_tx = $objp->localtax2_tx;
4267  $this->remise = $objp->remise;
4268  $this->remise_percent = $objp->remise_percent;
4269  $this->fk_remise_except = $objp->fk_remise_except;
4270  $this->fk_product = $objp->fk_product;
4271  $this->product_type = $objp->product_type;
4272  $this->info_bits = $objp->info_bits;
4273  $this->special_code = $objp->special_code;
4274  $this->total_ht = $objp->total_ht;
4275  $this->total_tva = $objp->total_tva;
4276  $this->total_localtax1 = $objp->total_localtax1;
4277  $this->total_localtax2 = $objp->total_localtax2;
4278  $this->total_ttc = $objp->total_ttc;
4279  $this->fk_fournprice = $objp->fk_fournprice;
4280  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4281  $this->pa_ht = $marginInfos[0];
4282  $this->marge_tx = $marginInfos[1];
4283  $this->marque_tx = $marginInfos[2];
4284  $this->special_code = $objp->special_code;
4285  $this->rang = $objp->rang;
4286 
4287  $this->ref = $objp->product_ref; // deprecated
4288 
4289  $this->product_ref = $objp->product_ref;
4290  $this->product_label = $objp->product_label;
4291  $this->product_desc = $objp->product_desc;
4292  $this->product_tobatch = $objp->product_tobatch;
4293  $this->fk_unit = $objp->fk_unit;
4294 
4295  $this->date_start = $this->db->jdate($objp->date_start);
4296  $this->date_end = $this->db->jdate($objp->date_end);
4297 
4298  $this->fk_multicurrency = $objp->fk_multicurrency;
4299  $this->multicurrency_code = $objp->multicurrency_code;
4300  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4301  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4302  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4303  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4304 
4305  $this->db->free($result);
4306 
4307  return 1;
4308  } else {
4309  $this->error = $this->db->lasterror();
4310  return -1;
4311  }
4312  }
4313 
4321  public function delete(User $user, $notrigger = 0)
4322  {
4323  global $conf, $langs;
4324 
4325  $error = 0;
4326 
4327  if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4328  $this->id = $this->rowid;
4329  }
4330 
4331  // check if order line is not in a shipment line before deleting
4332  $sqlCheckShipmentLine = "SELECT";
4333  $sqlCheckShipmentLine .= " ed.rowid";
4334  $sqlCheckShipmentLine .= " FROM ".MAIN_DB_PREFIX."expeditiondet ed";
4335  $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = ".((int) $this->id);
4336 
4337  $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4338  if (!$resqlCheckShipmentLine) {
4339  $error++;
4340  $this->error = $this->db->lasterror();
4341  $this->errors[] = $this->error;
4342  } else {
4343  $langs->load('errors');
4344  $num = $this->db->num_rows($resqlCheckShipmentLine);
4345  if ($num > 0) {
4346  $error++;
4347  $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4348  $this->error = $langs->trans('ErrorRecordAlreadyExists').' : '.$langs->trans('ShipmentLine').' '.$objCheckShipmentLine->rowid;
4349  $this->errors[] = $this->error;
4350  }
4351  $this->db->free($resqlCheckShipmentLine);
4352  }
4353  if ($error) {
4354  dol_syslog(__METHOD__.'Error ; '.$this->error, LOG_ERR);
4355  return -1;
4356  }
4357 
4358  $this->db->begin();
4359 
4360  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid = ".((int) $this->id);
4361 
4362  dol_syslog("OrderLine::delete", LOG_DEBUG);
4363  $resql = $this->db->query($sql);
4364  if ($resql) {
4365  if (!$error && !$notrigger) {
4366  // Call trigger
4367  $result = $this->call_trigger('LINEORDER_DELETE', $user);
4368  if ($result < 0) {
4369  $error++;
4370  }
4371  // End call triggers
4372  }
4373 
4374  // Remove extrafields
4375  if (!$error) {
4376  $result = $this->deleteExtraFields();
4377  if ($result < 0) {
4378  $error++;
4379  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4380  }
4381  }
4382 
4383  if (!$error) {
4384  $this->db->commit();
4385  return 1;
4386  }
4387 
4388  foreach ($this->errors as $errmsg) {
4389  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4390  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4391  }
4392  $this->db->rollback();
4393  return -1 * $error;
4394  } else {
4395  $this->error = $this->db->lasterror();
4396  return -1;
4397  }
4398  }
4399 
4407  public function insert($user = null, $notrigger = 0)
4408  {
4409  global $langs, $conf;
4410 
4411  $error = 0;
4412 
4413  $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'.
4414 
4415  dol_syslog(get_class($this)."::insert rang=".$this->rang);
4416 
4417  // Clean parameters
4418  if (empty($this->tva_tx)) {
4419  $this->tva_tx = 0;
4420  }
4421  if (empty($this->localtax1_tx)) {
4422  $this->localtax1_tx = 0;
4423  }
4424  if (empty($this->localtax2_tx)) {
4425  $this->localtax2_tx = 0;
4426  }
4427  if (empty($this->localtax1_type)) {
4428  $this->localtax1_type = 0;
4429  }
4430  if (empty($this->localtax2_type)) {
4431  $this->localtax2_type = 0;
4432  }
4433  if (empty($this->total_localtax1)) {
4434  $this->total_localtax1 = 0;
4435  }
4436  if (empty($this->total_localtax2)) {
4437  $this->total_localtax2 = 0;
4438  }
4439  if (empty($this->rang)) {
4440  $this->rang = 0;
4441  }
4442  if (empty($this->remise_percent)) {
4443  $this->remise_percent = 0;
4444  }
4445  if (empty($this->info_bits)) {
4446  $this->info_bits = 0;
4447  }
4448  if (empty($this->special_code)) {
4449  $this->special_code = 0;
4450  }
4451  if (empty($this->fk_parent_line)) {
4452  $this->fk_parent_line = 0;
4453  }
4454  if (empty($this->pa_ht)) {
4455  $this->pa_ht = 0;
4456  }
4457  if (empty($this->ref_ext)) {
4458  $this->ref_ext = '';
4459  }
4460 
4461  // if buy price not defined, define buyprice as configured in margin admin
4462  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4463  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4464  if ($result < 0) {
4465  return $result;
4466  } else {
4467  $this->pa_ht = $result;
4468  }
4469  }
4470 
4471  // Check parameters
4472  if ($this->product_type < 0) {
4473  return -1;
4474  }
4475 
4476  $this->db->begin();
4477 
4478  // Insertion dans base de la ligne
4479  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4480  $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4481  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4482  $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4483  $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4484  $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4485  $sql .= ' fk_unit';
4486  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4487  $sql .= ')';
4488  $sql .= " VALUES (".$this->fk_commande.",";
4489  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4490  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4491  $sql .= " '".$this->db->escape($this->desc)."',";
4492  $sql .= " '".price2num($this->qty)."',";
4493  $sql .= " '".$this->db->escape($this->ref_ext)."',";
4494  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4495  $sql .= " '".price2num($this->tva_tx)."',";
4496  $sql .= " '".price2num($this->localtax1_tx)."',";
4497  $sql .= " '".price2num($this->localtax2_tx)."',";
4498  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4499  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4500  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4501  $sql .= " '".$this->db->escape($this->product_type)."',";
4502  $sql .= " '".price2num($this->remise_percent)."',";
4503  $sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4504  $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4505  $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4506  $sql .= ' '.((int) $this->special_code).',';
4507  $sql .= ' '.((int) $this->rang).',';
4508  $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4509  $sql .= ' '.price2num($this->pa_ht).',';
4510  $sql .= " ".((int) $this->info_bits).",";
4511  $sql .= " ".price2num($this->total_ht, 'MT').",";
4512  $sql .= " ".price2num($this->total_tva, 'MT').",";
4513  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4514  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4515  $sql .= " ".price2num($this->total_ttc, 'MT').",";
4516  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4517  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4518  $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4519  $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4520  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4521  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4522  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4523  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4524  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4525  $sql .= ')';
4526 
4527  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4528  $resql = $this->db->query($sql);
4529  if ($resql) {
4530  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4531  $this->rowid = $this->id;
4532 
4533  if (!$error) {
4534  $result = $this->insertExtraFields();
4535  if ($result < 0) {
4536  $error++;
4537  }
4538  }
4539 
4540  if (!$error && !$notrigger) {
4541  // Call trigger
4542  $result = $this->call_trigger('LINEORDER_INSERT', $user);
4543  if ($result < 0) {
4544  $error++;
4545  }
4546  // End call triggers
4547  }
4548 
4549  if (!$error) {
4550  $this->db->commit();
4551  return 1;
4552  }
4553 
4554  foreach ($this->errors as $errmsg) {
4555  dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4556  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4557  }
4558  $this->db->rollback();
4559  return -1 * $error;
4560  } else {
4561  $this->error = $this->db->error();
4562  $this->db->rollback();
4563  return -2;
4564  }
4565  }
4566 
4574  public function update(User $user, $notrigger = 0)
4575  {
4576  global $conf, $langs;
4577 
4578  $error = 0;
4579 
4580  $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'.
4581 
4582  // Clean parameters
4583  if (empty($this->tva_tx)) {
4584  $this->tva_tx = 0;
4585  }
4586  if (empty($this->localtax1_tx)) {
4587  $this->localtax1_tx = 0;
4588  }
4589  if (empty($this->localtax2_tx)) {
4590  $this->localtax2_tx = 0;
4591  }
4592  if (empty($this->localtax1_type)) {
4593  $this->localtax1_type = 0;
4594  }
4595  if (empty($this->localtax2_type)) {
4596  $this->localtax2_type = 0;
4597  }
4598  if (empty($this->qty)) {
4599  $this->qty = 0;
4600  }
4601  if (empty($this->total_localtax1)) {
4602  $this->total_localtax1 = 0;
4603  }
4604  if (empty($this->total_localtax2)) {
4605  $this->total_localtax2 = 0;
4606  }
4607  if (empty($this->marque_tx)) {
4608  $this->marque_tx = 0;
4609  }
4610  if (empty($this->marge_tx)) {
4611  $this->marge_tx = 0;
4612  }
4613  if (empty($this->remise_percent)) {
4614  $this->remise_percent = 0;
4615  }
4616  if (empty($this->remise)) {
4617  $this->remise = 0;
4618  }
4619  if (empty($this->info_bits)) {
4620  $this->info_bits = 0;
4621  }
4622  if (empty($this->special_code)) {
4623  $this->special_code = 0;
4624  }
4625  if (empty($this->product_type)) {
4626  $this->product_type = 0;
4627  }
4628  if (empty($this->fk_parent_line)) {
4629  $this->fk_parent_line = 0;
4630  }
4631  if (empty($this->pa_ht)) {
4632  $this->pa_ht = 0;
4633  }
4634  if (empty($this->ref_ext)) {
4635  $this->ref_ext = '';
4636  }
4637 
4638  // if buy price not defined, define buyprice as configured in margin admin
4639  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4640  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4641  if ($result < 0) {
4642  return $result;
4643  } else {
4644  $this->pa_ht = $result;
4645  }
4646  }
4647 
4648  $this->db->begin();
4649 
4650  // Mise a jour ligne en base
4651  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4652  $sql .= " description='".$this->db->escape($this->desc)."'";
4653  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4654  $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4655  $sql .= " , tva_tx=".price2num($this->tva_tx);
4656  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4657  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4658  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4659  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4660  $sql .= " , qty=".price2num($this->qty);
4661  $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4662  $sql .= " , subprice=".price2num($this->subprice)."";
4663  $sql .= " , remise_percent=".price2num($this->remise_percent)."";
4664  $sql .= " , price=".price2num($this->price).""; // TODO A virer
4665  $sql .= " , remise=".price2num($this->remise).""; // TODO A virer
4666  if (empty($this->skip_update_total)) {
4667  $sql .= " , total_ht=".price2num($this->total_ht)."";
4668  $sql .= " , total_tva=".price2num($this->total_tva)."";
4669  $sql .= " , total_ttc=".price2num($this->total_ttc)."";
4670  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4671  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4672  }
4673  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4674  $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4675  $sql .= " , info_bits=".((int) $this->info_bits);
4676  $sql .= " , special_code=".((int) $this->special_code);
4677  $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4678  $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4679  $sql .= " , product_type=".$this->product_type;
4680  $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4681  if (!empty($this->rang)) {
4682  $sql .= ", rang=".((int) $this->rang);
4683  }
4684  $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4685 
4686  // Multicurrency
4687  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4688  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4689  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4690  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4691 
4692  $sql .= " WHERE rowid = ".((int) $this->rowid);
4693 
4694  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4695  $resql = $this->db->query($sql);
4696  if ($resql) {
4697  if (!$error) {
4698  $this->id = $this->rowid;
4699  $result = $this->insertExtraFields();
4700  if ($result < 0) {
4701  $error++;
4702  }
4703  }
4704 
4705  if (!$error && !$notrigger) {
4706  // Call trigger
4707  $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4708  if ($result < 0) {
4709  $error++;
4710  }
4711  // End call triggers
4712  }
4713 
4714  if (!$error) {
4715  $this->db->commit();
4716  return 1;
4717  }
4718 
4719  foreach ($this->errors as $errmsg) {
4720  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4721  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4722  }
4723  $this->db->rollback();
4724  return -1 * $error;
4725  } else {
4726  $this->error = $this->db->error();
4727  $this->db->rollback();
4728  return -2;
4729  }
4730  }
4731 
4732  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4739  public function update_total()
4740  {
4741  // phpcs:enable
4742  $this->db->begin();
4743 
4744  // Clean parameters
4745  if (empty($this->total_localtax1)) {
4746  $this->total_localtax1 = 0;
4747  }
4748  if (empty($this->total_localtax2)) {
4749  $this->total_localtax2 = 0;
4750  }
4751 
4752  // Mise a jour ligne en base
4753  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4754  $sql .= " total_ht='".price2num($this->total_ht)."'";
4755  $sql .= ",total_tva='".price2num($this->total_tva)."'";
4756  $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4757  $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4758  $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4759  $sql .= " WHERE rowid = ".((int) $this->rowid);
4760 
4761  dol_syslog("OrderLine::update_total", LOG_DEBUG);
4762 
4763  $resql = $this->db->query($sql);
4764  if ($resql) {
4765  $this->db->commit();
4766  return 1;
4767  } else {
4768  $this->error = $this->db->error();
4769  $this->db->rollback();
4770  return -2;
4771  }
4772  }
4773 }
$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.
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.
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.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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.
deleteline($user=null, $lineid=0)
Delete an order line.
$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.
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty 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 in llx_element_contact.
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:47
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:1402
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:1251
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:1454
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