dolibarr  18.0.6
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-2023 Frédéric France <frederic.france@netlogic.fr>
15  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program. If not, see <https://www.gnu.org/licenses/>.
29  */
30 
36 include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
37 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
38 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
39 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
40 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
42 
43 
47 class Commande extends CommonOrder
48 {
52  public $element = 'commande';
53 
57  public $table_element = 'commande';
58 
62  public $table_element_line = 'commandedet';
63 
67  public $class_element_line = 'OrderLine';
68 
72  public $fk_element = 'fk_commande';
73 
77  public $picto = 'order';
78 
83  public $ismultientitymanaged = 1;
84 
89  public $restrictiononfksoc = 1;
90 
94  protected $table_ref_field = 'ref';
95 
99  public $socid;
100 
104  public $ref_client;
105 
109  public $ref_customer;
110 
114  public $contactid;
115 
120  public $statut;
121 
125  public $billed;
126 
130  public $brouillon;
131 
135  public $cond_reglement_code;
136 
140  public $cond_reglement_doc;
141 
145  public $deposit_percent;
146 
150  public $fk_account;
151 
155  public $mode_reglement;
156 
160  public $mode_reglement_id;
161 
165  public $mode_reglement_code;
166 
171  public $availability_id;
172 
177  public $availability_code;
178 
183  public $availability;
184 
188  public $demand_reason_id;
189 
193  public $demand_reason_code;
194 
198  public $date;
199 
205  public $date_commande;
206 
212  public $date_livraison;
213 
217  public $delivery_date;
218 
222  public $fk_remise_except;
223 
228 
229  public $remise_absolue;
230  public $info_bits;
231  public $rang;
232  public $special_code;
233  public $source; // Order mode. How we received order (by phone, by email, ...)
234 
238  public $warehouse_id;
239 
240  public $extraparams = array();
241 
242  public $linked_objects = array();
243 
247  public $user_author_id;
248 
252  public $user_valid;
253 
257  public $line;
258 
262  public $lines = array();
263 
264  // Multicurrency
268  public $fk_multicurrency;
269 
273  public $multicurrency_code;
274  public $multicurrency_tx;
275  public $multicurrency_total_ht;
276  public $multicurrency_total_tva;
277  public $multicurrency_total_ttc;
278  public $multicurrency_total_localtax1; // not in database
279  public $multicurrency_total_localtax2; // not in database
280 
284  public $pos_source;
285 
289  public $expeditions;
290 
294  public $online_payment_url;
295 
296 
321  // BEGIN MODULEBUILDER PROPERTIES
325  public $fields = array(
326  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
327  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
328  'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25),
329  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>26),
330  'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>28),
331  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>-1, 'notnull'=>1, 'position'=>20),
332  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>25),
333  'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>1, 'position'=>60),
334  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
335  'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
336  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
337  'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
338  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
339  //'amount_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
340  //'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
341  'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
342  //'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
343  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
344  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
345  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
346  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
347  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
348  'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>150),
349  'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155),
350  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>160),
351  //'facture' =>array('type'=>'tinyint(4)', 'label'=>'ParentInvoice', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
352  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>-1, 'position'=>170),
353  'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
354  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
355  'deposit_percent' =>array('type'=>'varchar(63)', 'label'=>'DepositPercent', 'enabled'=>1, 'visible'=>-1, 'position'=>181),
356  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
357  'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
358  'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
359  'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>'isModEnabled("stock")', 'visible'=>-1, 'position'=>200),
360  'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
361  'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
362  //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
363  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
364  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>230),
365  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>235),
366  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
367  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>245),
368  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
369  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
370  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>260, 'isameasure'=>1),
371  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>265, 'isameasure'=>1),
372  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>270),
373  'module_source' =>array('type'=>'varchar(32)', 'label'=>'POSModule', 'enabled'=>1, 'visible'=>-1, 'position'=>275),
374  'pos_source' =>array('type'=>'varchar(32)', 'label'=>'POSTerminal', 'enabled'=>1, 'visible'=>-1, 'position'=>280),
375  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>300),
376  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>302),
377  'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>304),
378  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>306),
379  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
380  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
381  );
382  // END MODULEBUILDER PROPERTIES
383 
388 
392  const STATUS_CANCELED = -1;
396  const STATUS_DRAFT = 0;
400  const STATUS_VALIDATED = 1;
405  const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
406 
410  const STATUS_CLOSED = 3;
411 
412 
418  public function __construct($db)
419  {
420  $this->db = $db;
421  }
422 
430  public function getNextNumRef($soc)
431  {
432  global $langs, $conf;
433  $langs->load("order");
434 
435  if (!empty($conf->global->COMMANDE_ADDON)) {
436  $mybool = false;
437 
438  $file = $conf->global->COMMANDE_ADDON.".php";
439  $classname = $conf->global->COMMANDE_ADDON;
440 
441  // Include file with class
442  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
443  foreach ($dirmodels as $reldir) {
444  $dir = dol_buildpath($reldir."core/modules/commande/");
445 
446  // Load file with numbering class (if found)
447  $mybool |= @include_once $dir.$file;
448  }
449 
450  if ($mybool === false) {
451  dol_print_error('', "Failed to include file ".$file);
452  return '';
453  }
454 
455  $obj = new $classname();
456  $numref = $obj->getNextValue($soc, $this);
457 
458  if ($numref != "") {
459  return $numref;
460  } else {
461  $this->error = $obj->error;
462  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
463  return "";
464  }
465  } else {
466  print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
467  return "";
468  }
469  }
470 
471 
480  public function valid($user, $idwarehouse = 0, $notrigger = 0)
481  {
482  global $conf, $langs;
483 
484  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
485 
486  $error = 0;
487 
488  // Protection
489  if ($this->statut == self::STATUS_VALIDATED) {
490  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
491  return 0;
492  }
493 
494  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
495  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
496  $this->error = 'NotEnoughPermissions';
497  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
498  return -1;
499  }
500 
501  $now = dol_now();
502 
503  $this->db->begin();
504 
505  // Definition du nom de module de numerotation de commande
506  $soc = new Societe($this->db);
507  $soc->fetch($this->socid);
508 
509  // Class of company linked to order
510  $result = $soc->set_as_client();
511 
512  // Define new ref
513  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
514  $num = $this->getNextNumRef($soc);
515  } else {
516  $num = $this->ref;
517  }
518  $this->newref = dol_sanitizeFileName($num);
519 
520  // Validate
521  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
522  $sql .= " SET ref = '".$this->db->escape($num)."',";
523  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
524  $sql .= " date_valid='".$this->db->idate($now)."',";
525  $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
526  $sql .= " fk_user_modif = ".((int) $user->id);
527  $sql .= " WHERE rowid = ".((int) $this->id);
528 
529  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
530  $resql = $this->db->query($sql);
531  if (!$resql) {
532  dol_print_error($this->db);
533  $this->error = $this->db->lasterror();
534  $error++;
535  }
536 
537  if (!$error) {
538  // If stock is incremented on validate order, we must increment it
539  if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
540  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
541  $langs->load("agenda");
542 
543  // Loop on each line
544  $cpt = count($this->lines);
545  for ($i = 0; $i < $cpt; $i++) {
546  if ($this->lines[$i]->fk_product > 0) {
547  $mouvP = new MouvementStock($this->db);
548  $mouvP->origin = &$this;
549  $mouvP->setOrigin($this->element, $this->id);
550  // We decrement stock of product (and sub-products)
551  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
552  if ($result < 0) {
553  $error++;
554  $this->error = $mouvP->error;
555  }
556  }
557  if ($error) {
558  break;
559  }
560  }
561  }
562  }
563 
564  if (!$error && !$notrigger) {
565  // Call trigger
566  $result = $this->call_trigger('ORDER_VALIDATE', $user);
567  if ($result < 0) {
568  $error++;
569  }
570  // End call triggers
571  }
572 
573  if (!$error) {
574  $this->oldref = $this->ref;
575 
576  // Rename directory if dir was a temporary ref
577  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
578  // Now we rename also files into index
579  $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)."'";
580  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
581  $resql = $this->db->query($sql);
582  if (!$resql) {
583  $error++; $this->error = $this->db->lasterror();
584  }
585  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
586  $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
587  $resql = $this->db->query($sql);
588  if (!$resql) {
589  $error++; $this->error = $this->db->lasterror();
590  }
591 
592  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
593  $oldref = dol_sanitizeFileName($this->ref);
594  $newref = dol_sanitizeFileName($num);
595  $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
596  $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
597  if (!$error && file_exists($dirsource)) {
598  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
599 
600  if (@rename($dirsource, $dirdest)) {
601  dol_syslog("Rename ok");
602  // Rename docs starting with $oldref with $newref
603  $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
604  foreach ($listoffiles as $fileentry) {
605  $dirsource = $fileentry['name'];
606  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
607  $dirsource = $fileentry['path'].'/'.$dirsource;
608  $dirdest = $fileentry['path'].'/'.$dirdest;
609  @rename($dirsource, $dirdest);
610  }
611  }
612  }
613  }
614  }
615 
616  // Set new ref and current status
617  if (!$error) {
618  $this->ref = $num;
619  $this->statut = self::STATUS_VALIDATED;
620  $this->brouillon = 0;
621  }
622 
623  if (!$error) {
624  $this->db->commit();
625  return 1;
626  } else {
627  $this->db->rollback();
628  return -1;
629  }
630  }
631 
632  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
640  public function setDraft($user, $idwarehouse = -1)
641  {
642  //phpcs:enable
643  global $conf, $langs;
644 
645  $error = 0;
646 
647  // Protection
648  if ($this->statut <= self::STATUS_DRAFT) {
649  return 0;
650  }
651 
652  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
653  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
654  $this->error = 'Permission denied';
655  return -1;
656  }
657 
658  dol_syslog(__METHOD__, LOG_DEBUG);
659 
660  $this->db->begin();
661 
662  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
663  $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
664  $sql .= " fk_user_modif = ".((int) $user->id);
665  $sql .= " WHERE rowid = ".((int) $this->id);
666 
667  if ($this->db->query($sql)) {
668  if (!$error) {
669  $this->oldcopy = clone $this;
670  }
671 
672  // If stock is decremented on validate order, we must reincrement it
673  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
674  $result = 0;
675 
676  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
677  $langs->load("agenda");
678 
679  $num = count($this->lines);
680  for ($i = 0; $i < $num; $i++) {
681  if ($this->lines[$i]->fk_product > 0) {
682  $mouvP = new MouvementStock($this->db);
683  $mouvP->origin = &$this;
684  $mouvP->setOrigin($this->element, $this->id);
685  // We increment stock of product (and sub-products)
686  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
687  if ($result < 0) {
688  $error++; $this->error = $mouvP->error; break;
689  }
690  }
691  }
692  }
693 
694  if (!$error) {
695  // Call trigger
696  $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
697  if ($result < 0) {
698  $error++;
699  }
700  }
701 
702  if (!$error) {
703  $this->statut = self::STATUS_DRAFT;
704  $this->db->commit();
705  return 1;
706  } else {
707  $this->db->rollback();
708  return -1;
709  }
710  } else {
711  $this->error = $this->db->error();
712  $this->db->rollback();
713  return -1;
714  }
715  }
716 
717 
718  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
726  public function set_reopen($user)
727  {
728  // phpcs:enable
729  $error = 0;
730 
731  if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
732  dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
733  return 0;
734  }
735 
736  $this->db->begin();
737 
738  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
739  $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
740  $sql .= " fk_user_modif = ".((int) $user->id);
741  $sql .= " WHERE rowid = ".((int) $this->id);
742 
743  dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
744  $resql = $this->db->query($sql);
745  if ($resql) {
746  // Call trigger
747  $result = $this->call_trigger('ORDER_REOPEN', $user);
748  if ($result < 0) {
749  $error++;
750  }
751  // End call triggers
752  } else {
753  $error++;
754  $this->error = $this->db->lasterror();
755  dol_print_error($this->db);
756  }
757 
758  if (!$error) {
759  $this->statut = self::STATUS_VALIDATED;
760  $this->billed = 0;
761 
762  $this->db->commit();
763  return 1;
764  } else {
765  foreach ($this->errors as $errmsg) {
766  dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
767  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
768  }
769  $this->db->rollback();
770  return -1 * $error;
771  }
772  }
773 
781  public function cloture($user, $notrigger = 0)
782  {
783  global $conf;
784 
785  $error = 0;
786 
787  $usercanclose = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
788  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->close)));
789 
790  if ($usercanclose) {
791  if ($this->statut == self::STATUS_CLOSED) {
792  return 0;
793  }
794  $this->db->begin();
795 
796  $now = dol_now();
797 
798  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
799  $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
800  $sql .= ' fk_user_cloture = '.((int) $user->id).',';
801  $sql .= " date_cloture = '".$this->db->idate($now)."',";
802  $sql .= " fk_user_modif = ".((int) $user->id);
803  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
804 
805  if ($this->db->query($sql)) {
806  if (!$notrigger) {
807  // Call trigger
808  $result = $this->call_trigger('ORDER_CLOSE', $user);
809  if ($result < 0) {
810  $error++;
811  }
812  // End call triggers
813  }
814 
815  if (!$error) {
816  $this->statut = self::STATUS_CLOSED;
817 
818  $this->db->commit();
819  return 1;
820  } else {
821  $this->db->rollback();
822  return -1;
823  }
824  } else {
825  $this->error = $this->db->lasterror();
826 
827  $this->db->rollback();
828  return -1;
829  }
830  }
831  return 0;
832  }
833 
841  public function cancel($idwarehouse = -1)
842  {
843  global $conf, $user, $langs;
844 
845  $error = 0;
846 
847  $this->db->begin();
848 
849  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
850  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
851  $sql .= " fk_user_modif = ".((int) $user->id);
852  $sql .= " WHERE rowid = ".((int) $this->id);
853  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
854 
855  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
856  if ($this->db->query($sql)) {
857  // If stock is decremented on validate order, we must reincrement it
858  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
859  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
860  $langs->load("agenda");
861 
862  $num = count($this->lines);
863  for ($i = 0; $i < $num; $i++) {
864  if ($this->lines[$i]->fk_product > 0) {
865  $mouvP = new MouvementStock($this->db);
866  $mouvP->setOrigin($this->element, $this->id);
867  // We increment stock of product (and sub-products)
868  $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
869  if ($result < 0) {
870  $error++;
871  $this->error = $mouvP->error;
872  break;
873  }
874  }
875  }
876  }
877 
878  if (!$error) {
879  // Call trigger
880  $result = $this->call_trigger('ORDER_CANCEL', $user);
881  if ($result < 0) {
882  $error++;
883  }
884  // End call triggers
885  }
886 
887  if (!$error) {
888  $this->statut = self::STATUS_CANCELED;
889  $this->db->commit();
890  return 1;
891  } else {
892  foreach ($this->errors as $errmsg) {
893  dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
894  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
895  }
896  $this->db->rollback();
897  return -1 * $error;
898  }
899  } else {
900  $this->error = $this->db->error();
901  $this->db->rollback();
902  return -1;
903  }
904  }
905 
914  public function create($user, $notrigger = 0)
915  {
916  global $conf, $langs, $mysoc;
917  $error = 0;
918 
919  // Clean parameters
920  $this->brouillon = 1; // set command as draft
921 
922  // Set tmp vars
923  $date = ($this->date_commande ? $this->date_commande : $this->date);
924  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
925 
926  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
927  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
928  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
929  } else {
930  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
931  }
932  if (empty($this->fk_multicurrency)) {
933  $this->multicurrency_code = $conf->currency;
934  $this->fk_multicurrency = 0;
935  $this->multicurrency_tx = 1;
936  }
937 
938  dol_syslog(get_class($this)."::create user=".$user->id);
939 
940  // Check parameters
941  if (!empty($this->ref)) { // We check that ref is not already used
942  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
943  if ($result > 0) {
944  $this->error = 'ErrorRefAlreadyExists';
945  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
946  $this->db->rollback();
947  return -1;
948  }
949  }
950 
951  $soc = new Societe($this->db);
952  $result = $soc->fetch($this->socid);
953  if ($result < 0) {
954  $this->error = "Failed to fetch company";
955  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
956  return -2;
957  }
958  if (!empty($conf->global->ORDER_REQUIRE_SOURCE) && $this->source < 0) {
959  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
960  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
961  return -1;
962  }
963 
964  $now = dol_now();
965 
966  $this->db->begin();
967 
968  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
969  $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
970  $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
971  $sql .= ", fk_shipping_method";
972  $sql .= ", fk_warehouse";
973  $sql .= ", remise_absolue, remise_percent";
974  $sql .= ", fk_incoterms, location_incoterms";
975  $sql .= ", entity, module_source, pos_source";
976  $sql .= ", fk_multicurrency";
977  $sql .= ", multicurrency_code";
978  $sql .= ", multicurrency_tx";
979  $sql .= ")";
980  $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
981  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
982  $sql .= ", '".$this->db->idate($date)."'";
983  $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
984  $sql .= ", '".$this->db->escape($this->note_private)."'";
985  $sql .= ", '".$this->db->escape($this->note_public)."'";
986  $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
987  $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
988  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
989  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
990  $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
991  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
992  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
993  $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
994  $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
995  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
996  $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
997  $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
998  $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
999  $sql .= ", ".($this->remise_absolue > 0 ? $this->db->escape($this->remise_absolue) : 'NULL');
1000  $sql .= ", ".($this->remise_percent > 0 ? $this->db->escape($this->remise_percent) : 0); // TODO deprecated
1001  $sql .= ", ".(int) $this->fk_incoterms;
1002  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1003  $sql .= ", ".setEntity($this);
1004  $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1005  $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1006  $sql .= ", ".(int) $this->fk_multicurrency;
1007  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1008  $sql .= ", ".(float) $this->multicurrency_tx;
1009  $sql .= ")";
1010 
1011  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1012  $resql = $this->db->query($sql);
1013  if ($resql) {
1014  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1015 
1016  if ($this->id) {
1017  $fk_parent_line = 0;
1018  $num = count($this->lines);
1019 
1020  /*
1021  * Insert products details into db
1022  */
1023  for ($i = 0; $i < $num; $i++) {
1024  $line = $this->lines[$i];
1025 
1026  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1027  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1028  if (!is_object($line)) {
1029  $line = (object) $line;
1030  }
1031 
1032  // Reset fk_parent_line for no child products and special product
1033  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1034  $fk_parent_line = 0;
1035  }
1036 
1037  // Complete vat rate with code
1038  $vatrate = $line->tva_tx;
1039  if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1040  $vatrate .= ' ('.$line->vat_src_code.')';
1041  }
1042 
1043  if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1044  $originid = $line->origin_id;
1045  $origintype = $line->origin;
1046  } else {
1047  $originid = $line->id;
1048  $origintype = $this->element;
1049  }
1050 
1051  // ref_ext
1052  if (empty($line->ref_ext)) {
1053  $line->ref_ext = '';
1054  }
1055 
1056  $result = $this->addline(
1057  $line->desc,
1058  $line->subprice,
1059  $line->qty,
1060  $vatrate,
1061  $line->localtax1_tx,
1062  $line->localtax2_tx,
1063  $line->fk_product,
1064  $line->remise_percent,
1065  $line->info_bits,
1066  $line->fk_remise_except,
1067  'HT',
1068  0,
1069  $line->date_start,
1070  $line->date_end,
1071  $line->product_type,
1072  $line->rang,
1073  $line->special_code,
1074  $fk_parent_line,
1075  $line->fk_fournprice,
1076  $line->pa_ht,
1077  $line->label,
1078  $line->array_options,
1079  $line->fk_unit,
1080  $origintype,
1081  $originid,
1082  0,
1083  $line->ref_ext,
1084  1
1085  );
1086  if ($result < 0) {
1087  if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1088  $this->error = $this->db->lasterror();
1089  $this->errors[] = $this->error;
1090  dol_print_error($this->db);
1091  }
1092  $this->db->rollback();
1093  return -1;
1094  }
1095  // Defined the new fk_parent_line
1096  if ($result > 0 && $line->product_type == 9) {
1097  $fk_parent_line = $result;
1098  }
1099  }
1100 
1101  $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.
1102 
1103  // update ref
1104  $initialref = '(PROV'.$this->id.')';
1105  if (!empty($this->ref)) {
1106  $initialref = $this->ref;
1107  }
1108 
1109  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1110  if ($this->db->query($sql)) {
1111  $this->ref = $initialref;
1112 
1113  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1114  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1115  }
1116 
1117  // Add object linked
1118  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1119  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1120  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, ...))
1121  foreach ($tmp_origin_id as $origin_id) {
1122  $ret = $this->add_object_linked($origin, $origin_id);
1123  if (!$ret) {
1124  $this->error = $this->db->lasterror();
1125  $error++;
1126  }
1127  }
1128  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1129  {
1130  $origin_id = $tmp_origin_id;
1131  $ret = $this->add_object_linked($origin, $origin_id);
1132  if (!$ret) {
1133  $this->error = $this->db->lasterror();
1134  $error++;
1135  }
1136  }
1137  }
1138  }
1139 
1140  if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1141  $originforcontact = $this->origin;
1142  $originidforcontact = $this->origin_id;
1143  if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1144  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1145  $exp = new Expedition($this->db);
1146  $exp->fetch($this->origin_id);
1147  $exp->fetchObjectLinked();
1148  if (count($exp->linkedObjectsIds['commande']) > 0) {
1149  foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1150  $originforcontact = 'commande';
1151  if (is_object($value)) {
1152  $originidforcontact = $value->id;
1153  } else {
1154  $originidforcontact = $value;
1155  }
1156  break; // We take first one
1157  }
1158  }
1159  }
1160 
1161  $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";
1162  $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1163 
1164  $resqlcontact = $this->db->query($sqlcontact);
1165  if ($resqlcontact) {
1166  while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1167  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1168  $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
1169  }
1170  } else {
1171  dol_print_error($resqlcontact);
1172  }
1173  }
1174 
1175  if (!$error) {
1176  $result = $this->insertExtraFields();
1177  if ($result < 0) {
1178  $error++;
1179  }
1180  }
1181 
1182  if (!$error && !$notrigger) {
1183  // Call trigger
1184  $result = $this->call_trigger('ORDER_CREATE', $user);
1185  if ($result < 0) {
1186  $error++;
1187  }
1188  // End call triggers
1189  }
1190 
1191  if (!$error) {
1192  $this->db->commit();
1193  return $this->id;
1194  } else {
1195  $this->db->rollback();
1196  return -1 * $error;
1197  }
1198  } else {
1199  $this->error = $this->db->lasterror();
1200  $this->db->rollback();
1201  return -1;
1202  }
1203  }
1204 
1205  return 0;
1206  } else {
1207  dol_print_error($this->db);
1208  $this->db->rollback();
1209  return -1;
1210  }
1211  }
1212 
1213 
1221  public function createFromClone(User $user, $socid = 0)
1222  {
1223  global $conf, $user, $hookmanager;
1224 
1225  $error = 0;
1226 
1227  $this->db->begin();
1228 
1229  // get lines so they will be clone
1230  foreach ($this->lines as $line) {
1231  $line->fetch_optionals();
1232  }
1233 
1234  // Load source object
1235  $objFrom = clone $this;
1236 
1237  // Change socid if needed
1238  if (!empty($socid) && $socid != $this->socid) {
1239  $objsoc = new Societe($this->db);
1240 
1241  if ($objsoc->fetch($socid) > 0) {
1242  $this->socid = $objsoc->id;
1243  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1244  $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1245  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1246  $this->fk_project = 0;
1247  $this->fk_delivery_address = 0;
1248  }
1249 
1250  // TODO Change product price if multi-prices
1251  }
1252 
1253  $this->id = 0;
1254  $this->ref = '';
1255  $this->statut = self::STATUS_DRAFT;
1256 
1257  // Clear fields
1258  $this->user_author_id = $user->id;
1259  $this->user_valid = 0; // deprecated
1260  $this->user_validation_id = 0;
1261  $this->date = dol_now();
1262  $this->date_commande = dol_now();
1263  $this->date_creation = '';
1264  $this->date_validation = '';
1265  if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) {
1266  $this->ref_client = '';
1267  $this->ref_customer = '';
1268  }
1269 
1270  // Do not clone ref_ext
1271  $num = count($this->lines);
1272  for ($i = 0; $i < $num; $i++) {
1273  $this->lines[$i]->ref_ext = '';
1274  }
1275 
1276  // Create clone
1277  $this->context['createfromclone'] = 'createfromclone';
1278  $result = $this->create($user);
1279  if ($result < 0) {
1280  $error++;
1281  }
1282 
1283  if (!$error) {
1284  // copy internal contacts
1285  if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1286  $error++;
1287  }
1288  }
1289 
1290  if (!$error) {
1291  // copy external contacts if same company
1292  if ($this->socid == $objFrom->socid) {
1293  if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1294  $error++;
1295  }
1296  }
1297  }
1298 
1299  if (!$error) {
1300  // Hook of thirdparty module
1301  if (is_object($hookmanager)) {
1302  $parameters = array('objFrom'=>$objFrom);
1303  $action = '';
1304  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1305  if ($reshook < 0) {
1306  $this->setErrorsFromObject($hookmanager);
1307  $error++;
1308  }
1309  }
1310  }
1311 
1312  unset($this->context['createfromclone']);
1313 
1314  // End
1315  if (!$error) {
1316  $this->db->commit();
1317  return $this->id;
1318  } else {
1319  $this->db->rollback();
1320  return -1;
1321  }
1322  }
1323 
1324 
1332  public function createFromProposal($object, User $user)
1333  {
1334  global $conf, $hookmanager;
1335 
1336  dol_include_once('/multicurrency/class/multicurrency.class.php');
1337  dol_include_once('/core/class/extrafields.class.php');
1338 
1339  $error = 0;
1340 
1341 
1342  $this->date_commande = dol_now();
1343  $this->source = 0;
1344 
1345  $num = count($object->lines);
1346  for ($i = 0; $i < $num; $i++) {
1347  $line = new OrderLine($this->db);
1348 
1349  $line->libelle = $object->lines[$i]->libelle;
1350  $line->label = $object->lines[$i]->label;
1351  $line->desc = $object->lines[$i]->desc;
1352  $line->price = $object->lines[$i]->price;
1353  $line->subprice = $object->lines[$i]->subprice;
1354  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1355  $line->tva_tx = $object->lines[$i]->tva_tx;
1356  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1357  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1358  $line->qty = $object->lines[$i]->qty;
1359  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1360  $line->remise_percent = $object->lines[$i]->remise_percent;
1361  $line->fk_product = $object->lines[$i]->fk_product;
1362  $line->info_bits = $object->lines[$i]->info_bits;
1363  $line->product_type = $object->lines[$i]->product_type;
1364  $line->rang = $object->lines[$i]->rang;
1365  $line->special_code = $object->lines[$i]->special_code;
1366  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1367  $line->fk_unit = $object->lines[$i]->fk_unit;
1368 
1369  $line->date_start = $object->lines[$i]->date_start;
1370  $line->date_end = $object->lines[$i]->date_end;
1371 
1372  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1373  $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);
1374  $line->pa_ht = $marginInfos[0];
1375  $line->marge_tx = $marginInfos[1];
1376  $line->marque_tx = $marginInfos[2];
1377 
1378  $line->origin = $object->element;
1379  $line->origin_id = $object->lines[$i]->id;
1380 
1381  // get extrafields from original line
1382  $object->lines[$i]->fetch_optionals();
1383  foreach ($object->lines[$i]->array_options as $options_key => $value) {
1384  $line->array_options[$options_key] = $value;
1385  }
1386 
1387  $this->lines[$i] = $line;
1388  }
1389 
1390  $this->entity = $object->entity;
1391  $this->socid = $object->socid;
1392  $this->fk_project = $object->fk_project;
1393  $this->cond_reglement_id = $object->cond_reglement_id;
1394  $this->deposit_percent = $object->deposit_percent;
1395  $this->mode_reglement_id = $object->mode_reglement_id;
1396  $this->fk_account = $object->fk_account;
1397  $this->availability_id = $object->availability_id;
1398  $this->demand_reason_id = $object->demand_reason_id;
1399  $this->date_livraison = $object->date_livraison; // deprecated
1400  $this->delivery_date = $object->date_livraison;
1401  $this->shipping_method_id = $object->shipping_method_id;
1402  $this->warehouse_id = $object->warehouse_id;
1403  $this->fk_delivery_address = $object->fk_delivery_address;
1404  $this->contact_id = $object->contact_id;
1405  $this->ref_client = $object->ref_client;
1406  $this->ref_customer = $object->ref_client;
1407 
1408  if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) {
1409  $this->note_private = $object->note_private;
1410  $this->note_public = $object->note_public;
1411  }
1412 
1413  $this->origin = $object->element;
1414  $this->origin_id = $object->id;
1415 
1416  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1417  if (!empty($conf->multicurrency->enabled)) {
1418  if (!empty($object->multicurrency_code)) {
1419  $this->multicurrency_code = $object->multicurrency_code;
1420  }
1421  if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($object->multicurrency_tx)) {
1422  $this->multicurrency_tx = $object->multicurrency_tx;
1423  }
1424 
1425  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1426  $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1427  $this->fk_multicurrency = $tmparray[0];
1428  $this->multicurrency_tx = $tmparray[1];
1429  } else {
1430  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1431  }
1432  if (empty($this->fk_multicurrency)) {
1433  $this->multicurrency_code = $conf->currency;
1434  $this->fk_multicurrency = 0;
1435  $this->multicurrency_tx = 1;
1436  }
1437  }
1438 
1439  // get extrafields from original line
1440  $object->fetch_optionals();
1441 
1442  $e = new ExtraFields($this->db);
1443  $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1444 
1445  foreach ($object->array_options as $options_key => $value) {
1446  if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1447  $this->array_options[$options_key] = $value;
1448  }
1449  }
1450  // Possibility to add external linked objects with hooks
1451  $this->linked_objects[$this->origin] = $this->origin_id;
1452  if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1453  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1454  }
1455 
1456  $ret = $this->create($user);
1457 
1458  if ($ret > 0) {
1459  // Actions hooked (by external module)
1460  $hookmanager->initHooks(array('orderdao'));
1461 
1462  $parameters = array('objFrom'=>$object);
1463  $action = '';
1464  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1465  if ($reshook < 0) {
1466  $this->setErrorsFromObject($hookmanager);
1467  $error++;
1468  }
1469 
1470  if (!$error) {
1471  // Validate immediatly the order
1472  if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL)) {
1473  $this->fetch($ret);
1474  $this->valid($user);
1475  }
1476  return $ret;
1477  } else {
1478  return -1;
1479  }
1480  } else {
1481  return -1;
1482  }
1483  }
1484 
1485 
1526  public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $info_bits = 0, $fk_remise_except = 0, $price_base_type = 'HT', $pu_ttc = 0, $date_start = '', $date_end = '', $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = null, $pa_ht = 0, $label = '', $array_options = 0, $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $ref_ext = '', $noupdateafterinsertline = 0)
1527  {
1528  global $mysoc, $conf, $langs, $user;
1529 
1530  $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1531  $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";
1532  $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";
1533  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1534 
1535  if ($this->statut == self::STATUS_DRAFT) {
1536  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1537 
1538  // Clean parameters
1539 
1540  if (empty($remise_percent)) {
1541  $remise_percent = 0;
1542  }
1543  if (empty($qty)) {
1544  $qty = 0;
1545  }
1546  if (empty($info_bits)) {
1547  $info_bits = 0;
1548  }
1549  if (empty($rang)) {
1550  $rang = 0;
1551  }
1552  if (empty($txtva)) {
1553  $txtva = 0;
1554  }
1555  if (empty($txlocaltax1)) {
1556  $txlocaltax1 = 0;
1557  }
1558  if (empty($txlocaltax2)) {
1559  $txlocaltax2 = 0;
1560  }
1561  if (empty($fk_parent_line) || $fk_parent_line < 0) {
1562  $fk_parent_line = 0;
1563  }
1564  if (empty($this->fk_multicurrency)) {
1565  $this->fk_multicurrency = 0;
1566  }
1567  if (empty($ref_ext)) {
1568  $ref_ext = '';
1569  }
1570 
1572  $qty = price2num($qty);
1573  $pu_ht = price2num($pu_ht);
1574  $pu_ht_devise = price2num($pu_ht_devise);
1575  $pu_ttc = price2num($pu_ttc);
1576  $pa_ht = price2num($pa_ht);
1577  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1578  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1579  }
1580  $txlocaltax1 = price2num($txlocaltax1);
1581  $txlocaltax2 = price2num($txlocaltax2);
1582  if ($price_base_type == 'HT') {
1583  $pu = $pu_ht;
1584  } else {
1585  $pu = $pu_ttc;
1586  }
1587  $label = trim($label);
1588  $desc = trim($desc);
1589 
1590  // Check parameters
1591  if ($type < 0) {
1592  return -1;
1593  }
1594 
1595  if ($date_start && $date_end && $date_start > $date_end) {
1596  $langs->load("errors");
1597  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1598  return -1;
1599  }
1600 
1601  $this->db->begin();
1602 
1603  $product_type = $type;
1604  if (!empty($fk_product) && $fk_product > 0) {
1605  $product = new Product($this->db);
1606  $result = $product->fetch($fk_product);
1607  $product_type = $product->type;
1608 
1609  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
1610  $langs->load("errors");
1611  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1612  $this->errors[] = $this->error;
1613  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1614  $this->db->rollback();
1616  }
1617  }
1618  // Calcul du total TTC et de la TVA pour la ligne a partir de
1619  // qty, pu, remise_percent et txtva
1620  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1621  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1622 
1623  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1624 
1625  // Clean vat code
1626  $reg = array();
1627  $vat_src_code = '';
1628  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1629  $vat_src_code = $reg[1];
1630  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1631  }
1632 
1633  $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);
1634 
1635  /*var_dump($txlocaltax1);
1636  var_dump($txlocaltax2);
1637  var_dump($localtaxes_type);
1638  var_dump($tabprice);
1639  var_dump($tabprice[9]);
1640  var_dump($tabprice[10]);
1641  exit;*/
1642 
1643  $total_ht = $tabprice[0];
1644  $total_tva = $tabprice[1];
1645  $total_ttc = $tabprice[2];
1646  $total_localtax1 = $tabprice[9];
1647  $total_localtax2 = $tabprice[10];
1648  $pu_ht = $tabprice[3];
1649 
1650  // MultiCurrency
1651  $multicurrency_total_ht = $tabprice[16];
1652  $multicurrency_total_tva = $tabprice[17];
1653  $multicurrency_total_ttc = $tabprice[18];
1654  $pu_ht_devise = $tabprice[19];
1655 
1656  // Rang to use
1657  $ranktouse = $rang;
1658  if ($ranktouse == -1) {
1659  $rangmax = $this->line_max($fk_parent_line);
1660  $ranktouse = $rangmax + 1;
1661  }
1662 
1663  // TODO A virer
1664  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1665  $price = $pu;
1666  $remise = 0;
1667  if ($remise_percent > 0) {
1668  $remise = round(($pu * $remise_percent / 100), 2);
1669  $price = $pu - $remise;
1670  }
1671 
1672  // Insert line
1673  $this->line = new OrderLine($this->db);
1674 
1675  $this->line->context = $this->context;
1676 
1677  $this->line->fk_commande = $this->id;
1678  $this->line->label = $label;
1679  $this->line->desc = $desc;
1680  $this->line->qty = $qty;
1681  $this->line->ref_ext = $ref_ext;
1682 
1683  $this->line->vat_src_code = $vat_src_code;
1684  $this->line->tva_tx = $txtva;
1685  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1686  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1687  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1688  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1689  $this->line->fk_product = $fk_product;
1690  $this->line->product_type = $product_type;
1691  $this->line->fk_remise_except = $fk_remise_except;
1692  $this->line->remise_percent = $remise_percent;
1693  $this->line->subprice = $pu_ht;
1694  $this->line->rang = $ranktouse;
1695  $this->line->info_bits = $info_bits;
1696  $this->line->total_ht = $total_ht;
1697  $this->line->total_tva = $total_tva;
1698  $this->line->total_localtax1 = $total_localtax1;
1699  $this->line->total_localtax2 = $total_localtax2;
1700  $this->line->total_ttc = $total_ttc;
1701  $this->line->special_code = $special_code;
1702  $this->line->origin = $origin;
1703  $this->line->origin_id = $origin_id;
1704  $this->line->fk_parent_line = $fk_parent_line;
1705  $this->line->fk_unit = $fk_unit;
1706 
1707  $this->line->date_start = $date_start;
1708  $this->line->date_end = $date_end;
1709 
1710  $this->line->fk_fournprice = $fk_fournprice;
1711  $this->line->pa_ht = $pa_ht;
1712 
1713  // Multicurrency
1714  $this->line->fk_multicurrency = $this->fk_multicurrency;
1715  $this->line->multicurrency_code = $this->multicurrency_code;
1716  $this->line->multicurrency_subprice = $pu_ht_devise;
1717  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1718  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1719  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1720 
1721  // TODO Ne plus utiliser
1722  $this->line->price = $price;
1723 
1724  if (is_array($array_options) && count($array_options) > 0) {
1725  $this->line->array_options = $array_options;
1726  }
1727 
1728  $result = $this->line->insert($user);
1729  if ($result > 0) {
1730  // Reorder if child line
1731  if (!empty($fk_parent_line)) {
1732  $this->line_order(true, 'DESC');
1733  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1734  $linecount = count($this->lines);
1735  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1736  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1737  }
1738  }
1739 
1740  // Mise a jour informations denormalisees au niveau de la commande meme
1741  if (empty($noupdateafterinsertline)) {
1742  $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.
1743  }
1744 
1745  if ($result > 0) {
1746  $this->db->commit();
1747  return $this->line->id;
1748  } else {
1749  $this->db->rollback();
1750  return -1;
1751  }
1752  } else {
1753  $this->error = $this->line->error;
1754  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1755  $this->db->rollback();
1756  return -2;
1757  }
1758  } else {
1759  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1760  return -3;
1761  }
1762  }
1763 
1764 
1765  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1779  public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1780  {
1781  // phpcs:enable
1782  global $conf, $mysoc;
1783 
1784  if (!$qty) {
1785  $qty = 1;
1786  }
1787 
1788  if ($idproduct > 0) {
1789  $prod = new Product($this->db);
1790  $prod->fetch($idproduct);
1791 
1792  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1793  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1794  if (empty($tva_tx)) {
1795  $tva_npr = 0;
1796  }
1797  $vat_src_code = ''; // May be defined into tva_tx
1798 
1799  $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1800  $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1801 
1802  // multiprix
1803  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1804  $price = $prod->multiprices[$this->thirdparty->price_level];
1805  } else {
1806  $price = $prod->price;
1807  }
1808 
1809  $line = new OrderLine($this->db);
1810 
1811  $line->context = $this->context;
1812 
1813  $line->fk_product = $idproduct;
1814  $line->desc = $prod->description;
1815  $line->qty = $qty;
1816  $line->subprice = $price;
1817  $line->remise_percent = $remise_percent;
1818  $line->vat_src_code = $vat_src_code;
1819  $line->tva_tx = $tva_tx;
1820  $line->localtax1_tx = $localtax1_tx;
1821  $line->localtax2_tx = $localtax2_tx;
1822  $line->ref = $prod->ref;
1823  $line->libelle = $prod->label;
1824  $line->product_desc = $prod->description;
1825  $line->fk_unit = $prod->fk_unit;
1826 
1827  // Save the start and end date of the line in the object
1828  if ($date_start) {
1829  $line->date_start = $date_start;
1830  }
1831  if ($date_end) {
1832  $line->date_end = $date_end;
1833  }
1834 
1835  $this->lines[] = $line;
1836 
1855  }
1856  }
1857 
1858 
1868  public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1869  {
1870  // Check parameters
1871  if (empty($id) && empty($ref) && empty($ref_ext)) {
1872  return -1;
1873  }
1874 
1875  $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';
1876  $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';
1877  $sql .= ', c.fk_account';
1878  $sql .= ', c.date_commande, c.date_valid, c.tms';
1879  $sql .= ', c.date_livraison as delivery_date';
1880  $sql .= ', c.fk_shipping_method';
1881  $sql .= ', c.fk_warehouse';
1882  $sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1883  $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';
1884  $sql .= ', c.fk_incoterms, c.location_incoterms';
1885  $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1886  $sql .= ", c.module_source, c.pos_source";
1887  $sql .= ", i.libelle as label_incoterms";
1888  $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1889  $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1890  $sql .= ', ca.code as availability_code, ca.label as availability_label';
1891  $sql .= ', dr.code as demand_reason_code';
1892  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1893  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1894  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1895  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1896  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1897  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1898 
1899  if ($id) {
1900  $sql .= " WHERE c.rowid=".((int) $id);
1901  } else {
1902  $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1903  }
1904 
1905  if ($ref) {
1906  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1907  }
1908  if ($ref_ext) {
1909  $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1910  }
1911 
1912  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1913  $result = $this->db->query($sql);
1914  if ($result) {
1915  $obj = $this->db->fetch_object($result);
1916  if ($obj) {
1917  $this->id = $obj->rowid;
1918  $this->entity = $obj->entity;
1919 
1920  $this->ref = $obj->ref;
1921  $this->ref_client = $obj->ref_client;
1922  $this->ref_customer = $obj->ref_client;
1923  $this->ref_ext = $obj->ref_ext;
1924 
1925  $this->socid = $obj->fk_soc;
1926  $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1927 
1928  $this->fk_project = $obj->fk_project;
1929  $this->project = null; // Clear if another value was already set by fetch_projet
1930 
1931  $this->statut = $obj->fk_statut;
1932  $this->status = $obj->fk_statut;
1933 
1934  $this->user_author_id = $obj->fk_user_author;
1935  $this->user_creation_id = $obj->fk_user_author;
1936  $this->user_validation_id = $obj->fk_user_valid;
1937  $this->user_valid = $obj->fk_user_valid; // deprecated
1938  $this->user_modification_id = $obj->fk_user_modif;
1939  $this->user_modification = $obj->fk_user_modif;
1940  $this->total_ht = $obj->total_ht;
1941  $this->total_tva = $obj->total_tva;
1942  $this->total_localtax1 = $obj->total_localtax1;
1943  $this->total_localtax2 = $obj->total_localtax2;
1944  $this->total_ttc = $obj->total_ttc;
1945  $this->date = $this->db->jdate($obj->date_commande);
1946  $this->date_commande = $this->db->jdate($obj->date_commande);
1947  $this->date_creation = $this->db->jdate($obj->date_creation);
1948  $this->date_validation = $this->db->jdate($obj->date_valid);
1949  $this->date_modification = $this->db->jdate($obj->tms);
1950  $this->remise = $obj->remise;
1951  $this->remise_percent = $obj->remise_percent; // TODO deprecated
1952  $this->remise_absolue = $obj->remise_absolue;
1953  $this->source = $obj->source;
1954  $this->billed = $obj->billed;
1955  $this->note = $obj->note_private; // deprecated
1956  $this->note_private = $obj->note_private;
1957  $this->note_public = $obj->note_public;
1958  $this->model_pdf = $obj->model_pdf;
1959  $this->modelpdf = $obj->model_pdf; // deprecated
1960  $this->last_main_doc = $obj->last_main_doc;
1961  $this->mode_reglement_id = $obj->fk_mode_reglement;
1962  $this->mode_reglement_code = $obj->mode_reglement_code;
1963  $this->mode_reglement = $obj->mode_reglement_libelle;
1964  $this->cond_reglement_id = $obj->fk_cond_reglement;
1965  $this->cond_reglement_code = $obj->cond_reglement_code;
1966  $this->cond_reglement = $obj->cond_reglement_libelle;
1967  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1968  $this->deposit_percent = $obj->deposit_percent;
1969  $this->fk_account = $obj->fk_account;
1970  $this->availability_id = $obj->fk_availability;
1971  $this->availability_code = $obj->availability_code;
1972  $this->availability = $obj->availability_label;
1973  $this->demand_reason_id = $obj->fk_input_reason;
1974  $this->demand_reason_code = $obj->demand_reason_code;
1975  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1976  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1977  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1978  $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1979  $this->fk_delivery_address = $obj->fk_delivery_address;
1980  $this->module_source = $obj->module_source;
1981  $this->pos_source = $obj->pos_source;
1982 
1983  //Incoterms
1984  $this->fk_incoterms = $obj->fk_incoterms;
1985  $this->location_incoterms = $obj->location_incoterms;
1986  $this->label_incoterms = $obj->label_incoterms;
1987 
1988  // Multicurrency
1989  $this->fk_multicurrency = $obj->fk_multicurrency;
1990  $this->multicurrency_code = $obj->multicurrency_code;
1991  $this->multicurrency_tx = $obj->multicurrency_tx;
1992  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1993  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1994  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1995 
1996  $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1997 
1998  $this->lines = array();
1999 
2000  if ($this->statut == self::STATUS_DRAFT) {
2001  $this->brouillon = 1;
2002  }
2003 
2004  // Retrieve all extrafield
2005  // fetch optionals attributes and labels
2006  $this->fetch_optionals();
2007 
2008  $this->db->free($result);
2009 
2010  // Lines
2011  $result = $this->fetch_lines();
2012  if ($result < 0) {
2013  return -3;
2014  }
2015  return 1;
2016  } else {
2017  $this->error = 'Order with id '.$id.' not found sql='.$sql;
2018  return 0;
2019  }
2020  } else {
2021  $this->error = $this->db->error();
2022  return -1;
2023  }
2024  }
2025 
2026 
2027  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2034  public function insert_discount($idremise)
2035  {
2036  // phpcs:enable
2037  global $langs;
2038 
2039  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2040  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2041 
2042  $this->db->begin();
2043 
2044  $remise = new DiscountAbsolute($this->db);
2045  $result = $remise->fetch($idremise);
2046 
2047  if ($result > 0) {
2048  if ($remise->fk_facture) { // Protection against multiple submission
2049  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2050  $this->db->rollback();
2051  return -5;
2052  }
2053 
2054  $line = new OrderLine($this->db);
2055 
2056  $line->fk_commande = $this->id;
2057  $line->fk_remise_except = $remise->id;
2058  $line->desc = $remise->description; // Description ligne
2059  $line->vat_src_code = $remise->vat_src_code;
2060  $line->tva_tx = $remise->tva_tx;
2061  $line->subprice = -$remise->amount_ht;
2062  $line->price = -$remise->amount_ht;
2063  $line->fk_product = 0; // Id produit predefini
2064  $line->qty = 1;
2065  $line->remise_percent = 0;
2066  $line->rang = -1;
2067  $line->info_bits = 2;
2068 
2069  $line->total_ht = -$remise->amount_ht;
2070  $line->total_tva = -$remise->amount_tva;
2071  $line->total_ttc = -$remise->amount_ttc;
2072 
2073  $result = $line->insert();
2074  if ($result > 0) {
2075  $result = $this->update_price(1);
2076  if ($result > 0) {
2077  $this->db->commit();
2078  return 1;
2079  } else {
2080  $this->db->rollback();
2081  return -1;
2082  }
2083  } else {
2084  $this->error = $line->error;
2085  $this->errors = $line->errors;
2086  $this->db->rollback();
2087  return -2;
2088  }
2089  } else {
2090  $this->db->rollback();
2091  return -2;
2092  }
2093  }
2094 
2095 
2096  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2104  public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2105  {
2106  // phpcs:enable
2107  global $langs, $conf;
2108 
2109  $this->lines = array();
2110 
2111  $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,';
2112  $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,';
2113  $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2114  $sql .= ' l.fk_unit,';
2115  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2116  $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,';
2117  $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2118  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2119  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2120  $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2121  if ($only_product) {
2122  $sql .= ' AND p.fk_product_type = 0';
2123  }
2124  $sql .= ' ORDER BY l.rang, l.rowid';
2125 
2126  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2127  $result = $this->db->query($sql);
2128  if ($result) {
2129  $num = $this->db->num_rows($result);
2130 
2131  $i = 0;
2132  while ($i < $num) {
2133  $objp = $this->db->fetch_object($result);
2134 
2135  $line = new OrderLine($this->db);
2136 
2137  $line->rowid = $objp->rowid;
2138  $line->id = $objp->rowid;
2139  $line->fk_commande = $objp->fk_commande;
2140  $line->commande_id = $objp->fk_commande;
2141  $line->label = $objp->custom_label;
2142  $line->desc = $objp->description;
2143  $line->description = $objp->description; // Description line
2144  $line->product_type = $objp->product_type;
2145  $line->qty = $objp->qty;
2146  $line->ref_ext = $objp->ref_ext;
2147 
2148  $line->vat_src_code = $objp->vat_src_code;
2149  $line->tva_tx = $objp->tva_tx;
2150  $line->localtax1_tx = $objp->localtax1_tx;
2151  $line->localtax2_tx = $objp->localtax2_tx;
2152  $line->localtax1_type = $objp->localtax1_type;
2153  $line->localtax2_type = $objp->localtax2_type;
2154  $line->total_ht = $objp->total_ht;
2155  $line->total_ttc = $objp->total_ttc;
2156  $line->total_tva = $objp->total_tva;
2157  $line->total_localtax1 = $objp->total_localtax1;
2158  $line->total_localtax2 = $objp->total_localtax2;
2159  $line->subprice = $objp->subprice;
2160  $line->fk_remise_except = $objp->fk_remise_except;
2161  $line->remise_percent = $objp->remise_percent;
2162  $line->price = $objp->price;
2163  $line->fk_product = $objp->fk_product;
2164  $line->fk_fournprice = $objp->fk_fournprice;
2165  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2166  $line->pa_ht = $marginInfos[0];
2167  $line->marge_tx = $marginInfos[1];
2168  $line->marque_tx = $marginInfos[2];
2169  $line->rang = $objp->rang;
2170  $line->info_bits = $objp->info_bits;
2171  $line->special_code = $objp->special_code;
2172  $line->fk_parent_line = $objp->fk_parent_line;
2173 
2174  $line->ref = $objp->product_ref;
2175  $line->libelle = $objp->product_label;
2176 
2177  $line->product_ref = $objp->product_ref;
2178  $line->product_label = $objp->product_label;
2179  $line->product_tosell = $objp->product_tosell;
2180  $line->product_tobuy = $objp->product_tobuy;
2181  $line->product_desc = $objp->product_desc;
2182  $line->product_tobatch = $objp->product_tobatch;
2183  $line->product_barcode = $objp->product_barcode;
2184 
2185  $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2186  $line->fk_unit = $objp->fk_unit;
2187 
2188  $line->weight = $objp->weight;
2189  $line->weight_units = $objp->weight_units;
2190  $line->volume = $objp->volume;
2191  $line->volume_units = $objp->volume_units;
2192 
2193  $line->date_start = $this->db->jdate($objp->date_start);
2194  $line->date_end = $this->db->jdate($objp->date_end);
2195 
2196  // Multicurrency
2197  $line->fk_multicurrency = $objp->fk_multicurrency;
2198  $line->multicurrency_code = $objp->multicurrency_code;
2199  $line->multicurrency_subprice = $objp->multicurrency_subprice;
2200  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2201  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2202  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2203 
2204  $line->fetch_optionals();
2205 
2206  // multilangs
2207  if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2208  $tmpproduct = new Product($this->db);
2209  $tmpproduct->fetch($objp->fk_product);
2210  $tmpproduct->getMultiLangs();
2211 
2212  $line->multilangs = $tmpproduct->multilangs;
2213  }
2214 
2215  $this->lines[$i] = $line;
2216 
2217  $i++;
2218  }
2219 
2220  $this->db->free($result);
2221 
2222  return 1;
2223  } else {
2224  $this->error = $this->db->error();
2225  return -3;
2226  }
2227  }
2228 
2229 
2235  public function getNbOfProductsLines()
2236  {
2237  $nb = 0;
2238  foreach ($this->lines as $line) {
2239  if ($line->product_type == 0) {
2240  $nb++;
2241  }
2242  }
2243  return $nb;
2244  }
2245 
2251  public function getNbOfServicesLines()
2252  {
2253  $nb = 0;
2254  foreach ($this->lines as $line) {
2255  if ($line->product_type == 1) {
2256  $nb++;
2257  }
2258  }
2259  return $nb;
2260  }
2261 
2267  public function getNbOfShipments()
2268  {
2269  $nb = 0;
2270 
2271  $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2272  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2273  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2274  $sql .= ' WHERE';
2275  $sql .= ' ed.fk_origin_line = cd.rowid';
2276  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2277  //print $sql;
2278 
2279  dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2280  $resql = $this->db->query($sql);
2281  if ($resql) {
2282  $obj = $this->db->fetch_object($resql);
2283  if ($obj) {
2284  $nb = $obj->nb;
2285  }
2286 
2287  $this->db->free($resql);
2288  return $nb;
2289  } else {
2290  $this->error = $this->db->lasterror();
2291  return -1;
2292  }
2293  }
2294 
2303  public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2304  {
2305  $this->expeditions = array();
2306 
2307  $sql = 'SELECT cd.rowid, cd.fk_product,';
2308  $sql .= ' sum(ed.qty) as qty';
2309  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2310  if ($filtre_statut >= 0) {
2311  $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2312  }
2313  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2314  $sql .= ' WHERE';
2315  if ($filtre_statut >= 0) {
2316  $sql .= ' ed.fk_expedition = e.rowid AND';
2317  }
2318  $sql .= ' ed.fk_origin_line = cd.rowid';
2319  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2320  if ($fk_product > 0) {
2321  $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2322  }
2323  if ($filtre_statut >= 0) {
2324  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2325  }
2326  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2327  //print $sql;
2328 
2329  dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2330  $resql = $this->db->query($sql);
2331  if ($resql) {
2332  $num = $this->db->num_rows($resql);
2333  $i = 0;
2334  while ($i < $num) {
2335  $obj = $this->db->fetch_object($resql);
2336  $this->expeditions[$obj->rowid] = $obj->qty;
2337  $i++;
2338  }
2339  $this->db->free($resql);
2340  return $num;
2341  } else {
2342  $this->error = $this->db->lasterror();
2343  return -1;
2344  }
2345  }
2346 
2352  public function countNbOfShipments()
2353  {
2354  $sql = 'SELECT count(*)';
2355  $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2356  $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2357  $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2358  $sql .= " AND el.sourcetype = 'commande'";
2359  $sql .= " AND el.fk_target = e.rowid";
2360  $sql .= " AND el.targettype = 'shipping'";
2361 
2362  $resql = $this->db->query($sql);
2363  if ($resql) {
2364  $row = $this->db->fetch_row($resql);
2365  return $row[0];
2366  } else {
2367  dol_print_error($this->db);
2368  }
2369 
2370  return 0;
2371  }
2372 
2373  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2382  public function stock_array($filtre_statut = self::STATUS_CANCELED)
2383  {
2384  // phpcs:enable
2385  $this->stocks = array();
2386 
2387  // Tableau des id de produit de la commande
2388  $array_of_product = array();
2389 
2390  // Recherche total en stock pour chaque produit
2391  // TODO $array_of_product est défini vide juste au dessus !!
2392  if (count($array_of_product)) {
2393  $sql = "SELECT fk_product, sum(ps.reel) as total";
2394  $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2395  $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2396  $sql .= ' GROUP BY fk_product';
2397  $resql = $this->db->query($sql);
2398  if ($resql) {
2399  $num = $this->db->num_rows($resql);
2400  $i = 0;
2401  while ($i < $num) {
2402  $obj = $this->db->fetch_object($resql);
2403  $this->stocks[$obj->fk_product] = $obj->total;
2404  $i++;
2405  }
2406  $this->db->free($resql);
2407  }
2408  }
2409  return 0;
2410  }
2411 
2420  public function deleteline($user = null, $lineid = 0, $id = 0)
2421  {
2422  if ($this->statut == self::STATUS_DRAFT) {
2423  $this->db->begin();
2424 
2425  // Delete line
2426  $line = new OrderLine($this->db);
2427 
2428  $line->context = $this->context;
2429 
2430  // Load data
2431  $line->fetch($lineid);
2432 
2433  if ($id > 0 && $line->fk_commande != $id) {
2434  $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2435  return -1;
2436  }
2437 
2438  // Memorize previous line for triggers
2439  $staticline = clone $line;
2440  $line->oldline = $staticline;
2441 
2442  if ($line->delete($user) > 0) {
2443  $result = $this->update_price(1);
2444 
2445  if ($result > 0) {
2446  $this->db->commit();
2447  return 1;
2448  } else {
2449  $this->db->rollback();
2450  $this->error = $this->db->lasterror();
2451  return -1;
2452  }
2453  } else {
2454  $this->db->rollback();
2455  $this->error = $line->error;
2456  return -1;
2457  }
2458  } else {
2459  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2460  return -1;
2461  }
2462  }
2463 
2464  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2475  public function set_remise($user, $remise, $notrigger = 0)
2476  {
2477  // phpcs:enable
2478  dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2479  return $this->setDiscount($user, $remise, $notrigger);
2480  }
2481 
2491  public function setDiscount($user, $remise, $notrigger = 0)
2492  {
2493  $remise = trim($remise) ?trim($remise) : 0;
2494 
2495  if ($user->hasRight('commande', 'creer')) {
2496  $error = 0;
2497 
2498  $this->db->begin();
2499 
2500  $remise = price2num($remise, 2);
2501 
2502  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2503  $sql .= ' SET remise_percent = '.((float) $remise);
2504  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2505 
2506  dol_syslog(__METHOD__, LOG_DEBUG);
2507  $resql = $this->db->query($sql);
2508  if (!$resql) {
2509  $this->errors[] = $this->db->error();
2510  $error++;
2511  }
2512 
2513  if (!$error) {
2514  $this->oldcopy = clone $this;
2515  $this->remise_percent = $remise;
2516  $this->update_price(1);
2517  }
2518 
2519  if (!$notrigger && empty($error)) {
2520  // Call trigger
2521  $result = $this->call_trigger('ORDER_MODIFY', $user);
2522  if ($result < 0) {
2523  $error++;
2524  }
2525  // End call triggers
2526  }
2527 
2528  if (!$error) {
2529  $this->db->commit();
2530  return 1;
2531  } else {
2532  foreach ($this->errors as $errmsg) {
2533  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2534  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2535  }
2536  $this->db->rollback();
2537  return -1 * $error;
2538  }
2539  }
2540 
2541  return 0;
2542  }
2543 
2544 
2545  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2554  public function set_remise_absolue($user, $remise, $notrigger = 0)
2555  {
2556  // phpcs:enable
2557  if (empty($remise)) {
2558  $remise = 0;
2559  }
2560 
2561  $remise = price2num($remise);
2562 
2563  if ($user->hasRight('commande', 'creer')) {
2564  $error = 0;
2565 
2566  $this->db->begin();
2567 
2568  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2569  $sql .= ' SET remise_absolue = '.((float) $remise);
2570  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2571 
2572  dol_syslog(__METHOD__, LOG_DEBUG);
2573  $resql = $this->db->query($sql);
2574  if (!$resql) {
2575  $this->errors[] = $this->db->error();
2576  $error++;
2577  }
2578 
2579  if (!$error) {
2580  $this->oldcopy = clone $this;
2581  $this->remise_absolue = $remise;
2582  $this->update_price(1);
2583  }
2584 
2585  if (!$notrigger && empty($error)) {
2586  // Call trigger
2587  $result = $this->call_trigger('ORDER_MODIFY', $user);
2588  if ($result < 0) {
2589  $error++;
2590  }
2591  // End call triggers
2592  }
2593 
2594  if (!$error) {
2595  $this->db->commit();
2596  return 1;
2597  } else {
2598  foreach ($this->errors as $errmsg) {
2599  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2600  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2601  }
2602  $this->db->rollback();
2603  return -1 * $error;
2604  }
2605  }
2606 
2607  return 0;
2608  }
2609 
2610 
2611  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2620  public function set_date($user, $date, $notrigger = 0)
2621  {
2622  // phpcs:enable
2623  if ($user->hasRight('commande', 'creer')) {
2624  $error = 0;
2625 
2626  $this->db->begin();
2627 
2628  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2629  $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2630  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2631 
2632  dol_syslog(__METHOD__, LOG_DEBUG);
2633  $resql = $this->db->query($sql);
2634  if (!$resql) {
2635  $this->errors[] = $this->db->error();
2636  $error++;
2637  }
2638 
2639  if (!$error) {
2640  $this->oldcopy = clone $this;
2641  $this->date = $date;
2642  }
2643 
2644  if (!$notrigger && empty($error)) {
2645  // Call trigger
2646  $result = $this->call_trigger('ORDER_MODIFY', $user);
2647  if ($result < 0) {
2648  $error++;
2649  }
2650  // End call triggers
2651  }
2652 
2653  if (!$error) {
2654  $this->db->commit();
2655  return 1;
2656  } else {
2657  foreach ($this->errors as $errmsg) {
2658  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2659  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2660  }
2661  $this->db->rollback();
2662  return -1 * $error;
2663  }
2664  } else {
2665  return -2;
2666  }
2667  }
2668 
2669  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2679  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2680  {
2681  // phpcs:enable
2682  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2683  }
2684 
2693  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2694  {
2695  if ($user->hasRight('commande', 'creer')) {
2696  $error = 0;
2697 
2698  $this->db->begin();
2699 
2700  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2701  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2702  $sql .= " WHERE rowid = ".((int) $this->id);
2703 
2704  dol_syslog(__METHOD__, LOG_DEBUG);
2705  $resql = $this->db->query($sql);
2706  if (!$resql) {
2707  $this->errors[] = $this->db->error();
2708  $error++;
2709  }
2710 
2711  if (!$error) {
2712  $this->oldcopy = clone $this;
2713  $this->date_livraison = $delivery_date;
2714  $this->delivery_date = $delivery_date;
2715  }
2716 
2717  if (!$notrigger && empty($error)) {
2718  // Call trigger
2719  $result = $this->call_trigger('ORDER_MODIFY', $user);
2720  if ($result < 0) {
2721  $error++;
2722  }
2723  // End call triggers
2724  }
2725 
2726  if (!$error) {
2727  $this->db->commit();
2728  return 1;
2729  } else {
2730  foreach ($this->errors as $errmsg) {
2731  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2732  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2733  }
2734  $this->db->rollback();
2735  return -1 * $error;
2736  }
2737  } else {
2738  return -2;
2739  }
2740  }
2741 
2742  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2756  public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2757  {
2758  // phpcs:enable
2759  global $user;
2760 
2761  $ga = array();
2762 
2763  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2764  $sql .= " c.rowid as cid, c.ref";
2765  if (empty($user->rights->societe->client->voir) && !$socid) {
2766  $sql .= ", sc.fk_soc, sc.fk_user";
2767  }
2768  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2769  if (empty($user->rights->societe->client->voir) && !$socid) {
2770  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2771  }
2772  $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2773  $sql .= " AND c.fk_soc = s.rowid";
2774  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2775  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2776  }
2777  if ($socid) {
2778  $sql .= " AND s.rowid = ".((int) $socid);
2779  }
2780  if ($draft) {
2781  $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2782  }
2783  if (is_object($excluser)) {
2784  $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2785  }
2786  $sql .= $this->db->order($sortfield, $sortorder);
2787  $sql .= $this->db->plimit($limit, $offset);
2788 
2789  $result = $this->db->query($sql);
2790  if ($result) {
2791  $numc = $this->db->num_rows($result);
2792  if ($numc) {
2793  $i = 0;
2794  while ($i < $numc) {
2795  $obj = $this->db->fetch_object($result);
2796 
2797  if ($shortlist == 1) {
2798  $ga[$obj->cid] = $obj->ref;
2799  } elseif ($shortlist == 2) {
2800  $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2801  } else {
2802  $ga[$i]['id'] = $obj->cid;
2803  $ga[$i]['ref'] = $obj->ref;
2804  $ga[$i]['name'] = $obj->name;
2805  }
2806  $i++;
2807  }
2808  }
2809  return $ga;
2810  } else {
2811  dol_print_error($this->db);
2812  return -1;
2813  }
2814  }
2815 
2823  public function availability($availability_id, $notrigger = 0)
2824  {
2825  global $user;
2826 
2827  dol_syslog('Commande::availability('.$availability_id.')');
2828  if ($this->statut >= self::STATUS_DRAFT) {
2829  $error = 0;
2830 
2831  $this->db->begin();
2832 
2833  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2834  $sql .= ' SET fk_availability = '.((int) $availability_id);
2835  $sql .= ' WHERE rowid='.((int) $this->id);
2836 
2837  dol_syslog(__METHOD__, LOG_DEBUG);
2838  $resql = $this->db->query($sql);
2839  if (!$resql) {
2840  $this->errors[] = $this->db->error();
2841  $error++;
2842  }
2843 
2844  if (!$error) {
2845  $this->oldcopy = clone $this;
2846  $this->availability_id = $availability_id;
2847  }
2848 
2849  if (!$notrigger && empty($error)) {
2850  // Call trigger
2851  $result = $this->call_trigger('ORDER_MODIFY', $user);
2852  if ($result < 0) {
2853  $error++;
2854  }
2855  // End call triggers
2856  }
2857 
2858  if (!$error) {
2859  $this->db->commit();
2860  return 1;
2861  } else {
2862  foreach ($this->errors as $errmsg) {
2863  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2864  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2865  }
2866  $this->db->rollback();
2867  return -1 * $error;
2868  }
2869  } else {
2870  $error_str = 'Command status do not meet requirement '.$this->statut;
2871  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2872  $this->error = $error_str;
2873  $this->errors[] = $this->error;
2874  return -2;
2875  }
2876  }
2877 
2878  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2886  public function demand_reason($demand_reason_id, $notrigger = 0)
2887  {
2888  // phpcs:enable
2889  global $user;
2890 
2891  dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2892  if ($this->statut >= self::STATUS_DRAFT) {
2893  $error = 0;
2894 
2895  $this->db->begin();
2896 
2897  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2898  $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2899  $sql .= ' WHERE rowid='.((int) $this->id);
2900 
2901  dol_syslog(__METHOD__, LOG_DEBUG);
2902  $resql = $this->db->query($sql);
2903  if (!$resql) {
2904  $this->errors[] = $this->db->error();
2905  $error++;
2906  }
2907 
2908  if (!$error) {
2909  $this->oldcopy = clone $this;
2910  $this->demand_reason_id = $demand_reason_id;
2911  }
2912 
2913  if (!$notrigger && empty($error)) {
2914  // Call trigger
2915  $result = $this->call_trigger('ORDER_MODIFY', $user);
2916  if ($result < 0) {
2917  $error++;
2918  }
2919  // End call triggers
2920  }
2921 
2922  if (!$error) {
2923  $this->db->commit();
2924  return 1;
2925  } else {
2926  foreach ($this->errors as $errmsg) {
2927  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2928  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2929  }
2930  $this->db->rollback();
2931  return -1 * $error;
2932  }
2933  } else {
2934  $error_str = 'order status do not meet requirement '.$this->statut;
2935  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2936  $this->error = $error_str;
2937  $this->errors[] = $this->error;
2938  return -2;
2939  }
2940  }
2941 
2942  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2951  public function set_ref_client($user, $ref_client, $notrigger = 0)
2952  {
2953  // phpcs:enable
2954  if ($user->hasRight('commande', 'creer')) {
2955  $error = 0;
2956 
2957  $this->db->begin();
2958 
2959  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2960  $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2961  $sql .= ' WHERE rowid = '.((int) $this->id);
2962 
2963  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2964  $resql = $this->db->query($sql);
2965  if (!$resql) {
2966  $this->errors[] = $this->db->error();
2967  $error++;
2968  }
2969 
2970  if (!$error) {
2971  $this->oldcopy = clone $this;
2972  $this->ref_client = $ref_client;
2973  $this->ref_customer = $ref_client;
2974  }
2975 
2976  if (!$notrigger && empty($error)) {
2977  // Call trigger
2978  $result = $this->call_trigger('ORDER_MODIFY', $user);
2979  if ($result < 0) {
2980  $error++;
2981  }
2982  // End call triggers
2983  }
2984  if (!$error) {
2985  $this->db->commit();
2986  return 1;
2987  } else {
2988  foreach ($this->errors as $errmsg) {
2989  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2990  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2991  }
2992  $this->db->rollback();
2993  return -1 * $error;
2994  }
2995  } else {
2996  return -1;
2997  }
2998  }
2999 
3007  public function classifyBilled(User $user, $notrigger = 0)
3008  {
3009  $error = 0;
3010 
3011  if ($this->billed) {
3012  return 0;
3013  }
3014 
3015  $this->db->begin();
3016 
3017  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
3018  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3019 
3020  dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3021  if ($this->db->query($sql)) {
3022  if (!$error) {
3023  $this->oldcopy = clone $this;
3024  $this->billed = 1;
3025  }
3026 
3027  if (!$notrigger && empty($error)) {
3028  // Call trigger
3029  $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3030  if ($result < 0) {
3031  $error++;
3032  }
3033  // End call triggers
3034  }
3035 
3036  if (!$error) {
3037  $this->db->commit();
3038  return 1;
3039  } else {
3040  foreach ($this->errors as $errmsg) {
3041  dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3042  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3043  }
3044  $this->db->rollback();
3045  return -1 * $error;
3046  }
3047  } else {
3048  $this->error = $this->db->error();
3049  $this->db->rollback();
3050  return -1;
3051  }
3052  }
3053 
3061  public function classifyUnBilled(User $user, $notrigger = 0)
3062  {
3063  $error = 0;
3064 
3065  $this->db->begin();
3066 
3067  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3068  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3069 
3070  dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3071  if ($this->db->query($sql)) {
3072  if (!$error) {
3073  $this->oldcopy = clone $this;
3074  $this->billed = 1;
3075  }
3076 
3077  if (!$notrigger && empty($error)) {
3078  // Call trigger
3079  $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3080  if ($result < 0) {
3081  $error++;
3082  }
3083  // End call triggers
3084  }
3085 
3086  if (!$error) {
3087  $this->billed = 0;
3088 
3089  $this->db->commit();
3090  return 1;
3091  } else {
3092  foreach ($this->errors as $errmsg) {
3093  dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3094  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3095  }
3096  $this->db->rollback();
3097  return -1 * $error;
3098  }
3099  } else {
3100  $this->error = $this->db->error();
3101  $this->db->rollback();
3102  return -1;
3103  }
3104  }
3105 
3106 
3137  public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $price_base_type = 'HT', $info_bits = 0, $date_start = '', $date_end = '', $type = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = null, $pa_ht = 0, $label = '', $special_code = 0, $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $ref_ext = '', $rang = 0)
3138  {
3139  global $conf, $mysoc, $langs, $user;
3140 
3141  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");
3142  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3143 
3144  if ($this->statut == Commande::STATUS_DRAFT) {
3145  // Clean parameters
3146  if (empty($qty)) {
3147  $qty = 0;
3148  }
3149  if (empty($info_bits)) {
3150  $info_bits = 0;
3151  }
3152  if (empty($txtva)) {
3153  $txtva = 0;
3154  }
3155  if (empty($txlocaltax1)) {
3156  $txlocaltax1 = 0;
3157  }
3158  if (empty($txlocaltax2)) {
3159  $txlocaltax2 = 0;
3160  }
3161  if (empty($remise_percent)) {
3162  $remise_percent = 0;
3163  }
3164  if (empty($special_code) || $special_code == 3) {
3165  $special_code = 0;
3166  }
3167  if (empty($ref_ext)) {
3168  $ref_ext = '';
3169  }
3170 
3171  if ($date_start && $date_end && $date_start > $date_end) {
3172  $langs->load("errors");
3173  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3174  return -1;
3175  }
3176 
3178  $qty = price2num($qty);
3179  $pu = price2num($pu);
3180  $pa_ht = price2num($pa_ht);
3181  $pu_ht_devise = price2num($pu_ht_devise);
3182  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
3183  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3184  }
3185  $txlocaltax1 = price2num($txlocaltax1);
3186  $txlocaltax2 = price2num($txlocaltax2);
3187 
3188  $this->db->begin();
3189 
3190  // Calcul du total TTC et de la TVA pour la ligne a partir de
3191  // qty, pu, remise_percent et txtva
3192  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3193  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3194 
3195  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3196 
3197  // Clean vat code
3198  $vat_src_code = '';
3199  $reg = array();
3200  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3201  $vat_src_code = $reg[1];
3202  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3203  }
3204 
3205  $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);
3206 
3207  $total_ht = $tabprice[0];
3208  $total_tva = $tabprice[1];
3209  $total_ttc = $tabprice[2];
3210  $total_localtax1 = $tabprice[9];
3211  $total_localtax2 = $tabprice[10];
3212  $pu_ht = $tabprice[3];
3213  $pu_tva = $tabprice[4];
3214  $pu_ttc = $tabprice[5];
3215 
3216  // MultiCurrency
3217  $multicurrency_total_ht = $tabprice[16];
3218  $multicurrency_total_tva = $tabprice[17];
3219  $multicurrency_total_ttc = $tabprice[18];
3220  $pu_ht_devise = $tabprice[19];
3221 
3222  // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3223  $price = $pu_ht;
3224  if ($price_base_type == 'TTC') {
3225  $subprice = $pu_ttc;
3226  } else {
3227  $subprice = $pu_ht;
3228  }
3229  $remise = 0;
3230  if ($remise_percent > 0) {
3231  $remise = round(($pu * $remise_percent / 100), 2);
3232  $price = ($pu - $remise);
3233  }
3234 
3235  //Fetch current line from the database and then clone the object and set it in $oldline property
3236  $line = new OrderLine($this->db);
3237  $line->fetch($rowid);
3238  $line->fetch_optionals();
3239 
3240  if (!empty($line->fk_product)) {
3241  $product = new Product($this->db);
3242  $result = $product->fetch($line->fk_product);
3243  $product_type = $product->type;
3244 
3245  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
3246  $langs->load("errors");
3247  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3248  $this->errors[] = $this->error;
3249 
3250  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3251 
3252  $this->db->rollback();
3254  }
3255  }
3256 
3257  $staticline = clone $line;
3258 
3259  $line->oldline = $staticline;
3260  $this->line = $line;
3261  $this->line->context = $this->context;
3262  $this->line->rang = $rang;
3263 
3264  // Reorder if fk_parent_line change
3265  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3266  $rangmax = $this->line_max($fk_parent_line);
3267  $this->line->rang = $rangmax + 1;
3268  }
3269 
3270  $this->line->id = $rowid;
3271  $this->line->label = $label;
3272  $this->line->desc = $desc;
3273  $this->line->qty = $qty;
3274  $this->line->ref_ext = $ref_ext;
3275 
3276  $this->line->vat_src_code = $vat_src_code;
3277  $this->line->tva_tx = $txtva;
3278  $this->line->localtax1_tx = $txlocaltax1;
3279  $this->line->localtax2_tx = $txlocaltax2;
3280  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3281  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3282  $this->line->remise_percent = $remise_percent;
3283  $this->line->subprice = $subprice;
3284  $this->line->info_bits = $info_bits;
3285  $this->line->special_code = $special_code;
3286  $this->line->total_ht = $total_ht;
3287  $this->line->total_tva = $total_tva;
3288  $this->line->total_localtax1 = $total_localtax1;
3289  $this->line->total_localtax2 = $total_localtax2;
3290  $this->line->total_ttc = $total_ttc;
3291  $this->line->date_start = $date_start;
3292  $this->line->date_end = $date_end;
3293  $this->line->product_type = $type;
3294  $this->line->fk_parent_line = $fk_parent_line;
3295  $this->line->skip_update_total = $skip_update_total;
3296  $this->line->fk_unit = $fk_unit;
3297 
3298  $this->line->fk_fournprice = $fk_fournprice;
3299  $this->line->pa_ht = $pa_ht;
3300 
3301  // Multicurrency
3302  $this->line->multicurrency_subprice = $pu_ht_devise;
3303  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3304  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3305  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3306 
3307  // TODO deprecated
3308  $this->line->price = $price;
3309 
3310  if (is_array($array_options) && count($array_options) > 0) {
3311  // We replace values in this->line->array_options only for entries defined into $array_options
3312  foreach ($array_options as $key => $value) {
3313  $this->line->array_options[$key] = $array_options[$key];
3314  }
3315  }
3316 
3317  $result = $this->line->update($user, $notrigger);
3318  if ($result > 0) {
3319  // Reorder if child line
3320  if (!empty($fk_parent_line)) {
3321  $this->line_order(true, 'DESC');
3322  }
3323 
3324  // Mise a jour info denormalisees
3325  $this->update_price(1, 'auto');
3326 
3327  $this->db->commit();
3328  return $result;
3329  } else {
3330  $this->error = $this->line->error;
3331 
3332  $this->db->rollback();
3333  return -1;
3334  }
3335  } else {
3336  $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3337  $this->errors = array('OrderStatusMakeOperationForbidden');
3338  return -2;
3339  }
3340  }
3341 
3349  public function update(User $user, $notrigger = 0)
3350  {
3351  global $conf;
3352 
3353  $error = 0;
3354 
3355  // Clean parameters
3356  if (isset($this->ref)) {
3357  $this->ref = trim($this->ref);
3358  }
3359  if (isset($this->ref_client)) {
3360  $this->ref_client = trim($this->ref_client);
3361  }
3362  if (isset($this->ref_customer)) {
3363  $this->ref_customer = trim($this->ref_customer);
3364  }
3365  if (isset($this->note) || isset($this->note_private)) {
3366  $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3367  }
3368  if (isset($this->note_public)) {
3369  $this->note_public = trim($this->note_public);
3370  }
3371  if (isset($this->model_pdf)) {
3372  $this->model_pdf = trim($this->model_pdf);
3373  }
3374  if (isset($this->import_key)) {
3375  $this->import_key = trim($this->import_key);
3376  }
3377  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
3378 
3379  // Check parameters
3380  // Put here code to add control on parameters values
3381 
3382  // Update request
3383  $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3384 
3385  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3386  $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3387  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3388  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3389  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3390  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3391  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3392  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3393  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3394  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3395  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3396  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3397  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3398  $sql .= " fk_user_valid=".((isset($this->user_valid) && $this->user_valid > 0) ? $this->user_valid : "null").",";
3399  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3400  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3401  $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3402  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3403  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3404  $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3405  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3406  $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3407  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3408  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3409  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3410  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3411 
3412  $sql .= " WHERE rowid=".((int) $this->id);
3413 
3414  $this->db->begin();
3415 
3416  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3417  $resql = $this->db->query($sql);
3418  if (!$resql) {
3419  $error++; $this->errors[] = "Error ".$this->db->lasterror();
3420  }
3421 
3422  if (!$error) {
3423  $result = $this->insertExtraFields();
3424  if ($result < 0) {
3425  $error++;
3426  }
3427  }
3428 
3429  if (!$error && !$notrigger) {
3430  // Call trigger
3431  $result = $this->call_trigger('ORDER_MODIFY', $user);
3432  if ($result < 0) {
3433  $error++;
3434  }
3435  // End call triggers
3436  }
3437 
3438  // Commit or rollback
3439  if ($error) {
3440  foreach ($this->errors as $errmsg) {
3441  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3442  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3443  }
3444  $this->db->rollback();
3445  return -1 * $error;
3446  } else {
3447  $this->db->commit();
3448  return 1;
3449  }
3450  }
3451 
3459  public function delete($user, $notrigger = 0)
3460  {
3461  global $conf, $langs;
3462  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3463 
3464  $error = 0;
3465 
3466  dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3467 
3468  $this->db->begin();
3469 
3470  if (!$notrigger) {
3471  // Call trigger
3472  $result = $this->call_trigger('ORDER_DELETE', $user);
3473  if ($result < 0) {
3474  $error++;
3475  }
3476  // End call triggers
3477  }
3478 
3479  // Test we can delete
3480  if ($this->countNbOfShipments() != 0) {
3481  $this->errors[] = $langs->trans('SomeShipmentExists');
3482  $error++;
3483  }
3484 
3485  // Delete extrafields of lines and lines
3486  if (!$error && !empty($this->table_element_line)) {
3487  $tabletodelete = $this->table_element_line;
3488  $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).")";
3489  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3490  if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3491  $error++;
3492  $this->error = $this->db->lasterror();
3493  $this->errors[] = $this->error;
3494  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3495  }
3496  }
3497 
3498  if (!$error) {
3499  // Delete linked object
3500  $res = $this->deleteObjectLinked();
3501  if ($res < 0) {
3502  $error++;
3503  }
3504  }
3505 
3506  if (!$error) {
3507  // Delete linked contacts
3508  $res = $this->delete_linked_contact();
3509  if ($res < 0) {
3510  $error++;
3511  }
3512  }
3513 
3514  // Removed extrafields of object
3515  if (!$error) {
3516  $result = $this->deleteExtraFields();
3517  if ($result < 0) {
3518  $error++;
3519  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3520  }
3521  }
3522 
3523  // Delete main record
3524  if (!$error) {
3525  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3526  $res = $this->db->query($sql);
3527  if (!$res) {
3528  $error++;
3529  $this->error = $this->db->lasterror();
3530  $this->errors[] = $this->error;
3531  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3532  }
3533  }
3534 
3535  // Delete record into ECM index and physically
3536  if (!$error) {
3537  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3538  if (!$res) {
3539  $error++;
3540  }
3541  }
3542 
3543  if (!$error) {
3544  // We remove directory
3545  $ref = dol_sanitizeFileName($this->ref);
3546  if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3547  $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3548  $file = $dir."/".$ref.".pdf";
3549  if (file_exists($file)) {
3550  dol_delete_preview($this);
3551 
3552  if (!dol_delete_file($file, 0, 0, 0, $this)) {
3553  $this->error = 'ErrorFailToDeleteFile';
3554  $this->errors[] = $this->error;
3555  $this->db->rollback();
3556  return 0;
3557  }
3558  }
3559  if (file_exists($dir)) {
3560  $res = @dol_delete_dir_recursive($dir);
3561  if (!$res) {
3562  $this->error = 'ErrorFailToDeleteDir';
3563  $this->errors[] = $this->error;
3564  $this->db->rollback();
3565  return 0;
3566  }
3567  }
3568  }
3569  }
3570 
3571  if (!$error) {
3572  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3573  $this->db->commit();
3574  return 1;
3575  } else {
3576  $this->db->rollback();
3577  return -1;
3578  }
3579  }
3580 
3581 
3582  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3589  public function load_board($user)
3590  {
3591  // phpcs:enable
3592  global $conf, $langs;
3593 
3594  $clause = " WHERE";
3595 
3596  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3597  $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3598  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3599  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3600  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3601  $clause = " AND";
3602  }
3603  $sql .= $clause." c.entity IN (".getEntity('commande').")";
3604  //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3605  $sql .= " AND ((c.fk_statut IN (".self::STATUS_VALIDATED.",".self::STATUS_SHIPMENTONPROCESS.")) OR (c.fk_statut = ".self::STATUS_CLOSED." AND c.facture = 0))"; // If status is 2 and facture=1, it must be selected
3606  if ($user->socid) {
3607  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3608  }
3609 
3610  $resql = $this->db->query($sql);
3611  if ($resql) {
3612  $response = new WorkboardResponse();
3613  $response->warning_delay = $conf->commande->client->warning_delay / 60 / 60 / 24;
3614  $response->label = $langs->trans("OrdersToProcess");
3615  $response->labelShort = $langs->trans("Opened");
3616  $response->url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&mainmenu=commercial&leftmenu=orders';
3617  $response->img = img_object('', "order");
3618 
3619  $generic_commande = new Commande($this->db);
3620 
3621  while ($obj = $this->db->fetch_object($resql)) {
3622  $response->nbtodo++;
3623  $response->total += $obj->total_ht;
3624 
3625  $generic_commande->statut = $obj->fk_statut;
3626  $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3627  $generic_commande->date = $this->db->jdate($obj->date_commande);
3628  $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3629  $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3630 
3631  if ($generic_commande->hasDelay()) {
3632  $response->nbtodolate++;
3633  }
3634  }
3635 
3636  return $response;
3637  } else {
3638  $this->error = $this->db->error();
3639  return -1;
3640  }
3641  }
3642 
3648  public function getLabelSource()
3649  {
3650  global $langs;
3651 
3652  $label = $langs->trans('OrderSource'.$this->source);
3653 
3654  if ($label == 'OrderSource') {
3655  return '';
3656  }
3657  return $label;
3658  }
3659 
3666  public function getLibStatut($mode)
3667  {
3668  return $this->LibStatut($this->statut, $this->billed, $mode);
3669  }
3670 
3671  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3681  public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3682  {
3683  // phpcs:enable
3684  global $langs, $conf, $hookmanager;
3685 
3686  $billedtext = '';
3687  if (empty($donotshowbilled)) {
3688  $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3689  }
3690 
3691  $labelTooltip = '';
3692 
3693  if ($status == self::STATUS_CANCELED) {
3694  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3695  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3696  $statusType = 'status9';
3697  } elseif ($status == self::STATUS_DRAFT) {
3698  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3699  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3700  $statusType = 'status0';
3701  } elseif ($status == self::STATUS_VALIDATED) {
3702  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3703  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3704  $statusType = 'status1';
3705  } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3706  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3707  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3708  $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3709  if (!empty($this->delivery_date)) {
3710  $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3711  }
3712  $statusType = 'status4';
3713  } elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3714  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderToBill'); // translated into Delivered
3715  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderToBillShort'); // translated into Delivered
3716  $statusType = 'status4';
3717  } elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3718  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderProcessed').$billedtext;
3719  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderProcessedShort').$billedtext;
3720  $statusType = 'status6';
3721  } elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3722  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered');
3723  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort');
3724  $statusType = 'status6';
3725  } else {
3726  $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3727  $labelStatusShort = '';
3728  $statusType = '';
3729  $mode = 0;
3730  }
3731 
3732  $parameters = array(
3733  'status' => $status,
3734  'mode' => $mode,
3735  'billed' => $billed,
3736  'donotshowbilled' => $donotshowbilled
3737  );
3738 
3739  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3740 
3741  if ($reshook > 0) {
3742  return $hookmanager->resPrint;
3743  }
3744 
3745  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3746  }
3747 
3754  public function getTooltipContentArray($params)
3755  {
3756  global $conf, $langs, $user;
3757 
3758  $langs->load('orders');
3759  $datas = [];
3760  $nofetch = !empty($params['nofetch']);
3761 
3762  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3763  return ['optimize' => $langs->trans("Order")];
3764  }
3765 
3766  if ($user->hasRight('commande', 'lire')) {
3767  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3768  if (isset($this->statut)) {
3769  $datas['status'] = ' '.$this->getLibStatut(5);
3770  }
3771  $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3772  if (!$nofetch) {
3773  $langs->load('companies');
3774  if (empty($this->thirdparty)) {
3775  $this->fetch_thirdparty();
3776  }
3777  $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3778  }
3779  $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3780  if (!$nofetch) {
3781  $langs->load('project');
3782  if (empty($this->project)) {
3783  $res = $this->fetch_project();
3784  if ($res > 0) {
3785  $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3786  }
3787  }
3788  }
3789  if (!empty($this->total_ht)) {
3790  $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3791  }
3792  if (!empty($this->total_tva)) {
3793  $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3794  }
3795  if (!empty($this->total_ttc)) {
3796  $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3797  }
3798  if (!empty($this->date)) {
3799  $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3800  }
3801  if (!empty($this->delivery_date)) {
3802  $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3803  }
3804  }
3805 
3806  return $datas;
3807  }
3808 
3822  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3823  {
3824  global $conf, $langs, $user, $hookmanager;
3825 
3826  if (!empty($conf->dol_no_mouse_hover)) {
3827  $notooltip = 1; // Force disable tooltips
3828  }
3829 
3830  $result = '';
3831 
3832  if (isModEnabled("expedition") && ($option == '1' || $option == '2')) {
3833  $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3834  } else {
3835  $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3836  }
3837 
3838  if (!$user->rights->commande->lire) {
3839  $option = 'nolink';
3840  }
3841 
3842  if ($option !== 'nolink') {
3843  // Add param to save lastsearch_values or not
3844  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3845  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3846  $add_save_lastsearch_values = 1;
3847  }
3848  if ($add_save_lastsearch_values) {
3849  $url .= '&save_lastsearch_values=1';
3850  }
3851  }
3852 
3853  if ($short) {
3854  return $url;
3855  }
3856  $params = [
3857  'id' => $this->id,
3858  'objecttype' => $this->element,
3859  'option' => $option,
3860  'nofetch' => 1,
3861  ];
3862  $classfortooltip = 'classfortooltip';
3863  $dataparams = '';
3864  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3865  $classfortooltip = 'classforajaxtooltip';
3866  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3867  $label = '';
3868  } else {
3869  $label = implode($this->getTooltipContentArray($params));
3870  }
3871 
3872  $linkclose = '';
3873  if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3874  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3875  $label = $langs->trans("Order");
3876  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3877  }
3878  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3879  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3880 
3881  $target_value = array('_self', '_blank', '_parent', '_top');
3882  if (in_array($target, $target_value)) {
3883  $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3884  }
3885  }
3886 
3887  $linkstart = '<a href="'.$url.'"';
3888  $linkstart .= $linkclose.'>';
3889  $linkend = '</a>';
3890 
3891  if ($option === 'nolink') {
3892  $linkstart = '';
3893  $linkend = '';
3894  }
3895 
3896  $result .= $linkstart;
3897  if ($withpicto) {
3898  $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3899  }
3900  if ($withpicto != 2) {
3901  $result .= $this->ref;
3902  }
3903  $result .= $linkend;
3904 
3905  if ($addlinktonotes) {
3906  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3907  if ($txttoshow) {
3908  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3909  $result .= ' <span class="note inline-block">';
3910  $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3911  $result .= img_picto('', 'note');
3912  $result .= '</a>';
3913  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3914  //$result.='</a>';
3915  $result .= '</span>';
3916  }
3917  }
3918 
3919  global $action;
3920  $hookmanager->initHooks(array($this->element . 'dao'));
3921  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3922  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3923  if ($reshook > 0) {
3924  $result = $hookmanager->resPrint;
3925  } else {
3926  $result .= $hookmanager->resPrint;
3927  }
3928  return $result;
3929  }
3930 
3931 
3938  public function info($id)
3939  {
3940  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3941  $sql .= ' date_valid as datev,';
3942  $sql .= ' date_cloture as datecloture,';
3943  $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3944  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3945  $sql .= ' WHERE c.rowid = '.((int) $id);
3946  $result = $this->db->query($sql);
3947  if ($result) {
3948  if ($this->db->num_rows($result)) {
3949  $obj = $this->db->fetch_object($result);
3950  $this->id = $obj->rowid;
3951  if ($obj->fk_user_author) {
3952  $this->user_creation_id = $obj->fk_user_author;
3953  }
3954  if ($obj->fk_user_valid) {
3955  $this->user_validation_id = $obj->fk_user_valid;
3956  }
3957  if ($obj->fk_user_cloture) {
3958  $this->user_closing_id = $obj->fk_user_cloture;
3959  }
3960 
3961  $this->date_creation = $this->db->jdate($obj->datec);
3962  $this->date_modification = $this->db->jdate($obj->datem);
3963  $this->date_validation = $this->db->jdate($obj->datev);
3964  $this->date_cloture = $this->db->jdate($obj->datecloture);
3965  }
3966 
3967  $this->db->free($result);
3968  } else {
3969  dol_print_error($this->db);
3970  }
3971  }
3972 
3973 
3981  public function initAsSpecimen()
3982  {
3983  global $conf, $langs;
3984 
3985  dol_syslog(get_class($this)."::initAsSpecimen");
3986 
3987  // Load array of products prodids
3988  $num_prods = 0;
3989  $prodids = array();
3990  $sql = "SELECT rowid";
3991  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3992  $sql .= " WHERE entity IN (".getEntity('product').")";
3993  $sql .= $this->db->plimit(100);
3994 
3995  $resql = $this->db->query($sql);
3996  if ($resql) {
3997  $num_prods = $this->db->num_rows($resql);
3998  $i = 0;
3999  while ($i < $num_prods) {
4000  $i++;
4001  $row = $this->db->fetch_row($resql);
4002  $prodids[$i] = $row[0];
4003  }
4004  }
4005 
4006  // Initialise parametres
4007  $this->id = 0;
4008  $this->ref = 'SPECIMEN';
4009  $this->specimen = 1;
4010  $this->entity = $conf->entity;
4011  $this->socid = 1;
4012  $this->date = time();
4013  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4014  $this->cond_reglement_code = 'RECEP';
4015  $this->mode_reglement_code = 'CHQ';
4016  $this->availability_code = 'DSP';
4017  $this->demand_reason_code = 'SRC_00';
4018 
4019  $this->note_public = 'This is a comment (public)';
4020  $this->note_private = 'This is a comment (private)';
4021 
4022  $this->multicurrency_tx = 1;
4023  $this->multicurrency_code = $conf->currency;
4024 
4025  // Lines
4026  $nbp = 5;
4027  $xnbp = 0;
4028  while ($xnbp < $nbp) {
4029  $line = new OrderLine($this->db);
4030 
4031  $line->desc = $langs->trans("Description")." ".$xnbp;
4032  $line->qty = 1;
4033  $line->subprice = 100;
4034  $line->price = 100;
4035  $line->tva_tx = 20;
4036  if ($xnbp == 2) {
4037  $line->total_ht = 50;
4038  $line->total_ttc = 60;
4039  $line->total_tva = 10;
4040  $line->remise_percent = 50;
4041  } else {
4042  $line->total_ht = 100;
4043  $line->total_ttc = 120;
4044  $line->total_tva = 20;
4045  $line->remise_percent = 0;
4046  }
4047  if ($num_prods > 0) {
4048  $prodid = mt_rand(1, $num_prods);
4049  $line->fk_product = $prodids[$prodid];
4050  $line->product_ref = 'SPECIMEN';
4051  }
4052 
4053  $this->lines[$xnbp] = $line;
4054 
4055  $this->total_ht += $line->total_ht;
4056  $this->total_tva += $line->total_tva;
4057  $this->total_ttc += $line->total_ttc;
4058 
4059  $xnbp++;
4060  }
4061  }
4062 
4063 
4064  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4070  public function load_state_board()
4071  {
4072  // phpcs:enable
4073  global $user;
4074 
4075  $this->nb = array();
4076  $clause = "WHERE";
4077 
4078  $sql = "SELECT count(co.rowid) as nb";
4079  $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
4080  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4081  if (empty($user->rights->societe->client->voir) && !$user->socid) {
4082  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4083  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4084  $clause = "AND";
4085  }
4086  $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4087 
4088  $resql = $this->db->query($sql);
4089  if ($resql) {
4090  while ($obj = $this->db->fetch_object($resql)) {
4091  $this->nb["orders"] = $obj->nb;
4092  }
4093  $this->db->free($resql);
4094  return 1;
4095  } else {
4096  dol_print_error($this->db);
4097  $this->error = $this->db->error();
4098  return -1;
4099  }
4100  }
4101 
4107  public function getLinesArray()
4108  {
4109  return $this->fetch_lines();
4110  }
4111 
4123  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4124  {
4125  global $conf, $langs;
4126 
4127  $langs->load("orders");
4128  $outputlangs->load("products");
4129 
4130  if (!dol_strlen($modele)) {
4131  $modele = 'einstein';
4132 
4133  if (!empty($this->model_pdf)) {
4134  $modele = $this->model_pdf;
4135  } elseif (!empty($this->modelpdf)) { // deprecated
4136  $modele = $this->modelpdf;
4137  } elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
4138  $modele = $conf->global->COMMANDE_ADDON_PDF;
4139  }
4140  }
4141 
4142  $modelpath = "core/modules/commande/doc/";
4143 
4144  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4145  }
4146 
4147 
4156  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4157  {
4158  $tables = array(
4159  'commande'
4160  );
4161 
4162  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4163  }
4164 
4173  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4174  {
4175  $tables = array(
4176  'commandedet',
4177  );
4178 
4179  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4180  }
4181 
4187  public function hasDelay()
4188  {
4189  global $conf;
4190 
4191  if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4192  return false; // Never late if not inside this status range
4193  }
4194 
4195  $now = dol_now();
4196 
4197  return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4198  }
4199 
4205  public function showDelay()
4206  {
4207  global $conf, $langs;
4208 
4209  if (empty($this->delivery_date)) {
4210  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
4211  } else {
4212  $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
4213  }
4214  $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4215 
4216  return $text;
4217  }
4218 }
4219 
4220 
4225 {
4229  public $element = 'commandedet';
4230 
4231  public $table_element = 'commandedet';
4232 
4233  public $oldline;
4234 
4239  public $fk_commande;
4240 
4247  public $commande_id;
4248 
4249  public $fk_parent_line;
4250 
4254  public $fk_facture;
4255 
4259  public $ref_ext;
4260 
4261  public $fk_remise_except;
4262 
4266  public $rang = 0;
4267  public $fk_fournprice;
4268 
4273  public $pa_ht;
4274  public $marge_tx;
4275  public $marque_tx;
4276 
4281  public $remise;
4282 
4283  // Start and end date of the line
4284  public $date_start;
4285  public $date_end;
4286 
4287  public $skip_update_total; // Skip update price total for special lines
4288 
4289 
4295  public function __construct($db)
4296  {
4297  $this->db = $db;
4298  }
4299 
4306  public function fetch($rowid)
4307  {
4308  $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,';
4309  $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4310  $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,';
4311  $sql .= ' cd.fk_unit,';
4312  $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4313  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4314  $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4315  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4316  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4317  $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4318  $result = $this->db->query($sql);
4319  if ($result) {
4320  $objp = $this->db->fetch_object($result);
4321 
4322  if (!$objp) {
4323  $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4324  return 0;
4325  }
4326 
4327  $this->rowid = $objp->rowid;
4328  $this->id = $objp->rowid;
4329  $this->fk_commande = $objp->fk_commande;
4330  $this->fk_parent_line = $objp->fk_parent_line;
4331  $this->label = $objp->custom_label;
4332  $this->desc = $objp->description;
4333  $this->qty = $objp->qty;
4334  $this->price = $objp->price;
4335  $this->subprice = $objp->subprice;
4336  $this->ref_ext = $objp->ref_ext;
4337  $this->vat_src_code = $objp->vat_src_code;
4338  $this->tva_tx = $objp->tva_tx;
4339  $this->localtax1_tx = $objp->localtax1_tx;
4340  $this->localtax2_tx = $objp->localtax2_tx;
4341  $this->remise = $objp->remise;
4342  $this->remise_percent = $objp->remise_percent;
4343  $this->fk_remise_except = $objp->fk_remise_except;
4344  $this->fk_product = $objp->fk_product;
4345  $this->product_type = $objp->product_type;
4346  $this->info_bits = $objp->info_bits;
4347  $this->special_code = $objp->special_code;
4348  $this->total_ht = $objp->total_ht;
4349  $this->total_tva = $objp->total_tva;
4350  $this->total_localtax1 = $objp->total_localtax1;
4351  $this->total_localtax2 = $objp->total_localtax2;
4352  $this->total_ttc = $objp->total_ttc;
4353  $this->fk_fournprice = $objp->fk_fournprice;
4354  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4355  $this->pa_ht = $marginInfos[0];
4356  $this->marge_tx = $marginInfos[1];
4357  $this->marque_tx = $marginInfos[2];
4358  $this->special_code = $objp->special_code;
4359  $this->rang = $objp->rang;
4360 
4361  $this->ref = $objp->product_ref; // deprecated
4362 
4363  $this->product_ref = $objp->product_ref;
4364  $this->product_label = $objp->product_label;
4365  $this->product_desc = $objp->product_desc;
4366  $this->product_tobatch = $objp->product_tobatch;
4367  $this->fk_unit = $objp->fk_unit;
4368 
4369  $this->date_start = $this->db->jdate($objp->date_start);
4370  $this->date_end = $this->db->jdate($objp->date_end);
4371 
4372  $this->fk_multicurrency = $objp->fk_multicurrency;
4373  $this->multicurrency_code = $objp->multicurrency_code;
4374  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4375  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4376  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4377  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4378 
4379  $this->db->free($result);
4380 
4381  return 1;
4382  } else {
4383  $this->error = $this->db->lasterror();
4384  return -1;
4385  }
4386  }
4387 
4395  public function delete(User $user, $notrigger = 0)
4396  {
4397  global $conf, $langs;
4398 
4399  $error = 0;
4400 
4401  if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4402  $this->id = $this->rowid;
4403  }
4404 
4405  // check if order line is not in a shipment line before deleting
4406  $sqlCheckShipmentLine = "SELECT";
4407  $sqlCheckShipmentLine .= " ed.rowid";
4408  $sqlCheckShipmentLine .= " FROM " . MAIN_DB_PREFIX . "expeditiondet ed";
4409  $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = " . ((int) $this->id);
4410 
4411  $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4412  if (!$resqlCheckShipmentLine) {
4413  $error++;
4414  $this->error = $this->db->lasterror();
4415  $this->errors[] = $this->error;
4416  } else {
4417  $langs->load('errors');
4418  $num = $this->db->num_rows($resqlCheckShipmentLine);
4419  if ($num > 0) {
4420  $error++;
4421  $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4422  $this->error = $langs->trans('ErrorRecordAlreadyExists') . ' : ' . $langs->trans('ShipmentLine') . ' ' . $objCheckShipmentLine->rowid;
4423  $this->errors[] = $this->error;
4424  }
4425  $this->db->free($resqlCheckShipmentLine);
4426  }
4427  if ($error) {
4428  dol_syslog(__METHOD__ . 'Error ; ' . $this->error, LOG_ERR);
4429  return -1;
4430  }
4431 
4432  $this->db->begin();
4433 
4434  if (!$notrigger) {
4435  // Call trigger
4436  $result = $this->call_trigger('LINEORDER_DELETE', $user);
4437  if ($result < 0) {
4438  $error++;
4439  }
4440  // End call triggers
4441  }
4442 
4443  if (!$error) {
4444  $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . "commandedet WHERE rowid = " . ((int) $this->id);
4445 
4446  dol_syslog("OrderLine::delete", LOG_DEBUG);
4447  $resql = $this->db->query($sql);
4448  if (!$resql) {
4449  $this->error = $this->db->lasterror();
4450  $error++;
4451  }
4452  }
4453 
4454  // Remove extrafields
4455  if (!$error) {
4456  $result = $this->deleteExtraFields();
4457  if ($result < 0) {
4458  $error++;
4459  dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4460  }
4461  }
4462 
4463  if (!$error) {
4464  $this->db->commit();
4465  return 1;
4466  }
4467 
4468  foreach ($this->errors as $errmsg) {
4469  dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
4470  $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
4471  }
4472  $this->db->rollback();
4473  return -1 * $error;
4474  }
4475 
4483  public function insert($user = null, $notrigger = 0)
4484  {
4485  global $langs, $conf;
4486 
4487  $error = 0;
4488 
4489  $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'.
4490 
4491  dol_syslog(get_class($this)."::insert rang=".$this->rang);
4492 
4493  // Clean parameters
4494  if (empty($this->tva_tx)) {
4495  $this->tva_tx = 0;
4496  }
4497  if (empty($this->localtax1_tx)) {
4498  $this->localtax1_tx = 0;
4499  }
4500  if (empty($this->localtax2_tx)) {
4501  $this->localtax2_tx = 0;
4502  }
4503  if (empty($this->localtax1_type)) {
4504  $this->localtax1_type = 0;
4505  }
4506  if (empty($this->localtax2_type)) {
4507  $this->localtax2_type = 0;
4508  }
4509  if (empty($this->total_localtax1)) {
4510  $this->total_localtax1 = 0;
4511  }
4512  if (empty($this->total_localtax2)) {
4513  $this->total_localtax2 = 0;
4514  }
4515  if (empty($this->rang)) {
4516  $this->rang = 0;
4517  }
4518  if (empty($this->remise_percent)) {
4519  $this->remise_percent = 0;
4520  }
4521  if (empty($this->info_bits)) {
4522  $this->info_bits = 0;
4523  }
4524  if (empty($this->special_code)) {
4525  $this->special_code = 0;
4526  }
4527  if (empty($this->fk_parent_line)) {
4528  $this->fk_parent_line = 0;
4529  }
4530  if (empty($this->pa_ht)) {
4531  $this->pa_ht = 0;
4532  }
4533  if (empty($this->ref_ext)) {
4534  $this->ref_ext = '';
4535  }
4536 
4537  // if buy price not defined, define buyprice as configured in margin admin
4538  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4539  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4540  if ($result < 0) {
4541  return $result;
4542  } else {
4543  $this->pa_ht = $result;
4544  }
4545  }
4546 
4547  // Check parameters
4548  if ($this->product_type < 0) {
4549  return -1;
4550  }
4551 
4552  $this->db->begin();
4553 
4554  // Insertion dans base de la ligne
4555  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4556  $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4557  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4558  $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4559  $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4560  $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4561  $sql .= ' fk_unit';
4562  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4563  $sql .= ')';
4564  $sql .= " VALUES (".$this->fk_commande.",";
4565  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4566  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4567  $sql .= " '".$this->db->escape($this->desc)."',";
4568  $sql .= " '".price2num($this->qty)."',";
4569  $sql .= " '".$this->db->escape($this->ref_ext)."',";
4570  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4571  $sql .= " '".price2num($this->tva_tx)."',";
4572  $sql .= " '".price2num($this->localtax1_tx)."',";
4573  $sql .= " '".price2num($this->localtax2_tx)."',";
4574  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4575  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4576  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4577  $sql .= " '".$this->db->escape($this->product_type)."',";
4578  $sql .= " '".price2num($this->remise_percent)."',";
4579  $sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4580  $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4581  $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4582  $sql .= ' '.((int) $this->special_code).',';
4583  $sql .= ' '.((int) $this->rang).',';
4584  $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4585  $sql .= ' '.price2num($this->pa_ht).',';
4586  $sql .= " ".((int) $this->info_bits).",";
4587  $sql .= " ".price2num($this->total_ht, 'MT').",";
4588  $sql .= " ".price2num($this->total_tva, 'MT').",";
4589  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4590  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4591  $sql .= " ".price2num($this->total_ttc, 'MT').",";
4592  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4593  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4594  $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4595  $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4596  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4597  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4598  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4599  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4600  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4601  $sql .= ')';
4602 
4603  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4604  $resql = $this->db->query($sql);
4605  if ($resql) {
4606  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4607  $this->rowid = $this->id;
4608 
4609  if (!$error) {
4610  $result = $this->insertExtraFields();
4611  if ($result < 0) {
4612  $error++;
4613  }
4614  }
4615 
4616  if (!$error && !$notrigger) {
4617  // Call trigger
4618  $result = $this->call_trigger('LINEORDER_INSERT', $user);
4619  if ($result < 0) {
4620  $error++;
4621  }
4622  // End call triggers
4623  }
4624 
4625  if (!$error) {
4626  $this->db->commit();
4627  return 1;
4628  }
4629 
4630  foreach ($this->errors as $errmsg) {
4631  dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4632  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4633  }
4634  $this->db->rollback();
4635  return -1 * $error;
4636  } else {
4637  $this->error = $this->db->error();
4638  $this->db->rollback();
4639  return -2;
4640  }
4641  }
4642 
4650  public function update(User $user, $notrigger = 0)
4651  {
4652  global $conf, $langs;
4653 
4654  $error = 0;
4655 
4656  $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'.
4657 
4658  // Clean parameters
4659  if (empty($this->tva_tx)) {
4660  $this->tva_tx = 0;
4661  }
4662  if (empty($this->localtax1_tx)) {
4663  $this->localtax1_tx = 0;
4664  }
4665  if (empty($this->localtax2_tx)) {
4666  $this->localtax2_tx = 0;
4667  }
4668  if (empty($this->localtax1_type)) {
4669  $this->localtax1_type = 0;
4670  }
4671  if (empty($this->localtax2_type)) {
4672  $this->localtax2_type = 0;
4673  }
4674  if (empty($this->qty)) {
4675  $this->qty = 0;
4676  }
4677  if (empty($this->total_localtax1)) {
4678  $this->total_localtax1 = 0;
4679  }
4680  if (empty($this->total_localtax2)) {
4681  $this->total_localtax2 = 0;
4682  }
4683  if (empty($this->marque_tx)) {
4684  $this->marque_tx = 0;
4685  }
4686  if (empty($this->marge_tx)) {
4687  $this->marge_tx = 0;
4688  }
4689  if (empty($this->remise_percent)) {
4690  $this->remise_percent = 0;
4691  }
4692  if (empty($this->remise)) {
4693  $this->remise = 0;
4694  }
4695  if (empty($this->info_bits)) {
4696  $this->info_bits = 0;
4697  }
4698  if (empty($this->special_code)) {
4699  $this->special_code = 0;
4700  }
4701  if (empty($this->product_type)) {
4702  $this->product_type = 0;
4703  }
4704  if (empty($this->fk_parent_line)) {
4705  $this->fk_parent_line = 0;
4706  }
4707  if (empty($this->pa_ht)) {
4708  $this->pa_ht = 0;
4709  }
4710  if (empty($this->ref_ext)) {
4711  $this->ref_ext = '';
4712  }
4713 
4714  // if buy price not defined, define buyprice as configured in margin admin
4715  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4716  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4717  if ($result < 0) {
4718  return $result;
4719  } else {
4720  $this->pa_ht = $result;
4721  }
4722  }
4723 
4724  $this->db->begin();
4725 
4726  // Mise a jour ligne en base
4727  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4728  $sql .= " description='".$this->db->escape($this->desc)."'";
4729  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4730  $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4731  $sql .= " , tva_tx=".price2num($this->tva_tx);
4732  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4733  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4734  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4735  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4736  $sql .= " , qty=".price2num($this->qty);
4737  $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4738  $sql .= " , subprice=".price2num($this->subprice);
4739  $sql .= " , remise_percent=".price2num($this->remise_percent);
4740  $sql .= " , price=".price2num($this->price); // TODO A virer
4741  $sql .= " , remise=".price2num($this->remise); // TODO A virer
4742  if (empty($this->skip_update_total)) {
4743  $sql .= " , total_ht=".price2num($this->total_ht);
4744  $sql .= " , total_tva=".price2num($this->total_tva);
4745  $sql .= " , total_ttc=".price2num($this->total_ttc);
4746  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4747  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4748  }
4749  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4750  $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4751  $sql .= " , info_bits=".((int) $this->info_bits);
4752  $sql .= " , special_code=".((int) $this->special_code);
4753  $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4754  $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4755  $sql .= " , product_type=".$this->product_type;
4756  $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4757  if (!empty($this->rang)) {
4758  $sql .= ", rang=".((int) $this->rang);
4759  }
4760  $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4761 
4762  // Multicurrency
4763  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4764  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4765  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4766  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4767 
4768  $sql .= " WHERE rowid = ".((int) $this->rowid);
4769 
4770  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4771  $resql = $this->db->query($sql);
4772  if ($resql) {
4773  if (!$error) {
4774  $this->id = $this->rowid;
4775  $result = $this->insertExtraFields();
4776  if ($result < 0) {
4777  $error++;
4778  }
4779  }
4780 
4781  if (!$error && !$notrigger) {
4782  // Call trigger
4783  $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4784  if ($result < 0) {
4785  $error++;
4786  }
4787  // End call triggers
4788  }
4789 
4790  if (!$error) {
4791  $this->db->commit();
4792  return 1;
4793  }
4794 
4795  foreach ($this->errors as $errmsg) {
4796  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4797  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4798  }
4799  $this->db->rollback();
4800  return -1 * $error;
4801  } else {
4802  $this->error = $this->db->error();
4803  $this->db->rollback();
4804  return -2;
4805  }
4806  }
4807 
4808  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4815  public function update_total()
4816  {
4817  // phpcs:enable
4818  $this->db->begin();
4819 
4820  // Clean parameters
4821  if (empty($this->total_localtax1)) {
4822  $this->total_localtax1 = 0;
4823  }
4824  if (empty($this->total_localtax2)) {
4825  $this->total_localtax2 = 0;
4826  }
4827 
4828  // Mise a jour ligne en base
4829  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4830  $sql .= " total_ht='".price2num($this->total_ht)."'";
4831  $sql .= ",total_tva='".price2num($this->total_tva)."'";
4832  $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4833  $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4834  $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4835  $sql .= " WHERE rowid = ".((int) $this->rowid);
4836 
4837  dol_syslog("OrderLine::update_total", LOG_DEBUG);
4838 
4839  $resql = $this->db->query($sql);
4840  if ($resql) {
4841  $this->db->commit();
4842  return 1;
4843  } else {
4844  $this->error = $this->db->error();
4845  $this->db->rollback();
4846  return -2;
4847  }
4848  }
4849 }
$object ref
Definition: info.php:78
Class to manage customers orders.
getNbOfServicesLines()
Return number of line with type service.
getNbOfShipments()
Count number of shipments for this order.
deleteline($user=null, $lineid=0, $id=0)
Delete an order line.
createFromProposal($object, User $user)
Load an object from a proposal and create a new order into database.
setDraft($user, $idwarehouse=-1)
Set draft status.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
getLinesArray()
Create an array of order lines.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
showDelay()
Show the customer delayed info.
set_date($user, $date, $notrigger=0)
Set the order date.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetch_lines($only_product=0, $loadalsotranslation=0)
Load array lines.
$pos_source
key of pos source ('0', '1', ...)
createFromClone(User $user, $socid=0)
Load an object from its id and create a new one in database.
const STATUS_SHIPMENTONPROCESS
Shipment on process.
LibStatut($status, $billed, $mode, $donotshowbilled=0)
Return label of status.
liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
Return list of orders (eventuelly filtered on a user) into an array.
getLibStatut($mode)
Return status label of Order.
hasDelay()
Is the sales order delayed?
set_remise_absolue($user, $remise, $notrigger=0)
Set a fixed amount discount.
const STATUS_CLOSED
Closed (Sent, billed or not)
updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $ref_ext='', $rang=0)
Update a line in database.
valid($user, $idwarehouse=0, $notrigger=0)
Validate order.
getLabelSource()
Return source label of order.
set_remise($user, $remise, $notrigger=0)
Applique une remise relative.
const STATUS_CANCELED
Canceled status.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0, $target='')
Return clicable link of object (with eventually picto)
loadExpeditions($filtre_statut=-1, $fk_product=0)
Load array this->expeditions of lines of shipments with nb of products sent for each order line Note:...
__construct($db)
Constructor.
availability($availability_id, $notrigger=0)
Update delivery delay.
set_reopen($user)
Tag the order as validated (opened) Function used when order is reopend after being closed.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
set_ref_client($user, $ref_client, $notrigger=0)
Set customer ref.
getNextNumRef($soc)
Returns the reference to the following non used Order depending on the active numbering module define...
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)
Adding line of fixed discount in the order in DB.
getNbOfProductsLines()
Return number of line with type product.
update(User $user, $notrigger=0)
Update database.
$module_source
key of module source when order generated from a dedicated module ('cashdesk', 'takepos',...
classifyUnBilled(User $user, $notrigger=0)
Classify the order as not invoiced.
setDiscount($user, $remise, $notrigger=0)
Set a percentage discount.
stock_array($filtre_statut=self::STATUS_CANCELED)
Return a array with the pending stock by product.
cloture($user, $notrigger=0)
Close order.
cancel($idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
classifyBilled(User $user, $notrigger=0)
Classify the order as invoiced.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $info_bits=0, $fk_remise_except=0, $price_base_type='HT', $pu_ttc=0, $date_start='', $date_end='', $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0, $ref_ext='', $noupdateafterinsertline=0)
Add an order line into database (linked to product/service or not)
info($id)
Charge les informations d'ordre info dans l'objet commande.
const STATUS_VALIDATED
Validated status.
countNbOfShipments()
Returns an array with expeditions lines number.
fetch($id, $ref='', $ref_ext='', $notused='')
Get object from database.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
Add line into array $this->client must be loaded.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
fetch_project()
Load the project with id $this->fk_project into this->project.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
deleteExtraFields()
Delete all extra fields values for the current object.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Superclass for orders classes.
Superclass for orders classes.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class to manage shipments.
Class to manage standard extra fields.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage order lines.
insert($user=null, $notrigger=0)
Insert line into database.
update(User $user, $notrigger=0)
Update the line object into db.
update_total()
Update DB line fields total_xxx Used by migration.
__construct($db)
Constructor.
fetch($rowid)
Load line order.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
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:1507
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:1356
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1559
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
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.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
isModEnabled($module)
Is Dolibarr module enabled.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
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($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86