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