dolibarr  19.0.0-dev
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 
282  public $pos_source;
283 
287  public $expeditions;
288 
292  public $online_payment_url;
293 
294 
319  // BEGIN MODULEBUILDER PROPERTIES
323  public $fields = array(
324  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
325  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
326  'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25),
327  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>26),
328  'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>28),
329  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>-1, 'notnull'=>1, 'position'=>20),
330  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>25),
331  'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>1, 'position'=>60),
332  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
333  'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
334  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
335  'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
336  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
337  //'amount_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
338  //'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
339  'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
340  //'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
341  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
342  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
343  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
344  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
345  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
346  'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>150),
347  'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155),
348  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>160),
349  //'facture' =>array('type'=>'tinyint(4)', 'label'=>'ParentInvoice', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
350  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>-1, 'position'=>170),
351  'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
352  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
353  'deposit_percent' =>array('type'=>'varchar(63)', 'label'=>'DepositPercent', 'enabled'=>1, 'visible'=>-1, 'position'=>181),
354  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
355  'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
356  'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
357  'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>'isModEnabled("stock")', 'visible'=>-1, 'position'=>200),
358  'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
359  'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
360  //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
361  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
362  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>230),
363  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>235),
364  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
365  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>245),
366  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
367  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
368  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>260, 'isameasure'=>1),
369  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>265, 'isameasure'=>1),
370  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>270),
371  'module_source' =>array('type'=>'varchar(32)', 'label'=>'POSModule', 'enabled'=>1, 'visible'=>-1, 'position'=>275),
372  'pos_source' =>array('type'=>'varchar(32)', 'label'=>'POSTerminal', 'enabled'=>1, 'visible'=>-1, 'position'=>280),
373  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>300),
374  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>302),
375  'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>304),
376  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>306),
377  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
378  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
379  );
380  // END MODULEBUILDER PROPERTIES
381 
386 
390  const STATUS_CANCELED = -1;
394  const STATUS_DRAFT = 0;
398  const STATUS_VALIDATED = 1;
403  const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
404 
408  const STATUS_CLOSED = 3;
409 
410 
416  public function __construct($db)
417  {
418  $this->db = $db;
419  }
420 
428  public function getNextNumRef($soc)
429  {
430  global $langs, $conf;
431  $langs->load("order");
432 
433  if (!empty($conf->global->COMMANDE_ADDON)) {
434  $mybool = false;
435 
436  $file = $conf->global->COMMANDE_ADDON.".php";
437  $classname = $conf->global->COMMANDE_ADDON;
438 
439  // Include file with class
440  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
441  foreach ($dirmodels as $reldir) {
442  $dir = dol_buildpath($reldir."core/modules/commande/");
443 
444  // Load file with numbering class (if found)
445  $mybool |= @include_once $dir.$file;
446  }
447 
448  if ($mybool === false) {
449  dol_print_error('', "Failed to include file ".$file);
450  return '';
451  }
452 
453  $obj = new $classname();
454  $numref = $obj->getNextValue($soc, $this);
455 
456  if ($numref != "") {
457  return $numref;
458  } else {
459  $this->error = $obj->error;
460  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
461  return "";
462  }
463  } else {
464  print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
465  return "";
466  }
467  }
468 
469 
478  public function valid($user, $idwarehouse = 0, $notrigger = 0)
479  {
480  global $conf, $langs;
481 
482  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
483 
484  $error = 0;
485 
486  // Protection
487  if ($this->statut == self::STATUS_VALIDATED) {
488  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
489  return 0;
490  }
491 
492  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
493  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
494  $this->error = 'NotEnoughPermissions';
495  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
496  return -1;
497  }
498 
499  $now = dol_now();
500 
501  $this->db->begin();
502 
503  // Definition du nom de module de numerotation de commande
504  $soc = new Societe($this->db);
505  $soc->fetch($this->socid);
506 
507  // Class of company linked to order
508  $result = $soc->set_as_client();
509 
510  // Define new ref
511  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
512  $num = $this->getNextNumRef($soc);
513  } else {
514  $num = $this->ref;
515  }
516  $this->newref = dol_sanitizeFileName($num);
517 
518  // Validate
519  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
520  $sql .= " SET ref = '".$this->db->escape($num)."',";
521  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
522  $sql .= " date_valid='".$this->db->idate($now)."',";
523  $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
524  $sql .= " fk_user_modif = ".((int) $user->id);
525  $sql .= " WHERE rowid = ".((int) $this->id);
526 
527  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
528  $resql = $this->db->query($sql);
529  if (!$resql) {
530  dol_print_error($this->db);
531  $this->error = $this->db->lasterror();
532  $error++;
533  }
534 
535  if (!$error) {
536  // If stock is incremented on validate order, we must increment it
537  if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
538  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
539  $langs->load("agenda");
540 
541  // Loop on each line
542  $cpt = count($this->lines);
543  for ($i = 0; $i < $cpt; $i++) {
544  if ($this->lines[$i]->fk_product > 0) {
545  $mouvP = new MouvementStock($this->db);
546  $mouvP->origin = &$this;
547  $mouvP->setOrigin($this->element, $this->id);
548  // We decrement stock of product (and sub-products)
549  $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
550  if ($result < 0) {
551  $error++;
552  $this->error = $mouvP->error;
553  }
554  }
555  if ($error) {
556  break;
557  }
558  }
559  }
560  }
561 
562  if (!$error && !$notrigger) {
563  // Call trigger
564  $result = $this->call_trigger('ORDER_VALIDATE', $user);
565  if ($result < 0) {
566  $error++;
567  }
568  // End call triggers
569  }
570 
571  if (!$error) {
572  $this->oldref = $this->ref;
573 
574  // Rename directory if dir was a temporary ref
575  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
576  // Now we rename also files into index
577  $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)."'";
578  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
579  $resql = $this->db->query($sql);
580  if (!$resql) {
581  $error++; $this->error = $this->db->lasterror();
582  }
583 
584  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
585  $oldref = dol_sanitizeFileName($this->ref);
586  $newref = dol_sanitizeFileName($num);
587  $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
588  $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
589  if (!$error && file_exists($dirsource)) {
590  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
591 
592  if (@rename($dirsource, $dirdest)) {
593  dol_syslog("Rename ok");
594  // Rename docs starting with $oldref with $newref
595  $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
596  foreach ($listoffiles as $fileentry) {
597  $dirsource = $fileentry['name'];
598  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
599  $dirsource = $fileentry['path'].'/'.$dirsource;
600  $dirdest = $fileentry['path'].'/'.$dirdest;
601  @rename($dirsource, $dirdest);
602  }
603  }
604  }
605  }
606  }
607 
608  // Set new ref and current status
609  if (!$error) {
610  $this->ref = $num;
611  $this->statut = self::STATUS_VALIDATED;
612  $this->brouillon = 0;
613  }
614 
615  if (!$error) {
616  $this->db->commit();
617  return 1;
618  } else {
619  $this->db->rollback();
620  return -1;
621  }
622  }
623 
624  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
632  public function setDraft($user, $idwarehouse = -1)
633  {
634  //phpcs:enable
635  global $conf, $langs;
636 
637  $error = 0;
638 
639  // Protection
640  if ($this->statut <= self::STATUS_DRAFT) {
641  return 0;
642  }
643 
644  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
645  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
646  $this->error = 'Permission denied';
647  return -1;
648  }
649 
650  dol_syslog(__METHOD__, LOG_DEBUG);
651 
652  $this->db->begin();
653 
654  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
655  $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
656  $sql .= " fk_user_modif = ".((int) $user->id);
657  $sql .= " WHERE rowid = ".((int) $this->id);
658 
659  if ($this->db->query($sql)) {
660  if (!$error) {
661  $this->oldcopy = clone $this;
662  }
663 
664  // If stock is decremented on validate order, we must reincrement it
665  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
666  $result = 0;
667 
668  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
669  $langs->load("agenda");
670 
671  $num = count($this->lines);
672  for ($i = 0; $i < $num; $i++) {
673  if ($this->lines[$i]->fk_product > 0) {
674  $mouvP = new MouvementStock($this->db);
675  $mouvP->origin = &$this;
676  $mouvP->setOrigin($this->element, $this->id);
677  // We increment stock of product (and sub-products)
678  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
679  if ($result < 0) {
680  $error++; $this->error = $mouvP->error; break;
681  }
682  }
683  }
684  }
685 
686  if (!$error) {
687  // Call trigger
688  $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
689  if ($result < 0) {
690  $error++;
691  }
692  }
693 
694  if (!$error) {
695  $this->statut = self::STATUS_DRAFT;
696  $this->db->commit();
697  return 1;
698  } else {
699  $this->db->rollback();
700  return -1;
701  }
702  } else {
703  $this->error = $this->db->error();
704  $this->db->rollback();
705  return -1;
706  }
707  }
708 
709 
710  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
718  public function set_reopen($user)
719  {
720  // phpcs:enable
721  $error = 0;
722 
723  if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
724  dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
725  return 0;
726  }
727 
728  $this->db->begin();
729 
730  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
731  $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
732  $sql .= " fk_user_modif = ".((int) $user->id);
733  $sql .= " WHERE rowid = ".((int) $this->id);
734 
735  dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
736  $resql = $this->db->query($sql);
737  if ($resql) {
738  // Call trigger
739  $result = $this->call_trigger('ORDER_REOPEN', $user);
740  if ($result < 0) {
741  $error++;
742  }
743  // End call triggers
744  } else {
745  $error++;
746  $this->error = $this->db->lasterror();
747  dol_print_error($this->db);
748  }
749 
750  if (!$error) {
751  $this->statut = self::STATUS_VALIDATED;
752  $this->billed = 0;
753 
754  $this->db->commit();
755  return 1;
756  } else {
757  foreach ($this->errors as $errmsg) {
758  dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
759  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
760  }
761  $this->db->rollback();
762  return -1 * $error;
763  }
764  }
765 
773  public function cloture($user, $notrigger = 0)
774  {
775  global $conf;
776 
777  $error = 0;
778 
779  $usercanclose = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
780  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->close)));
781 
782  if ($usercanclose) {
783  if ($this->statut == self::STATUS_CLOSED) {
784  return 0;
785  }
786  $this->db->begin();
787 
788  $now = dol_now();
789 
790  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
791  $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
792  $sql .= ' fk_user_cloture = '.((int) $user->id).',';
793  $sql .= " date_cloture = '".$this->db->idate($now)."',";
794  $sql .= " fk_user_modif = ".((int) $user->id);
795  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
796 
797  if ($this->db->query($sql)) {
798  if (!$notrigger) {
799  // Call trigger
800  $result = $this->call_trigger('ORDER_CLOSE', $user);
801  if ($result < 0) {
802  $error++;
803  }
804  // End call triggers
805  }
806 
807  if (!$error) {
808  $this->statut = self::STATUS_CLOSED;
809 
810  $this->db->commit();
811  return 1;
812  } else {
813  $this->db->rollback();
814  return -1;
815  }
816  } else {
817  $this->error = $this->db->lasterror();
818 
819  $this->db->rollback();
820  return -1;
821  }
822  }
823  return 0;
824  }
825 
833  public function cancel($idwarehouse = -1)
834  {
835  global $conf, $user, $langs;
836 
837  $error = 0;
838 
839  $this->db->begin();
840 
841  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
842  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
843  $sql .= " fk_user_modif = ".((int) $user->id);
844  $sql .= " WHERE rowid = ".((int) $this->id);
845  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
846 
847  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
848  if ($this->db->query($sql)) {
849  // If stock is decremented on validate order, we must reincrement it
850  if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
851  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
852  $langs->load("agenda");
853 
854  $num = count($this->lines);
855  for ($i = 0; $i < $num; $i++) {
856  if ($this->lines[$i]->fk_product > 0) {
857  $mouvP = new MouvementStock($this->db);
858  $mouvP->setOrigin($this->element, $this->id);
859  // We increment stock of product (and sub-products)
860  $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
861  if ($result < 0) {
862  $error++;
863  $this->error = $mouvP->error;
864  break;
865  }
866  }
867  }
868  }
869 
870  if (!$error) {
871  // Call trigger
872  $result = $this->call_trigger('ORDER_CANCEL', $user);
873  if ($result < 0) {
874  $error++;
875  }
876  // End call triggers
877  }
878 
879  if (!$error) {
880  $this->statut = self::STATUS_CANCELED;
881  $this->db->commit();
882  return 1;
883  } else {
884  foreach ($this->errors as $errmsg) {
885  dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
886  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
887  }
888  $this->db->rollback();
889  return -1 * $error;
890  }
891  } else {
892  $this->error = $this->db->error();
893  $this->db->rollback();
894  return -1;
895  }
896  }
897 
906  public function create($user, $notrigger = 0)
907  {
908  global $conf, $langs, $mysoc;
909  $error = 0;
910 
911  // Clean parameters
912  $this->brouillon = 1; // set command as draft
913 
914  // Set tmp vars
915  $date = ($this->date_commande ? $this->date_commande : $this->date);
916  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
917 
918  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
919  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
920  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
921  } else {
922  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
923  }
924  if (empty($this->fk_multicurrency)) {
925  $this->multicurrency_code = $conf->currency;
926  $this->fk_multicurrency = 0;
927  $this->multicurrency_tx = 1;
928  }
929 
930  dol_syslog(get_class($this)."::create user=".$user->id);
931 
932  // Check parameters
933  if (!empty($this->ref)) { // We check that ref is not already used
934  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
935  if ($result > 0) {
936  $this->error = 'ErrorRefAlreadyExists';
937  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
938  $this->db->rollback();
939  return -1;
940  }
941  }
942 
943  $soc = new Societe($this->db);
944  $result = $soc->fetch($this->socid);
945  if ($result < 0) {
946  $this->error = "Failed to fetch company";
947  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
948  return -2;
949  }
950  if (!empty($conf->global->ORDER_REQUIRE_SOURCE) && $this->source < 0) {
951  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
952  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
953  return -1;
954  }
955 
956  $now = dol_now();
957 
958  $this->db->begin();
959 
960  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
961  $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
962  $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
963  $sql .= ", fk_shipping_method";
964  $sql .= ", fk_warehouse";
965  $sql .= ", remise_absolue, remise_percent";
966  $sql .= ", fk_incoterms, location_incoterms";
967  $sql .= ", entity, module_source, pos_source";
968  $sql .= ", fk_multicurrency";
969  $sql .= ", multicurrency_code";
970  $sql .= ", multicurrency_tx";
971  $sql .= ")";
972  $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
973  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
974  $sql .= ", '".$this->db->idate($date)."'";
975  $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
976  $sql .= ", '".$this->db->escape($this->note_private)."'";
977  $sql .= ", '".$this->db->escape($this->note_public)."'";
978  $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
979  $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
980  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
981  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
982  $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
983  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
984  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
985  $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
986  $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
987  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
988  $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
989  $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
990  $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
991  $sql .= ", ".($this->remise_absolue > 0 ? $this->db->escape($this->remise_absolue) : 'NULL');
992  $sql .= ", ".($this->remise_percent > 0 ? $this->db->escape($this->remise_percent) : 0); // TODO deprecated
993  $sql .= ", ".(int) $this->fk_incoterms;
994  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
995  $sql .= ", ".setEntity($this);
996  $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
997  $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
998  $sql .= ", ".(int) $this->fk_multicurrency;
999  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1000  $sql .= ", ".(float) $this->multicurrency_tx;
1001  $sql .= ")";
1002 
1003  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1004  $resql = $this->db->query($sql);
1005  if ($resql) {
1006  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1007 
1008  if ($this->id) {
1009  $fk_parent_line = 0;
1010  $num = count($this->lines);
1011 
1012  /*
1013  * Insert products details into db
1014  */
1015  for ($i = 0; $i < $num; $i++) {
1016  $line = $this->lines[$i];
1017 
1018  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1019  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1020  if (!is_object($line)) {
1021  $line = (object) $line;
1022  }
1023 
1024  // Reset fk_parent_line for no child products and special product
1025  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1026  $fk_parent_line = 0;
1027  }
1028 
1029  // Complete vat rate with code
1030  $vatrate = $line->tva_tx;
1031  if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1032  $vatrate .= ' ('.$line->vat_src_code.')';
1033  }
1034 
1035  if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1036  $originid = $line->origin_id;
1037  $origintype = $line->origin;
1038  } else {
1039  $originid = $line->id;
1040  $origintype = $this->element;
1041  }
1042 
1043  // ref_ext
1044  if (empty($line->ref_ext)) {
1045  $line->ref_ext = '';
1046  }
1047 
1048  $result = $this->addline(
1049  $line->desc,
1050  $line->subprice,
1051  $line->qty,
1052  $vatrate,
1053  $line->localtax1_tx,
1054  $line->localtax2_tx,
1055  $line->fk_product,
1056  $line->remise_percent,
1057  $line->info_bits,
1058  $line->fk_remise_except,
1059  'HT',
1060  0,
1061  $line->date_start,
1062  $line->date_end,
1063  $line->product_type,
1064  $line->rang,
1065  $line->special_code,
1066  $fk_parent_line,
1067  $line->fk_fournprice,
1068  $line->pa_ht,
1069  $line->label,
1070  $line->array_options,
1071  $line->fk_unit,
1072  $origintype,
1073  $originid,
1074  0,
1075  $line->ref_ext,
1076  1
1077  );
1078  if ($result < 0) {
1079  if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1080  $this->error = $this->db->lasterror();
1081  $this->errors[] = $this->error;
1082  dol_print_error($this->db);
1083  }
1084  $this->db->rollback();
1085  return -1;
1086  }
1087  // Defined the new fk_parent_line
1088  if ($result > 0 && $line->product_type == 9) {
1089  $fk_parent_line = $result;
1090  }
1091  }
1092 
1093  $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.
1094 
1095  // update ref
1096  $initialref = '(PROV'.$this->id.')';
1097  if (!empty($this->ref)) {
1098  $initialref = $this->ref;
1099  }
1100 
1101  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1102  if ($this->db->query($sql)) {
1103  $this->ref = $initialref;
1104 
1105  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1106  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1107  }
1108 
1109  // Add object linked
1110  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1111  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1112  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, ...))
1113  foreach ($tmp_origin_id as $origin_id) {
1114  $ret = $this->add_object_linked($origin, $origin_id);
1115  if (!$ret) {
1116  $this->error = $this->db->lasterror();
1117  $error++;
1118  }
1119  }
1120  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1121  {
1122  $origin_id = $tmp_origin_id;
1123  $ret = $this->add_object_linked($origin, $origin_id);
1124  if (!$ret) {
1125  $this->error = $this->db->lasterror();
1126  $error++;
1127  }
1128  }
1129  }
1130  }
1131 
1132  if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1133  $originforcontact = $this->origin;
1134  $originidforcontact = $this->origin_id;
1135  if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1136  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1137  $exp = new Expedition($this->db);
1138  $exp->fetch($this->origin_id);
1139  $exp->fetchObjectLinked();
1140  if (count($exp->linkedObjectsIds['commande']) > 0) {
1141  foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1142  $originforcontact = 'commande';
1143  if (is_object($value)) {
1144  $originidforcontact = $value->id;
1145  } else {
1146  $originidforcontact = $value;
1147  }
1148  break; // We take first one
1149  }
1150  }
1151  }
1152 
1153  $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";
1154  $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1155 
1156  $resqlcontact = $this->db->query($sqlcontact);
1157  if ($resqlcontact) {
1158  while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1159  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1160  $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
1161  }
1162  } else {
1163  dol_print_error($resqlcontact);
1164  }
1165  }
1166 
1167  if (!$error) {
1168  $result = $this->insertExtraFields();
1169  if ($result < 0) {
1170  $error++;
1171  }
1172  }
1173 
1174  if (!$error && !$notrigger) {
1175  // Call trigger
1176  $result = $this->call_trigger('ORDER_CREATE', $user);
1177  if ($result < 0) {
1178  $error++;
1179  }
1180  // End call triggers
1181  }
1182 
1183  if (!$error) {
1184  $this->db->commit();
1185  return $this->id;
1186  } else {
1187  $this->db->rollback();
1188  return -1 * $error;
1189  }
1190  } else {
1191  $this->error = $this->db->lasterror();
1192  $this->db->rollback();
1193  return -1;
1194  }
1195  }
1196 
1197  return 0;
1198  } else {
1199  dol_print_error($this->db);
1200  $this->db->rollback();
1201  return -1;
1202  }
1203  }
1204 
1205 
1213  public function createFromClone(User $user, $socid = 0)
1214  {
1215  global $conf, $user, $hookmanager;
1216 
1217  $error = 0;
1218 
1219  $this->db->begin();
1220 
1221  // get lines so they will be clone
1222  foreach ($this->lines as $line) {
1223  $line->fetch_optionals();
1224  }
1225 
1226  // Load source object
1227  $objFrom = clone $this;
1228 
1229  // Change socid if needed
1230  if (!empty($socid) && $socid != $this->socid) {
1231  $objsoc = new Societe($this->db);
1232 
1233  if ($objsoc->fetch($socid) > 0) {
1234  $this->socid = $objsoc->id;
1235  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1236  $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1237  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1238  $this->fk_project = 0;
1239  $this->fk_delivery_address = 0;
1240  }
1241 
1242  // TODO Change product price if multi-prices
1243  }
1244 
1245  $this->id = 0;
1246  $this->ref = '';
1247  $this->statut = self::STATUS_DRAFT;
1248 
1249  // Clear fields
1250  $this->user_author_id = $user->id;
1251  $this->user_valid = 0; // deprecated
1252  $this->user_validation_id = 0;
1253  $this->date = dol_now();
1254  $this->date_commande = dol_now();
1255  $this->date_creation = '';
1256  $this->date_validation = '';
1257  if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) {
1258  $this->ref_client = '';
1259  $this->ref_customer = '';
1260  }
1261 
1262  // Do not clone ref_ext
1263  $num = count($this->lines);
1264  for ($i = 0; $i < $num; $i++) {
1265  $this->lines[$i]->ref_ext = '';
1266  }
1267 
1268  // Create clone
1269  $this->context['createfromclone'] = 'createfromclone';
1270  $result = $this->create($user);
1271  if ($result < 0) {
1272  $error++;
1273  }
1274 
1275  if (!$error) {
1276  // copy internal contacts
1277  if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1278  $error++;
1279  }
1280  }
1281 
1282  if (!$error) {
1283  // copy external contacts if same company
1284  if ($this->socid == $objFrom->socid) {
1285  if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1286  $error++;
1287  }
1288  }
1289  }
1290 
1291  if (!$error) {
1292  // Hook of thirdparty module
1293  if (is_object($hookmanager)) {
1294  $parameters = array('objFrom'=>$objFrom);
1295  $action = '';
1296  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1297  if ($reshook < 0) {
1298  $this->setErrorsFromObject($hookmanager);
1299  $error++;
1300  }
1301  }
1302  }
1303 
1304  unset($this->context['createfromclone']);
1305 
1306  // End
1307  if (!$error) {
1308  $this->db->commit();
1309  return $this->id;
1310  } else {
1311  $this->db->rollback();
1312  return -1;
1313  }
1314  }
1315 
1316 
1324  public function createFromProposal($object, User $user)
1325  {
1326  global $conf, $hookmanager;
1327 
1328  dol_include_once('/multicurrency/class/multicurrency.class.php');
1329  dol_include_once('/core/class/extrafields.class.php');
1330 
1331  $error = 0;
1332 
1333 
1334  $this->date_commande = dol_now();
1335  $this->source = 0;
1336 
1337  $num = count($object->lines);
1338  for ($i = 0; $i < $num; $i++) {
1339  $line = new OrderLine($this->db);
1340 
1341  $line->libelle = $object->lines[$i]->libelle;
1342  $line->label = $object->lines[$i]->label;
1343  $line->desc = $object->lines[$i]->desc;
1344  $line->price = $object->lines[$i]->price;
1345  $line->subprice = $object->lines[$i]->subprice;
1346  $line->vat_src_code = $object->lines[$i]->vat_src_code;
1347  $line->tva_tx = $object->lines[$i]->tva_tx;
1348  $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1349  $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1350  $line->qty = $object->lines[$i]->qty;
1351  $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1352  $line->remise_percent = $object->lines[$i]->remise_percent;
1353  $line->fk_product = $object->lines[$i]->fk_product;
1354  $line->info_bits = $object->lines[$i]->info_bits;
1355  $line->product_type = $object->lines[$i]->product_type;
1356  $line->rang = $object->lines[$i]->rang;
1357  $line->special_code = $object->lines[$i]->special_code;
1358  $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1359  $line->fk_unit = $object->lines[$i]->fk_unit;
1360 
1361  $line->date_start = $object->lines[$i]->date_start;
1362  $line->date_end = $object->lines[$i]->date_end;
1363 
1364  $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1365  $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);
1366  $line->pa_ht = $marginInfos[0];
1367  $line->marge_tx = $marginInfos[1];
1368  $line->marque_tx = $marginInfos[2];
1369 
1370  $line->origin = $object->element;
1371  $line->origin_id = $object->lines[$i]->id;
1372 
1373  // get extrafields from original line
1374  $object->lines[$i]->fetch_optionals();
1375  foreach ($object->lines[$i]->array_options as $options_key => $value) {
1376  $line->array_options[$options_key] = $value;
1377  }
1378 
1379  $this->lines[$i] = $line;
1380  }
1381 
1382  $this->entity = $object->entity;
1383  $this->socid = $object->socid;
1384  $this->fk_project = $object->fk_project;
1385  $this->cond_reglement_id = $object->cond_reglement_id;
1386  $this->deposit_percent = $object->deposit_percent;
1387  $this->mode_reglement_id = $object->mode_reglement_id;
1388  $this->fk_account = $object->fk_account;
1389  $this->availability_id = $object->availability_id;
1390  $this->demand_reason_id = $object->demand_reason_id;
1391  $this->date_livraison = $object->date_livraison; // deprecated
1392  $this->delivery_date = $object->date_livraison;
1393  $this->shipping_method_id = $object->shipping_method_id;
1394  $this->warehouse_id = $object->warehouse_id;
1395  $this->fk_delivery_address = $object->fk_delivery_address;
1396  $this->contact_id = $object->contact_id;
1397  $this->ref_client = $object->ref_client;
1398  $this->ref_customer = $object->ref_client;
1399 
1400  if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) {
1401  $this->note_private = $object->note_private;
1402  $this->note_public = $object->note_public;
1403  }
1404 
1405  $this->origin = $object->element;
1406  $this->origin_id = $object->id;
1407 
1408  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1409  if (!empty($conf->multicurrency->enabled)) {
1410  if (!empty($object->multicurrency_code)) {
1411  $this->multicurrency_code = $object->multicurrency_code;
1412  }
1413  if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($object->multicurrency_tx)) {
1414  $this->multicurrency_tx = $object->multicurrency_tx;
1415  }
1416 
1417  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1418  $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1419  $this->fk_multicurrency = $tmparray[0];
1420  $this->multicurrency_tx = $tmparray[1];
1421  } else {
1422  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1423  }
1424  if (empty($this->fk_multicurrency)) {
1425  $this->multicurrency_code = $conf->currency;
1426  $this->fk_multicurrency = 0;
1427  $this->multicurrency_tx = 1;
1428  }
1429  }
1430 
1431  // get extrafields from original line
1432  $object->fetch_optionals();
1433 
1434  $e = new ExtraFields($this->db);
1435  $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1436 
1437  foreach ($object->array_options as $options_key => $value) {
1438  if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1439  $this->array_options[$options_key] = $value;
1440  }
1441  }
1442  // Possibility to add external linked objects with hooks
1443  $this->linked_objects[$this->origin] = $this->origin_id;
1444  if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1445  $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1446  }
1447 
1448  $ret = $this->create($user);
1449 
1450  if ($ret > 0) {
1451  // Actions hooked (by external module)
1452  $hookmanager->initHooks(array('orderdao'));
1453 
1454  $parameters = array('objFrom'=>$object);
1455  $action = '';
1456  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1457  if ($reshook < 0) {
1458  $this->setErrorsFromObject($hookmanager);
1459  $error++;
1460  }
1461 
1462  if (!$error) {
1463  // Validate immediatly the order
1464  if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL)) {
1465  $this->fetch($ret);
1466  $this->valid($user);
1467  }
1468  return $ret;
1469  } else {
1470  return -1;
1471  }
1472  } else {
1473  return -1;
1474  }
1475  }
1476 
1477 
1518  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)
1519  {
1520  global $mysoc, $conf, $langs, $user;
1521 
1522  $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1523  $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";
1524  $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";
1525  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1526 
1527  if ($this->statut == self::STATUS_DRAFT) {
1528  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1529 
1530  // Clean parameters
1531 
1532  if (empty($remise_percent)) {
1533  $remise_percent = 0;
1534  }
1535  if (empty($qty)) {
1536  $qty = 0;
1537  }
1538  if (empty($info_bits)) {
1539  $info_bits = 0;
1540  }
1541  if (empty($rang)) {
1542  $rang = 0;
1543  }
1544  if (empty($txtva)) {
1545  $txtva = 0;
1546  }
1547  if (empty($txlocaltax1)) {
1548  $txlocaltax1 = 0;
1549  }
1550  if (empty($txlocaltax2)) {
1551  $txlocaltax2 = 0;
1552  }
1553  if (empty($fk_parent_line) || $fk_parent_line < 0) {
1554  $fk_parent_line = 0;
1555  }
1556  if (empty($this->fk_multicurrency)) {
1557  $this->fk_multicurrency = 0;
1558  }
1559  if (empty($ref_ext)) {
1560  $ref_ext = '';
1561  }
1562 
1564  $qty = price2num($qty);
1565  $pu_ht = price2num($pu_ht);
1566  $pu_ht_devise = price2num($pu_ht_devise);
1567  $pu_ttc = price2num($pu_ttc);
1568  $pa_ht = price2num($pa_ht);
1569  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1570  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1571  }
1572  $txlocaltax1 = price2num($txlocaltax1);
1573  $txlocaltax2 = price2num($txlocaltax2);
1574  if ($price_base_type == 'HT') {
1575  $pu = $pu_ht;
1576  } else {
1577  $pu = $pu_ttc;
1578  }
1579  $label = trim($label);
1580  $desc = trim($desc);
1581 
1582  // Check parameters
1583  if ($type < 0) {
1584  return -1;
1585  }
1586 
1587  if ($date_start && $date_end && $date_start > $date_end) {
1588  $langs->load("errors");
1589  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1590  return -1;
1591  }
1592 
1593  $this->db->begin();
1594 
1595  $product_type = $type;
1596  if (!empty($fk_product) && $fk_product > 0) {
1597  $product = new Product($this->db);
1598  $result = $product->fetch($fk_product);
1599  $product_type = $product->type;
1600 
1601  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
1602  $langs->load("errors");
1603  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1604  $this->errors[] = $this->error;
1605  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1606  $this->db->rollback();
1608  }
1609  }
1610  // Calcul du total TTC et de la TVA pour la ligne a partir de
1611  // qty, pu, remise_percent et txtva
1612  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1613  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1614 
1615  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1616 
1617  // Clean vat code
1618  $reg = array();
1619  $vat_src_code = '';
1620  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1621  $vat_src_code = $reg[1];
1622  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1623  }
1624 
1625  $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);
1626 
1627  /*var_dump($txlocaltax1);
1628  var_dump($txlocaltax2);
1629  var_dump($localtaxes_type);
1630  var_dump($tabprice);
1631  var_dump($tabprice[9]);
1632  var_dump($tabprice[10]);
1633  exit;*/
1634 
1635  $total_ht = $tabprice[0];
1636  $total_tva = $tabprice[1];
1637  $total_ttc = $tabprice[2];
1638  $total_localtax1 = $tabprice[9];
1639  $total_localtax2 = $tabprice[10];
1640  $pu_ht = $tabprice[3];
1641 
1642  // MultiCurrency
1643  $multicurrency_total_ht = $tabprice[16];
1644  $multicurrency_total_tva = $tabprice[17];
1645  $multicurrency_total_ttc = $tabprice[18];
1646  $pu_ht_devise = $tabprice[19];
1647 
1648  // Rang to use
1649  $ranktouse = $rang;
1650  if ($ranktouse == -1) {
1651  $rangmax = $this->line_max($fk_parent_line);
1652  $ranktouse = $rangmax + 1;
1653  }
1654 
1655  // TODO A virer
1656  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1657  $price = $pu;
1658  $remise = 0;
1659  if ($remise_percent > 0) {
1660  $remise = round(($pu * $remise_percent / 100), 2);
1661  $price = $pu - $remise;
1662  }
1663 
1664  // Insert line
1665  $this->line = new OrderLine($this->db);
1666 
1667  $this->line->context = $this->context;
1668 
1669  $this->line->fk_commande = $this->id;
1670  $this->line->label = $label;
1671  $this->line->desc = $desc;
1672  $this->line->qty = $qty;
1673  $this->line->ref_ext = $ref_ext;
1674 
1675  $this->line->vat_src_code = $vat_src_code;
1676  $this->line->tva_tx = $txtva;
1677  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1678  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1679  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1680  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1681  $this->line->fk_product = $fk_product;
1682  $this->line->product_type = $product_type;
1683  $this->line->fk_remise_except = $fk_remise_except;
1684  $this->line->remise_percent = $remise_percent;
1685  $this->line->subprice = $pu_ht;
1686  $this->line->rang = $ranktouse;
1687  $this->line->info_bits = $info_bits;
1688  $this->line->total_ht = $total_ht;
1689  $this->line->total_tva = $total_tva;
1690  $this->line->total_localtax1 = $total_localtax1;
1691  $this->line->total_localtax2 = $total_localtax2;
1692  $this->line->total_ttc = $total_ttc;
1693  $this->line->special_code = $special_code;
1694  $this->line->origin = $origin;
1695  $this->line->origin_id = $origin_id;
1696  $this->line->fk_parent_line = $fk_parent_line;
1697  $this->line->fk_unit = $fk_unit;
1698 
1699  $this->line->date_start = $date_start;
1700  $this->line->date_end = $date_end;
1701 
1702  $this->line->fk_fournprice = $fk_fournprice;
1703  $this->line->pa_ht = $pa_ht;
1704 
1705  // Multicurrency
1706  $this->line->fk_multicurrency = $this->fk_multicurrency;
1707  $this->line->multicurrency_code = $this->multicurrency_code;
1708  $this->line->multicurrency_subprice = $pu_ht_devise;
1709  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1710  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1711  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1712 
1713  // TODO Ne plus utiliser
1714  $this->line->price = $price;
1715 
1716  if (is_array($array_options) && count($array_options) > 0) {
1717  $this->line->array_options = $array_options;
1718  }
1719 
1720  $result = $this->line->insert($user);
1721  if ($result > 0) {
1722  // Reorder if child line
1723  if (!empty($fk_parent_line)) {
1724  $this->line_order(true, 'DESC');
1725  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1726  $linecount = count($this->lines);
1727  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1728  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1729  }
1730  }
1731 
1732  // Mise a jour informations denormalisees au niveau de la commande meme
1733  if (empty($noupdateafterinsertline)) {
1734  $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.
1735  }
1736 
1737  if ($result > 0) {
1738  $this->db->commit();
1739  return $this->line->id;
1740  } else {
1741  $this->db->rollback();
1742  return -1;
1743  }
1744  } else {
1745  $this->error = $this->line->error;
1746  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1747  $this->db->rollback();
1748  return -2;
1749  }
1750  } else {
1751  dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1752  return -3;
1753  }
1754  }
1755 
1756 
1757  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1771  public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1772  {
1773  // phpcs:enable
1774  global $conf, $mysoc;
1775 
1776  if (!$qty) {
1777  $qty = 1;
1778  }
1779 
1780  if ($idproduct > 0) {
1781  $prod = new Product($this->db);
1782  $prod->fetch($idproduct);
1783 
1784  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1785  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1786  if (empty($tva_tx)) {
1787  $tva_npr = 0;
1788  }
1789  $vat_src_code = ''; // May be defined into tva_tx
1790 
1791  $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1792  $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1793 
1794  // multiprix
1795  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1796  $price = $prod->multiprices[$this->thirdparty->price_level];
1797  } else {
1798  $price = $prod->price;
1799  }
1800 
1801  $line = new OrderLine($this->db);
1802 
1803  $line->context = $this->context;
1804 
1805  $line->fk_product = $idproduct;
1806  $line->desc = $prod->description;
1807  $line->qty = $qty;
1808  $line->subprice = $price;
1809  $line->remise_percent = $remise_percent;
1810  $line->vat_src_code = $vat_src_code;
1811  $line->tva_tx = $tva_tx;
1812  $line->localtax1_tx = $localtax1_tx;
1813  $line->localtax2_tx = $localtax2_tx;
1814  $line->ref = $prod->ref;
1815  $line->libelle = $prod->label;
1816  $line->product_desc = $prod->description;
1817  $line->fk_unit = $prod->fk_unit;
1818 
1819  // Save the start and end date of the line in the object
1820  if ($date_start) {
1821  $line->date_start = $date_start;
1822  }
1823  if ($date_end) {
1824  $line->date_end = $date_end;
1825  }
1826 
1827  $this->lines[] = $line;
1828 
1847  }
1848  }
1849 
1850 
1860  public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1861  {
1862  // Check parameters
1863  if (empty($id) && empty($ref) && empty($ref_ext)) {
1864  return -1;
1865  }
1866 
1867  $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';
1868  $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';
1869  $sql .= ', c.fk_account';
1870  $sql .= ', c.date_commande, c.date_valid, c.tms';
1871  $sql .= ', c.date_livraison as delivery_date';
1872  $sql .= ', c.fk_shipping_method';
1873  $sql .= ', c.fk_warehouse';
1874  $sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1875  $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';
1876  $sql .= ', c.fk_incoterms, c.location_incoterms';
1877  $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1878  $sql .= ", c.module_source, c.pos_source";
1879  $sql .= ", i.libelle as label_incoterms";
1880  $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1881  $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1882  $sql .= ', ca.code as availability_code, ca.label as availability_label';
1883  $sql .= ', dr.code as demand_reason_code';
1884  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1885  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1886  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1887  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1888  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1889  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1890 
1891  if ($id) {
1892  $sql .= " WHERE c.rowid=".((int) $id);
1893  } else {
1894  $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1895  }
1896 
1897  if ($ref) {
1898  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1899  }
1900  if ($ref_ext) {
1901  $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1902  }
1903 
1904  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1905  $result = $this->db->query($sql);
1906  if ($result) {
1907  $obj = $this->db->fetch_object($result);
1908  if ($obj) {
1909  $this->id = $obj->rowid;
1910  $this->entity = $obj->entity;
1911 
1912  $this->ref = $obj->ref;
1913  $this->ref_client = $obj->ref_client;
1914  $this->ref_customer = $obj->ref_client;
1915  $this->ref_ext = $obj->ref_ext;
1916 
1917  $this->socid = $obj->fk_soc;
1918  $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1919 
1920  $this->fk_project = $obj->fk_project;
1921  $this->project = null; // Clear if another value was already set by fetch_projet
1922 
1923  $this->statut = $obj->fk_statut;
1924  $this->status = $obj->fk_statut;
1925 
1926  $this->user_author_id = $obj->fk_user_author;
1927  $this->user_creation_id = $obj->fk_user_author;
1928  $this->user_validation_id = $obj->fk_user_valid;
1929  $this->user_valid = $obj->fk_user_valid; // deprecated
1930  $this->user_modification_id = $obj->fk_user_modif;
1931  $this->user_modification = $obj->fk_user_modif;
1932  $this->total_ht = $obj->total_ht;
1933  $this->total_tva = $obj->total_tva;
1934  $this->total_localtax1 = $obj->total_localtax1;
1935  $this->total_localtax2 = $obj->total_localtax2;
1936  $this->total_ttc = $obj->total_ttc;
1937  $this->date = $this->db->jdate($obj->date_commande);
1938  $this->date_commande = $this->db->jdate($obj->date_commande);
1939  $this->date_creation = $this->db->jdate($obj->date_creation);
1940  $this->date_validation = $this->db->jdate($obj->date_valid);
1941  $this->date_modification = $this->db->jdate($obj->tms);
1942  $this->remise = $obj->remise;
1943  $this->remise_percent = $obj->remise_percent; // TODO deprecated
1944  $this->remise_absolue = $obj->remise_absolue;
1945  $this->source = $obj->source;
1946  $this->billed = $obj->billed;
1947  $this->note = $obj->note_private; // deprecated
1948  $this->note_private = $obj->note_private;
1949  $this->note_public = $obj->note_public;
1950  $this->model_pdf = $obj->model_pdf;
1951  $this->modelpdf = $obj->model_pdf; // deprecated
1952  $this->last_main_doc = $obj->last_main_doc;
1953  $this->mode_reglement_id = $obj->fk_mode_reglement;
1954  $this->mode_reglement_code = $obj->mode_reglement_code;
1955  $this->mode_reglement = $obj->mode_reglement_libelle;
1956  $this->cond_reglement_id = $obj->fk_cond_reglement;
1957  $this->cond_reglement_code = $obj->cond_reglement_code;
1958  $this->cond_reglement = $obj->cond_reglement_libelle;
1959  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1960  $this->deposit_percent = $obj->deposit_percent;
1961  $this->fk_account = $obj->fk_account;
1962  $this->availability_id = $obj->fk_availability;
1963  $this->availability_code = $obj->availability_code;
1964  $this->availability = $obj->availability_label;
1965  $this->demand_reason_id = $obj->fk_input_reason;
1966  $this->demand_reason_code = $obj->demand_reason_code;
1967  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1968  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1969  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1970  $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1971  $this->fk_delivery_address = $obj->fk_delivery_address;
1972  $this->module_source = $obj->module_source;
1973  $this->pos_source = $obj->pos_source;
1974 
1975  //Incoterms
1976  $this->fk_incoterms = $obj->fk_incoterms;
1977  $this->location_incoterms = $obj->location_incoterms;
1978  $this->label_incoterms = $obj->label_incoterms;
1979 
1980  // Multicurrency
1981  $this->fk_multicurrency = $obj->fk_multicurrency;
1982  $this->multicurrency_code = $obj->multicurrency_code;
1983  $this->multicurrency_tx = $obj->multicurrency_tx;
1984  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1985  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1986  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1987 
1988  $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1989 
1990  $this->lines = array();
1991 
1992  if ($this->statut == self::STATUS_DRAFT) {
1993  $this->brouillon = 1;
1994  }
1995 
1996  // Retrieve all extrafield
1997  // fetch optionals attributes and labels
1998  $this->fetch_optionals();
1999 
2000  $this->db->free($result);
2001 
2002  // Lines
2003  $result = $this->fetch_lines();
2004  if ($result < 0) {
2005  return -3;
2006  }
2007  return 1;
2008  } else {
2009  $this->error = 'Order with id '.$id.' not found sql='.$sql;
2010  return 0;
2011  }
2012  } else {
2013  $this->error = $this->db->error();
2014  return -1;
2015  }
2016  }
2017 
2018 
2019  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2026  public function insert_discount($idremise)
2027  {
2028  // phpcs:enable
2029  global $langs;
2030 
2031  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2032  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2033 
2034  $this->db->begin();
2035 
2036  $remise = new DiscountAbsolute($this->db);
2037  $result = $remise->fetch($idremise);
2038 
2039  if ($result > 0) {
2040  if ($remise->fk_facture) { // Protection against multiple submission
2041  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2042  $this->db->rollback();
2043  return -5;
2044  }
2045 
2046  $line = new OrderLine($this->db);
2047 
2048  $line->fk_commande = $this->id;
2049  $line->fk_remise_except = $remise->id;
2050  $line->desc = $remise->description; // Description ligne
2051  $line->vat_src_code = $remise->vat_src_code;
2052  $line->tva_tx = $remise->tva_tx;
2053  $line->subprice = -$remise->amount_ht;
2054  $line->price = -$remise->amount_ht;
2055  $line->fk_product = 0; // Id produit predefini
2056  $line->qty = 1;
2057  $line->remise_percent = 0;
2058  $line->rang = -1;
2059  $line->info_bits = 2;
2060 
2061  $line->total_ht = -$remise->amount_ht;
2062  $line->total_tva = -$remise->amount_tva;
2063  $line->total_ttc = -$remise->amount_ttc;
2064 
2065  $result = $line->insert();
2066  if ($result > 0) {
2067  $result = $this->update_price(1);
2068  if ($result > 0) {
2069  $this->db->commit();
2070  return 1;
2071  } else {
2072  $this->db->rollback();
2073  return -1;
2074  }
2075  } else {
2076  $this->error = $line->error;
2077  $this->errors = $line->errors;
2078  $this->db->rollback();
2079  return -2;
2080  }
2081  } else {
2082  $this->db->rollback();
2083  return -2;
2084  }
2085  }
2086 
2087 
2088  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2096  public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2097  {
2098  // phpcs:enable
2099  global $langs, $conf;
2100 
2101  $this->lines = array();
2102 
2103  $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,';
2104  $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,';
2105  $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2106  $sql .= ' l.fk_unit,';
2107  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2108  $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,';
2109  $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2110  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2111  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2112  $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2113  if ($only_product) {
2114  $sql .= ' AND p.fk_product_type = 0';
2115  }
2116  $sql .= ' ORDER BY l.rang, l.rowid';
2117 
2118  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2119  $result = $this->db->query($sql);
2120  if ($result) {
2121  $num = $this->db->num_rows($result);
2122 
2123  $i = 0;
2124  while ($i < $num) {
2125  $objp = $this->db->fetch_object($result);
2126 
2127  $line = new OrderLine($this->db);
2128 
2129  $line->rowid = $objp->rowid;
2130  $line->id = $objp->rowid;
2131  $line->fk_commande = $objp->fk_commande;
2132  $line->commande_id = $objp->fk_commande;
2133  $line->label = $objp->custom_label;
2134  $line->desc = $objp->description;
2135  $line->description = $objp->description; // Description line
2136  $line->product_type = $objp->product_type;
2137  $line->qty = $objp->qty;
2138  $line->ref_ext = $objp->ref_ext;
2139 
2140  $line->vat_src_code = $objp->vat_src_code;
2141  $line->tva_tx = $objp->tva_tx;
2142  $line->localtax1_tx = $objp->localtax1_tx;
2143  $line->localtax2_tx = $objp->localtax2_tx;
2144  $line->localtax1_type = $objp->localtax1_type;
2145  $line->localtax2_type = $objp->localtax2_type;
2146  $line->total_ht = $objp->total_ht;
2147  $line->total_ttc = $objp->total_ttc;
2148  $line->total_tva = $objp->total_tva;
2149  $line->total_localtax1 = $objp->total_localtax1;
2150  $line->total_localtax2 = $objp->total_localtax2;
2151  $line->subprice = $objp->subprice;
2152  $line->fk_remise_except = $objp->fk_remise_except;
2153  $line->remise_percent = $objp->remise_percent;
2154  $line->price = $objp->price;
2155  $line->fk_product = $objp->fk_product;
2156  $line->fk_fournprice = $objp->fk_fournprice;
2157  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2158  $line->pa_ht = $marginInfos[0];
2159  $line->marge_tx = $marginInfos[1];
2160  $line->marque_tx = $marginInfos[2];
2161  $line->rang = $objp->rang;
2162  $line->info_bits = $objp->info_bits;
2163  $line->special_code = $objp->special_code;
2164  $line->fk_parent_line = $objp->fk_parent_line;
2165 
2166  $line->ref = $objp->product_ref;
2167  $line->libelle = $objp->product_label;
2168 
2169  $line->product_ref = $objp->product_ref;
2170  $line->product_label = $objp->product_label;
2171  $line->product_tosell = $objp->product_tosell;
2172  $line->product_tobuy = $objp->product_tobuy;
2173  $line->product_desc = $objp->product_desc;
2174  $line->product_tobatch = $objp->product_tobatch;
2175  $line->product_barcode = $objp->product_barcode;
2176 
2177  $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2178  $line->fk_unit = $objp->fk_unit;
2179 
2180  $line->weight = $objp->weight;
2181  $line->weight_units = $objp->weight_units;
2182  $line->volume = $objp->volume;
2183  $line->volume_units = $objp->volume_units;
2184 
2185  $line->date_start = $this->db->jdate($objp->date_start);
2186  $line->date_end = $this->db->jdate($objp->date_end);
2187 
2188  // Multicurrency
2189  $line->fk_multicurrency = $objp->fk_multicurrency;
2190  $line->multicurrency_code = $objp->multicurrency_code;
2191  $line->multicurrency_subprice = $objp->multicurrency_subprice;
2192  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2193  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2194  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2195 
2196  $line->fetch_optionals();
2197 
2198  // multilangs
2199  if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2200  $tmpproduct = new Product($this->db);
2201  $tmpproduct->fetch($objp->fk_product);
2202  $tmpproduct->getMultiLangs();
2203 
2204  $line->multilangs = $tmpproduct->multilangs;
2205  }
2206 
2207  $this->lines[$i] = $line;
2208 
2209  $i++;
2210  }
2211 
2212  $this->db->free($result);
2213 
2214  return 1;
2215  } else {
2216  $this->error = $this->db->error();
2217  return -3;
2218  }
2219  }
2220 
2221 
2227  public function getNbOfProductsLines()
2228  {
2229  $nb = 0;
2230  foreach ($this->lines as $line) {
2231  if ($line->product_type == 0) {
2232  $nb++;
2233  }
2234  }
2235  return $nb;
2236  }
2237 
2243  public function getNbOfServicesLines()
2244  {
2245  $nb = 0;
2246  foreach ($this->lines as $line) {
2247  if ($line->product_type == 1) {
2248  $nb++;
2249  }
2250  }
2251  return $nb;
2252  }
2253 
2259  public function getNbOfShipments()
2260  {
2261  $nb = 0;
2262 
2263  $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2264  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2265  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2266  $sql .= ' WHERE';
2267  $sql .= ' ed.fk_origin_line = cd.rowid';
2268  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2269  //print $sql;
2270 
2271  dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2272  $resql = $this->db->query($sql);
2273  if ($resql) {
2274  $obj = $this->db->fetch_object($resql);
2275  if ($obj) {
2276  $nb = $obj->nb;
2277  }
2278 
2279  $this->db->free($resql);
2280  return $nb;
2281  } else {
2282  $this->error = $this->db->lasterror();
2283  return -1;
2284  }
2285  }
2286 
2295  public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2296  {
2297  $this->expeditions = array();
2298 
2299  $sql = 'SELECT cd.rowid, cd.fk_product,';
2300  $sql .= ' sum(ed.qty) as qty';
2301  $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2302  if ($filtre_statut >= 0) {
2303  $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2304  }
2305  $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2306  $sql .= ' WHERE';
2307  if ($filtre_statut >= 0) {
2308  $sql .= ' ed.fk_expedition = e.rowid AND';
2309  }
2310  $sql .= ' ed.fk_origin_line = cd.rowid';
2311  $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2312  if ($fk_product > 0) {
2313  $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2314  }
2315  if ($filtre_statut >= 0) {
2316  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2317  }
2318  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2319  //print $sql;
2320 
2321  dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2322  $resql = $this->db->query($sql);
2323  if ($resql) {
2324  $num = $this->db->num_rows($resql);
2325  $i = 0;
2326  while ($i < $num) {
2327  $obj = $this->db->fetch_object($resql);
2328  $this->expeditions[$obj->rowid] = $obj->qty;
2329  $i++;
2330  }
2331  $this->db->free($resql);
2332  return $num;
2333  } else {
2334  $this->error = $this->db->lasterror();
2335  return -1;
2336  }
2337  }
2338 
2344  public function countNbOfShipments()
2345  {
2346  $sql = 'SELECT count(*)';
2347  $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2348  $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2349  $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2350  $sql .= " AND el.sourcetype = 'commande'";
2351  $sql .= " AND el.fk_target = e.rowid";
2352  $sql .= " AND el.targettype = 'shipping'";
2353 
2354  $resql = $this->db->query($sql);
2355  if ($resql) {
2356  $row = $this->db->fetch_row($resql);
2357  return $row[0];
2358  } else {
2359  dol_print_error($this->db);
2360  }
2361 
2362  return 0;
2363  }
2364 
2365  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2374  public function stock_array($filtre_statut = self::STATUS_CANCELED)
2375  {
2376  // phpcs:enable
2377  $this->stocks = array();
2378 
2379  // Tableau des id de produit de la commande
2380  $array_of_product = array();
2381 
2382  // Recherche total en stock pour chaque produit
2383  // TODO $array_of_product est défini vide juste au dessus !!
2384  if (count($array_of_product)) {
2385  $sql = "SELECT fk_product, sum(ps.reel) as total";
2386  $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2387  $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2388  $sql .= ' GROUP BY fk_product';
2389  $resql = $this->db->query($sql);
2390  if ($resql) {
2391  $num = $this->db->num_rows($resql);
2392  $i = 0;
2393  while ($i < $num) {
2394  $obj = $this->db->fetch_object($resql);
2395  $this->stocks[$obj->fk_product] = $obj->total;
2396  $i++;
2397  }
2398  $this->db->free($resql);
2399  }
2400  }
2401  return 0;
2402  }
2403 
2412  public function deleteline($user = null, $lineid = 0, $id = 0)
2413  {
2414  if ($this->statut == self::STATUS_DRAFT) {
2415  $this->db->begin();
2416 
2417  // Delete line
2418  $line = new OrderLine($this->db);
2419 
2420  $line->context = $this->context;
2421 
2422  // Load data
2423  $line->fetch($lineid);
2424 
2425  if ($id > 0 && $line->fk_commande != $id) {
2426  $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2427  return -1;
2428  }
2429 
2430  // Memorize previous line for triggers
2431  $staticline = clone $line;
2432  $line->oldline = $staticline;
2433 
2434  if ($line->delete($user) > 0) {
2435  $result = $this->update_price(1);
2436 
2437  if ($result > 0) {
2438  $this->db->commit();
2439  return 1;
2440  } else {
2441  $this->db->rollback();
2442  $this->error = $this->db->lasterror();
2443  return -1;
2444  }
2445  } else {
2446  $this->db->rollback();
2447  $this->error = $line->error;
2448  return -1;
2449  }
2450  } else {
2451  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2452  return -1;
2453  }
2454  }
2455 
2456  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2467  public function set_remise($user, $remise, $notrigger = 0)
2468  {
2469  // phpcs:enable
2470  dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2471  return $this->setDiscount($user, $remise, $notrigger);
2472  }
2473 
2483  public function setDiscount($user, $remise, $notrigger = 0)
2484  {
2485  $remise = trim($remise) ?trim($remise) : 0;
2486 
2487  if ($user->hasRight('commande', 'creer')) {
2488  $error = 0;
2489 
2490  $this->db->begin();
2491 
2492  $remise = price2num($remise, 2);
2493 
2494  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2495  $sql .= ' SET remise_percent = '.((float) $remise);
2496  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2497 
2498  dol_syslog(__METHOD__, LOG_DEBUG);
2499  $resql = $this->db->query($sql);
2500  if (!$resql) {
2501  $this->errors[] = $this->db->error();
2502  $error++;
2503  }
2504 
2505  if (!$error) {
2506  $this->oldcopy = clone $this;
2507  $this->remise_percent = $remise;
2508  $this->update_price(1);
2509  }
2510 
2511  if (!$notrigger && empty($error)) {
2512  // Call trigger
2513  $result = $this->call_trigger('ORDER_MODIFY', $user);
2514  if ($result < 0) {
2515  $error++;
2516  }
2517  // End call triggers
2518  }
2519 
2520  if (!$error) {
2521  $this->db->commit();
2522  return 1;
2523  } else {
2524  foreach ($this->errors as $errmsg) {
2525  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2526  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2527  }
2528  $this->db->rollback();
2529  return -1 * $error;
2530  }
2531  }
2532 
2533  return 0;
2534  }
2535 
2536 
2537  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2546  public function set_remise_absolue($user, $remise, $notrigger = 0)
2547  {
2548  // phpcs:enable
2549  if (empty($remise)) {
2550  $remise = 0;
2551  }
2552 
2553  $remise = price2num($remise);
2554 
2555  if ($user->hasRight('commande', 'creer')) {
2556  $error = 0;
2557 
2558  $this->db->begin();
2559 
2560  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2561  $sql .= ' SET remise_absolue = '.((float) $remise);
2562  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2563 
2564  dol_syslog(__METHOD__, LOG_DEBUG);
2565  $resql = $this->db->query($sql);
2566  if (!$resql) {
2567  $this->errors[] = $this->db->error();
2568  $error++;
2569  }
2570 
2571  if (!$error) {
2572  $this->oldcopy = clone $this;
2573  $this->remise_absolue = $remise;
2574  $this->update_price(1);
2575  }
2576 
2577  if (!$notrigger && empty($error)) {
2578  // Call trigger
2579  $result = $this->call_trigger('ORDER_MODIFY', $user);
2580  if ($result < 0) {
2581  $error++;
2582  }
2583  // End call triggers
2584  }
2585 
2586  if (!$error) {
2587  $this->db->commit();
2588  return 1;
2589  } else {
2590  foreach ($this->errors as $errmsg) {
2591  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2592  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2593  }
2594  $this->db->rollback();
2595  return -1 * $error;
2596  }
2597  }
2598 
2599  return 0;
2600  }
2601 
2602 
2603  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2612  public function set_date($user, $date, $notrigger = 0)
2613  {
2614  // phpcs:enable
2615  if ($user->hasRight('commande', 'creer')) {
2616  $error = 0;
2617 
2618  $this->db->begin();
2619 
2620  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2621  $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2622  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2623 
2624  dol_syslog(__METHOD__, LOG_DEBUG);
2625  $resql = $this->db->query($sql);
2626  if (!$resql) {
2627  $this->errors[] = $this->db->error();
2628  $error++;
2629  }
2630 
2631  if (!$error) {
2632  $this->oldcopy = clone $this;
2633  $this->date = $date;
2634  }
2635 
2636  if (!$notrigger && empty($error)) {
2637  // Call trigger
2638  $result = $this->call_trigger('ORDER_MODIFY', $user);
2639  if ($result < 0) {
2640  $error++;
2641  }
2642  // End call triggers
2643  }
2644 
2645  if (!$error) {
2646  $this->db->commit();
2647  return 1;
2648  } else {
2649  foreach ($this->errors as $errmsg) {
2650  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2651  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2652  }
2653  $this->db->rollback();
2654  return -1 * $error;
2655  }
2656  } else {
2657  return -2;
2658  }
2659  }
2660 
2661  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2671  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2672  {
2673  // phpcs:enable
2674  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2675  }
2676 
2685  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2686  {
2687  if ($user->hasRight('commande', 'creer')) {
2688  $error = 0;
2689 
2690  $this->db->begin();
2691 
2692  $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2693  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2694  $sql .= " WHERE rowid = ".((int) $this->id);
2695 
2696  dol_syslog(__METHOD__, LOG_DEBUG);
2697  $resql = $this->db->query($sql);
2698  if (!$resql) {
2699  $this->errors[] = $this->db->error();
2700  $error++;
2701  }
2702 
2703  if (!$error) {
2704  $this->oldcopy = clone $this;
2705  $this->date_livraison = $delivery_date;
2706  $this->delivery_date = $delivery_date;
2707  }
2708 
2709  if (!$notrigger && empty($error)) {
2710  // Call trigger
2711  $result = $this->call_trigger('ORDER_MODIFY', $user);
2712  if ($result < 0) {
2713  $error++;
2714  }
2715  // End call triggers
2716  }
2717 
2718  if (!$error) {
2719  $this->db->commit();
2720  return 1;
2721  } else {
2722  foreach ($this->errors as $errmsg) {
2723  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2724  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2725  }
2726  $this->db->rollback();
2727  return -1 * $error;
2728  }
2729  } else {
2730  return -2;
2731  }
2732  }
2733 
2734  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2748  public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2749  {
2750  // phpcs:enable
2751  global $user;
2752 
2753  $ga = array();
2754 
2755  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2756  $sql .= " c.rowid as cid, c.ref";
2757  if (empty($user->rights->societe->client->voir) && !$socid) {
2758  $sql .= ", sc.fk_soc, sc.fk_user";
2759  }
2760  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2761  if (empty($user->rights->societe->client->voir) && !$socid) {
2762  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2763  }
2764  $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2765  $sql .= " AND c.fk_soc = s.rowid";
2766  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2767  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2768  }
2769  if ($socid) {
2770  $sql .= " AND s.rowid = ".((int) $socid);
2771  }
2772  if ($draft) {
2773  $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2774  }
2775  if (is_object($excluser)) {
2776  $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2777  }
2778  $sql .= $this->db->order($sortfield, $sortorder);
2779  $sql .= $this->db->plimit($limit, $offset);
2780 
2781  $result = $this->db->query($sql);
2782  if ($result) {
2783  $numc = $this->db->num_rows($result);
2784  if ($numc) {
2785  $i = 0;
2786  while ($i < $numc) {
2787  $obj = $this->db->fetch_object($result);
2788 
2789  if ($shortlist == 1) {
2790  $ga[$obj->cid] = $obj->ref;
2791  } elseif ($shortlist == 2) {
2792  $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2793  } else {
2794  $ga[$i]['id'] = $obj->cid;
2795  $ga[$i]['ref'] = $obj->ref;
2796  $ga[$i]['name'] = $obj->name;
2797  }
2798  $i++;
2799  }
2800  }
2801  return $ga;
2802  } else {
2803  dol_print_error($this->db);
2804  return -1;
2805  }
2806  }
2807 
2815  public function availability($availability_id, $notrigger = 0)
2816  {
2817  global $user;
2818 
2819  dol_syslog('Commande::availability('.$availability_id.')');
2820  if ($this->statut >= self::STATUS_DRAFT) {
2821  $error = 0;
2822 
2823  $this->db->begin();
2824 
2825  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2826  $sql .= ' SET fk_availability = '.((int) $availability_id);
2827  $sql .= ' WHERE rowid='.((int) $this->id);
2828 
2829  dol_syslog(__METHOD__, LOG_DEBUG);
2830  $resql = $this->db->query($sql);
2831  if (!$resql) {
2832  $this->errors[] = $this->db->error();
2833  $error++;
2834  }
2835 
2836  if (!$error) {
2837  $this->oldcopy = clone $this;
2838  $this->availability_id = $availability_id;
2839  }
2840 
2841  if (!$notrigger && empty($error)) {
2842  // Call trigger
2843  $result = $this->call_trigger('ORDER_MODIFY', $user);
2844  if ($result < 0) {
2845  $error++;
2846  }
2847  // End call triggers
2848  }
2849 
2850  if (!$error) {
2851  $this->db->commit();
2852  return 1;
2853  } else {
2854  foreach ($this->errors as $errmsg) {
2855  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2856  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2857  }
2858  $this->db->rollback();
2859  return -1 * $error;
2860  }
2861  } else {
2862  $error_str = 'Command status do not meet requirement '.$this->statut;
2863  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2864  $this->error = $error_str;
2865  $this->errors[] = $this->error;
2866  return -2;
2867  }
2868  }
2869 
2870  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2878  public function demand_reason($demand_reason_id, $notrigger = 0)
2879  {
2880  // phpcs:enable
2881  global $user;
2882 
2883  dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2884  if ($this->statut >= self::STATUS_DRAFT) {
2885  $error = 0;
2886 
2887  $this->db->begin();
2888 
2889  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2890  $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2891  $sql .= ' WHERE rowid='.((int) $this->id);
2892 
2893  dol_syslog(__METHOD__, LOG_DEBUG);
2894  $resql = $this->db->query($sql);
2895  if (!$resql) {
2896  $this->errors[] = $this->db->error();
2897  $error++;
2898  }
2899 
2900  if (!$error) {
2901  $this->oldcopy = clone $this;
2902  $this->demand_reason_id = $demand_reason_id;
2903  }
2904 
2905  if (!$notrigger && empty($error)) {
2906  // Call trigger
2907  $result = $this->call_trigger('ORDER_MODIFY', $user);
2908  if ($result < 0) {
2909  $error++;
2910  }
2911  // End call triggers
2912  }
2913 
2914  if (!$error) {
2915  $this->db->commit();
2916  return 1;
2917  } else {
2918  foreach ($this->errors as $errmsg) {
2919  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2920  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2921  }
2922  $this->db->rollback();
2923  return -1 * $error;
2924  }
2925  } else {
2926  $error_str = 'order status do not meet requirement '.$this->statut;
2927  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2928  $this->error = $error_str;
2929  $this->errors[] = $this->error;
2930  return -2;
2931  }
2932  }
2933 
2934  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2943  public function set_ref_client($user, $ref_client, $notrigger = 0)
2944  {
2945  // phpcs:enable
2946  if ($user->hasRight('commande', 'creer')) {
2947  $error = 0;
2948 
2949  $this->db->begin();
2950 
2951  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2952  $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2953  $sql .= ' WHERE rowid = '.((int) $this->id);
2954 
2955  dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2956  $resql = $this->db->query($sql);
2957  if (!$resql) {
2958  $this->errors[] = $this->db->error();
2959  $error++;
2960  }
2961 
2962  if (!$error) {
2963  $this->oldcopy = clone $this;
2964  $this->ref_client = $ref_client;
2965  $this->ref_customer = $ref_client;
2966  }
2967 
2968  if (!$notrigger && empty($error)) {
2969  // Call trigger
2970  $result = $this->call_trigger('ORDER_MODIFY', $user);
2971  if ($result < 0) {
2972  $error++;
2973  }
2974  // End call triggers
2975  }
2976  if (!$error) {
2977  $this->db->commit();
2978  return 1;
2979  } else {
2980  foreach ($this->errors as $errmsg) {
2981  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2982  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2983  }
2984  $this->db->rollback();
2985  return -1 * $error;
2986  }
2987  } else {
2988  return -1;
2989  }
2990  }
2991 
2999  public function classifyBilled(User $user, $notrigger = 0)
3000  {
3001  $error = 0;
3002 
3003  if ($this->billed) {
3004  return 0;
3005  }
3006 
3007  $this->db->begin();
3008 
3009  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
3010  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3011 
3012  dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3013  if ($this->db->query($sql)) {
3014  if (!$error) {
3015  $this->oldcopy = clone $this;
3016  $this->billed = 1;
3017  }
3018 
3019  if (!$notrigger && empty($error)) {
3020  // Call trigger
3021  $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3022  if ($result < 0) {
3023  $error++;
3024  }
3025  // End call triggers
3026  }
3027 
3028  if (!$error) {
3029  $this->db->commit();
3030  return 1;
3031  } else {
3032  foreach ($this->errors as $errmsg) {
3033  dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3034  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3035  }
3036  $this->db->rollback();
3037  return -1 * $error;
3038  }
3039  } else {
3040  $this->error = $this->db->error();
3041  $this->db->rollback();
3042  return -1;
3043  }
3044  }
3045 
3053  public function classifyUnBilled(User $user, $notrigger = 0)
3054  {
3055  $error = 0;
3056 
3057  $this->db->begin();
3058 
3059  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3060  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3061 
3062  dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3063  if ($this->db->query($sql)) {
3064  if (!$error) {
3065  $this->oldcopy = clone $this;
3066  $this->billed = 1;
3067  }
3068 
3069  if (!$notrigger && empty($error)) {
3070  // Call trigger
3071  $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3072  if ($result < 0) {
3073  $error++;
3074  }
3075  // End call triggers
3076  }
3077 
3078  if (!$error) {
3079  $this->billed = 0;
3080 
3081  $this->db->commit();
3082  return 1;
3083  } else {
3084  foreach ($this->errors as $errmsg) {
3085  dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3086  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3087  }
3088  $this->db->rollback();
3089  return -1 * $error;
3090  }
3091  } else {
3092  $this->error = $this->db->error();
3093  $this->db->rollback();
3094  return -1;
3095  }
3096  }
3097 
3098 
3129  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)
3130  {
3131  global $conf, $mysoc, $langs, $user;
3132 
3133  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");
3134  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3135 
3136  if ($this->statut == Commande::STATUS_DRAFT) {
3137  // Clean parameters
3138  if (empty($qty)) {
3139  $qty = 0;
3140  }
3141  if (empty($info_bits)) {
3142  $info_bits = 0;
3143  }
3144  if (empty($txtva)) {
3145  $txtva = 0;
3146  }
3147  if (empty($txlocaltax1)) {
3148  $txlocaltax1 = 0;
3149  }
3150  if (empty($txlocaltax2)) {
3151  $txlocaltax2 = 0;
3152  }
3153  if (empty($remise_percent)) {
3154  $remise_percent = 0;
3155  }
3156  if (empty($special_code) || $special_code == 3) {
3157  $special_code = 0;
3158  }
3159  if (empty($ref_ext)) {
3160  $ref_ext = '';
3161  }
3162 
3163  if ($date_start && $date_end && $date_start > $date_end) {
3164  $langs->load("errors");
3165  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3166  return -1;
3167  }
3168 
3170  $qty = price2num($qty);
3171  $pu = price2num($pu);
3172  $pa_ht = price2num($pa_ht);
3173  $pu_ht_devise = price2num($pu_ht_devise);
3174  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
3175  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3176  }
3177  $txlocaltax1 = price2num($txlocaltax1);
3178  $txlocaltax2 = price2num($txlocaltax2);
3179 
3180  $this->db->begin();
3181 
3182  // Calcul du total TTC et de la TVA pour la ligne a partir de
3183  // qty, pu, remise_percent et txtva
3184  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3185  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3186 
3187  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3188 
3189  // Clean vat code
3190  $vat_src_code = '';
3191  $reg = array();
3192  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3193  $vat_src_code = $reg[1];
3194  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3195  }
3196 
3197  $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);
3198 
3199  $total_ht = $tabprice[0];
3200  $total_tva = $tabprice[1];
3201  $total_ttc = $tabprice[2];
3202  $total_localtax1 = $tabprice[9];
3203  $total_localtax2 = $tabprice[10];
3204  $pu_ht = $tabprice[3];
3205  $pu_tva = $tabprice[4];
3206  $pu_ttc = $tabprice[5];
3207 
3208  // MultiCurrency
3209  $multicurrency_total_ht = $tabprice[16];
3210  $multicurrency_total_tva = $tabprice[17];
3211  $multicurrency_total_ttc = $tabprice[18];
3212  $pu_ht_devise = $tabprice[19];
3213 
3214  // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3215  $price = $pu_ht;
3216  if ($price_base_type == 'TTC') {
3217  $subprice = $pu_ttc;
3218  } else {
3219  $subprice = $pu_ht;
3220  }
3221  $remise = 0;
3222  if ($remise_percent > 0) {
3223  $remise = round(($pu * $remise_percent / 100), 2);
3224  $price = ($pu - $remise);
3225  }
3226 
3227  //Fetch current line from the database and then clone the object and set it in $oldline property
3228  $line = new OrderLine($this->db);
3229  $line->fetch($rowid);
3230  $line->fetch_optionals();
3231 
3232  if (!empty($line->fk_product)) {
3233  $product = new Product($this->db);
3234  $result = $product->fetch($line->fk_product);
3235  $product_type = $product->type;
3236 
3237  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
3238  $langs->load("errors");
3239  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3240  $this->errors[] = $this->error;
3241 
3242  dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3243 
3244  $this->db->rollback();
3246  }
3247  }
3248 
3249  $staticline = clone $line;
3250 
3251  $line->oldline = $staticline;
3252  $this->line = $line;
3253  $this->line->context = $this->context;
3254  $this->line->rang = $rang;
3255 
3256  // Reorder if fk_parent_line change
3257  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3258  $rangmax = $this->line_max($fk_parent_line);
3259  $this->line->rang = $rangmax + 1;
3260  }
3261 
3262  $this->line->id = $rowid;
3263  $this->line->label = $label;
3264  $this->line->desc = $desc;
3265  $this->line->qty = $qty;
3266  $this->line->ref_ext = $ref_ext;
3267 
3268  $this->line->vat_src_code = $vat_src_code;
3269  $this->line->tva_tx = $txtva;
3270  $this->line->localtax1_tx = $txlocaltax1;
3271  $this->line->localtax2_tx = $txlocaltax2;
3272  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3273  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3274  $this->line->remise_percent = $remise_percent;
3275  $this->line->subprice = $subprice;
3276  $this->line->info_bits = $info_bits;
3277  $this->line->special_code = $special_code;
3278  $this->line->total_ht = $total_ht;
3279  $this->line->total_tva = $total_tva;
3280  $this->line->total_localtax1 = $total_localtax1;
3281  $this->line->total_localtax2 = $total_localtax2;
3282  $this->line->total_ttc = $total_ttc;
3283  $this->line->date_start = $date_start;
3284  $this->line->date_end = $date_end;
3285  $this->line->product_type = $type;
3286  $this->line->fk_parent_line = $fk_parent_line;
3287  $this->line->skip_update_total = $skip_update_total;
3288  $this->line->fk_unit = $fk_unit;
3289 
3290  $this->line->fk_fournprice = $fk_fournprice;
3291  $this->line->pa_ht = $pa_ht;
3292 
3293  // Multicurrency
3294  $this->line->multicurrency_subprice = $pu_ht_devise;
3295  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3296  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3297  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3298 
3299  // TODO deprecated
3300  $this->line->price = $price;
3301 
3302  if (is_array($array_options) && count($array_options) > 0) {
3303  // We replace values in this->line->array_options only for entries defined into $array_options
3304  foreach ($array_options as $key => $value) {
3305  $this->line->array_options[$key] = $array_options[$key];
3306  }
3307  }
3308 
3309  $result = $this->line->update($user, $notrigger);
3310  if ($result > 0) {
3311  // Reorder if child line
3312  if (!empty($fk_parent_line)) {
3313  $this->line_order(true, 'DESC');
3314  }
3315 
3316  // Mise a jour info denormalisees
3317  $this->update_price(1, 'auto');
3318 
3319  $this->db->commit();
3320  return $result;
3321  } else {
3322  $this->error = $this->line->error;
3323 
3324  $this->db->rollback();
3325  return -1;
3326  }
3327  } else {
3328  $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3329  $this->errors = array('OrderStatusMakeOperationForbidden');
3330  return -2;
3331  }
3332  }
3333 
3341  public function update(User $user, $notrigger = 0)
3342  {
3343  global $conf;
3344 
3345  $error = 0;
3346 
3347  // Clean parameters
3348  if (isset($this->ref)) {
3349  $this->ref = trim($this->ref);
3350  }
3351  if (isset($this->ref_client)) {
3352  $this->ref_client = trim($this->ref_client);
3353  }
3354  if (isset($this->ref_customer)) {
3355  $this->ref_customer = trim($this->ref_customer);
3356  }
3357  if (isset($this->note) || isset($this->note_private)) {
3358  $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3359  }
3360  if (isset($this->note_public)) {
3361  $this->note_public = trim($this->note_public);
3362  }
3363  if (isset($this->model_pdf)) {
3364  $this->model_pdf = trim($this->model_pdf);
3365  }
3366  if (isset($this->import_key)) {
3367  $this->import_key = trim($this->import_key);
3368  }
3369  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
3370 
3371  // Check parameters
3372  // Put here code to add control on parameters values
3373 
3374  // Update request
3375  $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3376 
3377  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3378  $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3379  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3380  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3381  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3382  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3383  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3384  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3385  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3386  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3387  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3388  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3389  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3390  $sql .= " fk_user_valid=".((isset($this->user_valid) && $this->user_valid > 0) ? $this->user_valid : "null").",";
3391  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3392  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3393  $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3394  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3395  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3396  $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3397  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3398  $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3399  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3400  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3401  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3402  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3403 
3404  $sql .= " WHERE rowid=".((int) $this->id);
3405 
3406  $this->db->begin();
3407 
3408  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3409  $resql = $this->db->query($sql);
3410  if (!$resql) {
3411  $error++; $this->errors[] = "Error ".$this->db->lasterror();
3412  }
3413 
3414  if (!$error) {
3415  $result = $this->insertExtraFields();
3416  if ($result < 0) {
3417  $error++;
3418  }
3419  }
3420 
3421  if (!$error && !$notrigger) {
3422  // Call trigger
3423  $result = $this->call_trigger('ORDER_MODIFY', $user);
3424  if ($result < 0) {
3425  $error++;
3426  }
3427  // End call triggers
3428  }
3429 
3430  // Commit or rollback
3431  if ($error) {
3432  foreach ($this->errors as $errmsg) {
3433  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3434  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3435  }
3436  $this->db->rollback();
3437  return -1 * $error;
3438  } else {
3439  $this->db->commit();
3440  return 1;
3441  }
3442  }
3443 
3451  public function delete($user, $notrigger = 0)
3452  {
3453  global $conf, $langs;
3454  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3455 
3456  $error = 0;
3457 
3458  dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3459 
3460  $this->db->begin();
3461 
3462  if (!$notrigger) {
3463  // Call trigger
3464  $result = $this->call_trigger('ORDER_DELETE', $user);
3465  if ($result < 0) {
3466  $error++;
3467  }
3468  // End call triggers
3469  }
3470 
3471  // Test we can delete
3472  if ($this->countNbOfShipments() != 0) {
3473  $this->errors[] = $langs->trans('SomeShipmentExists');
3474  $error++;
3475  }
3476 
3477  // Delete extrafields of lines and lines
3478  if (!$error && !empty($this->table_element_line)) {
3479  $tabletodelete = $this->table_element_line;
3480  $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).")";
3481  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3482  if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3483  $error++;
3484  $this->error = $this->db->lasterror();
3485  $this->errors[] = $this->error;
3486  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3487  }
3488  }
3489 
3490  if (!$error) {
3491  // Delete linked object
3492  $res = $this->deleteObjectLinked();
3493  if ($res < 0) {
3494  $error++;
3495  }
3496  }
3497 
3498  if (!$error) {
3499  // Delete linked contacts
3500  $res = $this->delete_linked_contact();
3501  if ($res < 0) {
3502  $error++;
3503  }
3504  }
3505 
3506  // Removed extrafields of object
3507  if (!$error) {
3508  $result = $this->deleteExtraFields();
3509  if ($result < 0) {
3510  $error++;
3511  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3512  }
3513  }
3514 
3515  // Delete main record
3516  if (!$error) {
3517  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3518  $res = $this->db->query($sql);
3519  if (!$res) {
3520  $error++;
3521  $this->error = $this->db->lasterror();
3522  $this->errors[] = $this->error;
3523  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3524  }
3525  }
3526 
3527  // Delete record into ECM index and physically
3528  if (!$error) {
3529  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3530  if (!$res) {
3531  $error++;
3532  }
3533  }
3534 
3535  if (!$error) {
3536  // We remove directory
3537  $ref = dol_sanitizeFileName($this->ref);
3538  if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3539  $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3540  $file = $dir."/".$ref.".pdf";
3541  if (file_exists($file)) {
3542  dol_delete_preview($this);
3543 
3544  if (!dol_delete_file($file, 0, 0, 0, $this)) {
3545  $this->error = 'ErrorFailToDeleteFile';
3546  $this->errors[] = $this->error;
3547  $this->db->rollback();
3548  return 0;
3549  }
3550  }
3551  if (file_exists($dir)) {
3552  $res = @dol_delete_dir_recursive($dir);
3553  if (!$res) {
3554  $this->error = 'ErrorFailToDeleteDir';
3555  $this->errors[] = $this->error;
3556  $this->db->rollback();
3557  return 0;
3558  }
3559  }
3560  }
3561  }
3562 
3563  if (!$error) {
3564  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3565  $this->db->commit();
3566  return 1;
3567  } else {
3568  $this->db->rollback();
3569  return -1;
3570  }
3571  }
3572 
3573 
3574  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3582  public function load_board($user, $mode)
3583  {
3584  // phpcs:enable
3585  global $conf, $langs;
3586 
3587  $clause = " WHERE";
3588 
3589  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3590  $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3591  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3592  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3593  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3594  $clause = " AND";
3595  }
3596  $sql .= $clause." c.entity IN (".getEntity('commande').")";
3597  //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3598  if ($mode == 'toship') {
3599  // An order to ship is an open order (validated or in progress)
3600  $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3601  }
3602  if ($mode == 'tobill') {
3603  // An order to bill is an order not already billed
3604  $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3605  }
3606  if ($mode == 'shippedtobill') {
3607  // An order shipped and to bill is a delivered order not already billed
3608  $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3609  }
3610  if ($user->socid) {
3611  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3612  }
3613 
3614  $resql = $this->db->query($sql);
3615  if ($resql) {
3616  $delay_warning = 0;
3617  $label = $labelShort = $url = '';
3618  if ($mode == 'toship') {
3619  $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3620  $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3621  $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3622  $labelShort = $langs->transnoentitiesnoconv("Opened");
3623  }
3624  if ($mode == 'tobill') {
3625  $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3626  $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3627  $labelShort = $langs->trans("ToBill");
3628  }
3629  if ($mode == 'shippedtobill') {
3630  $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3631  $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3632  $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3633  }
3634 
3635  $response = new WorkboardResponse();
3636  $response->warning_delay = $delay_warning;
3637  $response->label = $label;
3638  $response->labelShort = $labelShort;
3639  $response->url = $url;
3640  $response->img = img_object('', "order");
3641 
3642  $generic_commande = new Commande($this->db);
3643 
3644  while ($obj = $this->db->fetch_object($resql)) {
3645  $response->nbtodo++;
3646  $response->total += $obj->total_ht;
3647 
3648  $generic_commande->statut = $obj->fk_statut;
3649  $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3650  $generic_commande->date = $this->db->jdate($obj->date_commande);
3651  $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3652  $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3653 
3654  if ($mode == 'toship' && $generic_commande->hasDelay()) {
3655  $response->nbtodolate++;
3656  }
3657  }
3658 
3659  return $response;
3660  } else {
3661  $this->error = $this->db->error();
3662  return -1;
3663  }
3664  }
3665 
3671  public function getLabelSource()
3672  {
3673  global $langs;
3674 
3675  $label = $langs->trans('OrderSource'.$this->source);
3676 
3677  if ($label == 'OrderSource') {
3678  return '';
3679  }
3680  return $label;
3681  }
3682 
3689  public function getLibStatut($mode)
3690  {
3691  return $this->LibStatut($this->statut, $this->billed, $mode);
3692  }
3693 
3694  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3704  public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3705  {
3706  // phpcs:enable
3707  global $langs, $conf, $hookmanager;
3708 
3709  $billedtext = '';
3710  if (empty($donotshowbilled)) {
3711  $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3712  }
3713 
3714  $labelTooltip = '';
3715 
3716  if ($status == self::STATUS_CANCELED) {
3717  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3718  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3719  $statusType = 'status9';
3720  } elseif ($status == self::STATUS_DRAFT) {
3721  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3722  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3723  $statusType = 'status0';
3724  } elseif ($status == self::STATUS_VALIDATED) {
3725  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3726  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3727  $statusType = 'status1';
3728  } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3729  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3730  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3731  $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3732  if (!empty($this->delivery_date)) {
3733  $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3734  }
3735  $statusType = 'status4';
3736  } elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3737  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderToBill'); // translated into Delivered
3738  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderToBillShort'); // translated into Delivered
3739  $statusType = 'status4';
3740  } elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3741  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderProcessed').$billedtext;
3742  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderProcessedShort').$billedtext;
3743  $statusType = 'status6';
3744  } elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3745  $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered');
3746  $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort');
3747  $statusType = 'status6';
3748  } else {
3749  $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3750  $labelStatusShort = '';
3751  $statusType = '';
3752  $mode = 0;
3753  }
3754 
3755  $parameters = array(
3756  'status' => $status,
3757  'mode' => $mode,
3758  'billed' => $billed,
3759  'donotshowbilled' => $donotshowbilled
3760  );
3761 
3762  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3763 
3764  if ($reshook > 0) {
3765  return $hookmanager->resPrint;
3766  }
3767 
3768  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3769  }
3770 
3777  public function getTooltipContentArray($params)
3778  {
3779  global $conf, $langs, $user;
3780 
3781  $langs->load('orders');
3782  $datas = [];
3783  $nofetch = !empty($params['nofetch']);
3784 
3785  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3786  return ['optimize' => $langs->trans("Order")];
3787  }
3788 
3789  if ($user->hasRight('commande', 'lire')) {
3790  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3791  if (isset($this->statut)) {
3792  $datas['status'] = ' '.$this->getLibStatut(5);
3793  }
3794  $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3795  if (!$nofetch) {
3796  $langs->load('companies');
3797  if (empty($this->thirdparty)) {
3798  $this->fetch_thirdparty();
3799  }
3800  $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3801  }
3802  $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3803  if (!$nofetch) {
3804  $langs->load('project');
3805  if (empty($this->project)) {
3806  $res = $this->fetch_project();
3807  if ($res > 0) {
3808  $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3809  }
3810  }
3811  }
3812  if (!empty($this->total_ht)) {
3813  $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3814  }
3815  if (!empty($this->total_tva)) {
3816  $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3817  }
3818  if (!empty($this->total_ttc)) {
3819  $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3820  }
3821  if (!empty($this->date)) {
3822  $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3823  }
3824  if (!empty($this->delivery_date)) {
3825  $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3826  }
3827  }
3828 
3829  return $datas;
3830  }
3831 
3845  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3846  {
3847  global $conf, $langs, $user, $hookmanager;
3848 
3849  if (!empty($conf->dol_no_mouse_hover)) {
3850  $notooltip = 1; // Force disable tooltips
3851  }
3852 
3853  $result = '';
3854 
3855  if (isModEnabled("expedition") && ($option == '1' || $option == '2')) {
3856  $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3857  } else {
3858  $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3859  }
3860 
3861  if (!$user->rights->commande->lire) {
3862  $option = 'nolink';
3863  }
3864 
3865  if ($option !== 'nolink') {
3866  // Add param to save lastsearch_values or not
3867  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3868  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3869  $add_save_lastsearch_values = 1;
3870  }
3871  if ($add_save_lastsearch_values) {
3872  $url .= '&save_lastsearch_values=1';
3873  }
3874  }
3875 
3876  if ($short) {
3877  return $url;
3878  }
3879  $params = [
3880  'id' => $this->id,
3881  'objecttype' => $this->element,
3882  'option' => $option,
3883  'nofetch' => 1,
3884  ];
3885  $classfortooltip = 'classfortooltip';
3886  $dataparams = '';
3887  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3888  $classfortooltip = 'classforajaxtooltip';
3889  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3890  $label = '';
3891  } else {
3892  $label = implode($this->getTooltipContentArray($params));
3893  }
3894 
3895  $linkclose = '';
3896  if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3897  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3898  $label = $langs->trans("Order");
3899  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3900  }
3901  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3902  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3903 
3904  $target_value = array('_self', '_blank', '_parent', '_top');
3905  if (in_array($target, $target_value)) {
3906  $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3907  }
3908  }
3909 
3910  $linkstart = '<a href="'.$url.'"';
3911  $linkstart .= $linkclose.'>';
3912  $linkend = '</a>';
3913 
3914  if ($option === 'nolink') {
3915  $linkstart = '';
3916  $linkend = '';
3917  }
3918 
3919  $result .= $linkstart;
3920  if ($withpicto) {
3921  $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3922  }
3923  if ($withpicto != 2) {
3924  $result .= $this->ref;
3925  }
3926  $result .= $linkend;
3927 
3928  if ($addlinktonotes) {
3929  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3930  if ($txttoshow) {
3931  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3932  $result .= ' <span class="note inline-block">';
3933  $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3934  $result .= img_picto('', 'note');
3935  $result .= '</a>';
3936  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3937  //$result.='</a>';
3938  $result .= '</span>';
3939  }
3940  }
3941 
3942  global $action;
3943  $hookmanager->initHooks(array($this->element . 'dao'));
3944  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3945  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3946  if ($reshook > 0) {
3947  $result = $hookmanager->resPrint;
3948  } else {
3949  $result .= $hookmanager->resPrint;
3950  }
3951  return $result;
3952  }
3953 
3954 
3961  public function info($id)
3962  {
3963  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3964  $sql .= ' date_valid as datev,';
3965  $sql .= ' date_cloture as datecloture,';
3966  $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3967  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3968  $sql .= ' WHERE c.rowid = '.((int) $id);
3969  $result = $this->db->query($sql);
3970  if ($result) {
3971  if ($this->db->num_rows($result)) {
3972  $obj = $this->db->fetch_object($result);
3973  $this->id = $obj->rowid;
3974  if ($obj->fk_user_author) {
3975  $this->user_creation_id = $obj->fk_user_author;
3976  }
3977  if ($obj->fk_user_valid) {
3978  $this->user_validation_id = $obj->fk_user_valid;
3979  }
3980  if ($obj->fk_user_cloture) {
3981  $this->user_closing_id = $obj->fk_user_cloture;
3982  }
3983 
3984  $this->date_creation = $this->db->jdate($obj->datec);
3985  $this->date_modification = $this->db->jdate($obj->datem);
3986  $this->date_validation = $this->db->jdate($obj->datev);
3987  $this->date_cloture = $this->db->jdate($obj->datecloture);
3988  }
3989 
3990  $this->db->free($result);
3991  } else {
3992  dol_print_error($this->db);
3993  }
3994  }
3995 
3996 
4004  public function initAsSpecimen()
4005  {
4006  global $conf, $langs;
4007 
4008  dol_syslog(get_class($this)."::initAsSpecimen");
4009 
4010  // Load array of products prodids
4011  $num_prods = 0;
4012  $prodids = array();
4013  $sql = "SELECT rowid";
4014  $sql .= " FROM ".MAIN_DB_PREFIX."product";
4015  $sql .= " WHERE entity IN (".getEntity('product').")";
4016  $sql .= $this->db->plimit(100);
4017 
4018  $resql = $this->db->query($sql);
4019  if ($resql) {
4020  $num_prods = $this->db->num_rows($resql);
4021  $i = 0;
4022  while ($i < $num_prods) {
4023  $i++;
4024  $row = $this->db->fetch_row($resql);
4025  $prodids[$i] = $row[0];
4026  }
4027  }
4028 
4029  // Initialise parametres
4030  $this->id = 0;
4031  $this->ref = 'SPECIMEN';
4032  $this->specimen = 1;
4033  $this->entity = $conf->entity;
4034  $this->socid = 1;
4035  $this->date = time();
4036  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4037  $this->cond_reglement_code = 'RECEP';
4038  $this->mode_reglement_code = 'CHQ';
4039  $this->availability_code = 'DSP';
4040  $this->demand_reason_code = 'SRC_00';
4041 
4042  $this->note_public = 'This is a comment (public)';
4043  $this->note_private = 'This is a comment (private)';
4044 
4045  $this->multicurrency_tx = 1;
4046  $this->multicurrency_code = $conf->currency;
4047 
4048  // Lines
4049  $nbp = 5;
4050  $xnbp = 0;
4051  while ($xnbp < $nbp) {
4052  $line = new OrderLine($this->db);
4053 
4054  $line->desc = $langs->trans("Description")." ".$xnbp;
4055  $line->qty = 1;
4056  $line->subprice = 100;
4057  $line->price = 100;
4058  $line->tva_tx = 20;
4059  if ($xnbp == 2) {
4060  $line->total_ht = 50;
4061  $line->total_ttc = 60;
4062  $line->total_tva = 10;
4063  $line->remise_percent = 50;
4064  } else {
4065  $line->total_ht = 100;
4066  $line->total_ttc = 120;
4067  $line->total_tva = 20;
4068  $line->remise_percent = 0;
4069  }
4070  if ($num_prods > 0) {
4071  $prodid = mt_rand(1, $num_prods);
4072  $line->fk_product = $prodids[$prodid];
4073  $line->product_ref = 'SPECIMEN';
4074  }
4075 
4076  $this->lines[$xnbp] = $line;
4077 
4078  $this->total_ht += $line->total_ht;
4079  $this->total_tva += $line->total_tva;
4080  $this->total_ttc += $line->total_ttc;
4081 
4082  $xnbp++;
4083  }
4084  }
4085 
4086 
4087  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4093  public function load_state_board()
4094  {
4095  // phpcs:enable
4096  global $user;
4097 
4098  $this->nb = array();
4099  $clause = "WHERE";
4100 
4101  $sql = "SELECT count(co.rowid) as nb";
4102  $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
4103  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4104  if (empty($user->rights->societe->client->voir) && !$user->socid) {
4105  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4106  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4107  $clause = "AND";
4108  }
4109  $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4110 
4111  $resql = $this->db->query($sql);
4112  if ($resql) {
4113  while ($obj = $this->db->fetch_object($resql)) {
4114  $this->nb["orders"] = $obj->nb;
4115  }
4116  $this->db->free($resql);
4117  return 1;
4118  } else {
4119  dol_print_error($this->db);
4120  $this->error = $this->db->error();
4121  return -1;
4122  }
4123  }
4124 
4130  public function getLinesArray()
4131  {
4132  return $this->fetch_lines();
4133  }
4134 
4146  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4147  {
4148  global $conf, $langs;
4149 
4150  $langs->load("orders");
4151  $outputlangs->load("products");
4152 
4153  if (!dol_strlen($modele)) {
4154  $modele = 'einstein';
4155 
4156  if (!empty($this->model_pdf)) {
4157  $modele = $this->model_pdf;
4158  } elseif (!empty($this->modelpdf)) { // deprecated
4159  $modele = $this->modelpdf;
4160  } elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
4161  $modele = $conf->global->COMMANDE_ADDON_PDF;
4162  }
4163  }
4164 
4165  $modelpath = "core/modules/commande/doc/";
4166 
4167  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4168  }
4169 
4170 
4179  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4180  {
4181  $tables = array(
4182  'commande'
4183  );
4184 
4185  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4186  }
4187 
4196  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4197  {
4198  $tables = array(
4199  'commandedet',
4200  );
4201 
4202  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4203  }
4204 
4210  public function hasDelay()
4211  {
4212  global $conf;
4213 
4214  if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4215  return false; // Never late if not inside this status range
4216  }
4217 
4218  $now = dol_now();
4219 
4220  return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4221  }
4222 
4228  public function showDelay()
4229  {
4230  global $conf, $langs;
4231 
4232  if (empty($this->delivery_date)) {
4233  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
4234  } else {
4235  $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
4236  }
4237  $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4238 
4239  return $text;
4240  }
4241 }
4242 
4243 
4248 {
4252  public $element = 'commandedet';
4253 
4254  public $table_element = 'commandedet';
4255 
4256  public $oldline;
4257 
4262  public $fk_commande;
4263 
4270  public $commande_id;
4271 
4272  public $fk_parent_line;
4273 
4277  public $fk_facture;
4278 
4282  public $ref_ext;
4283 
4284  public $fk_remise_except;
4285 
4289  public $rang = 0;
4290  public $fk_fournprice;
4291 
4296  public $pa_ht;
4297  public $marge_tx;
4298  public $marque_tx;
4299 
4304  public $remise;
4305 
4306  // Start and end date of the line
4307  public $date_start;
4308  public $date_end;
4309 
4310  public $skip_update_total; // Skip update price total for special lines
4311 
4312 
4318  public function __construct($db)
4319  {
4320  $this->db = $db;
4321  }
4322 
4329  public function fetch($rowid)
4330  {
4331  $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,';
4332  $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4333  $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,';
4334  $sql .= ' cd.fk_unit,';
4335  $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4336  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4337  $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4338  $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4339  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4340  $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4341  $result = $this->db->query($sql);
4342  if ($result) {
4343  $objp = $this->db->fetch_object($result);
4344 
4345  if (!$objp) {
4346  $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4347  return 0;
4348  }
4349 
4350  $this->rowid = $objp->rowid;
4351  $this->id = $objp->rowid;
4352  $this->fk_commande = $objp->fk_commande;
4353  $this->fk_parent_line = $objp->fk_parent_line;
4354  $this->label = $objp->custom_label;
4355  $this->desc = $objp->description;
4356  $this->qty = $objp->qty;
4357  $this->price = $objp->price;
4358  $this->subprice = $objp->subprice;
4359  $this->ref_ext = $objp->ref_ext;
4360  $this->vat_src_code = $objp->vat_src_code;
4361  $this->tva_tx = $objp->tva_tx;
4362  $this->localtax1_tx = $objp->localtax1_tx;
4363  $this->localtax2_tx = $objp->localtax2_tx;
4364  $this->remise = $objp->remise;
4365  $this->remise_percent = $objp->remise_percent;
4366  $this->fk_remise_except = $objp->fk_remise_except;
4367  $this->fk_product = $objp->fk_product;
4368  $this->product_type = $objp->product_type;
4369  $this->info_bits = $objp->info_bits;
4370  $this->special_code = $objp->special_code;
4371  $this->total_ht = $objp->total_ht;
4372  $this->total_tva = $objp->total_tva;
4373  $this->total_localtax1 = $objp->total_localtax1;
4374  $this->total_localtax2 = $objp->total_localtax2;
4375  $this->total_ttc = $objp->total_ttc;
4376  $this->fk_fournprice = $objp->fk_fournprice;
4377  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4378  $this->pa_ht = $marginInfos[0];
4379  $this->marge_tx = $marginInfos[1];
4380  $this->marque_tx = $marginInfos[2];
4381  $this->special_code = $objp->special_code;
4382  $this->rang = $objp->rang;
4383 
4384  $this->ref = $objp->product_ref; // deprecated
4385 
4386  $this->product_ref = $objp->product_ref;
4387  $this->product_label = $objp->product_label;
4388  $this->product_desc = $objp->product_desc;
4389  $this->product_tobatch = $objp->product_tobatch;
4390  $this->fk_unit = $objp->fk_unit;
4391 
4392  $this->date_start = $this->db->jdate($objp->date_start);
4393  $this->date_end = $this->db->jdate($objp->date_end);
4394 
4395  $this->fk_multicurrency = $objp->fk_multicurrency;
4396  $this->multicurrency_code = $objp->multicurrency_code;
4397  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4398  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4399  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4400  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4401 
4402  $this->db->free($result);
4403 
4404  return 1;
4405  } else {
4406  $this->error = $this->db->lasterror();
4407  return -1;
4408  }
4409  }
4410 
4418  public function delete(User $user, $notrigger = 0)
4419  {
4420  global $conf, $langs;
4421 
4422  $error = 0;
4423 
4424  if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4425  $this->id = $this->rowid;
4426  }
4427 
4428  // check if order line is not in a shipment line before deleting
4429  $sqlCheckShipmentLine = "SELECT";
4430  $sqlCheckShipmentLine .= " ed.rowid";
4431  $sqlCheckShipmentLine .= " FROM ".MAIN_DB_PREFIX."expeditiondet ed";
4432  $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = ".((int) $this->id);
4433 
4434  $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4435  if (!$resqlCheckShipmentLine) {
4436  $error++;
4437  $this->error = $this->db->lasterror();
4438  $this->errors[] = $this->error;
4439  } else {
4440  $langs->load('errors');
4441  $num = $this->db->num_rows($resqlCheckShipmentLine);
4442  if ($num > 0) {
4443  $error++;
4444  $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4445  $this->error = $langs->trans('ErrorRecordAlreadyExists').' : '.$langs->trans('ShipmentLine').' '.$objCheckShipmentLine->rowid;
4446  $this->errors[] = $this->error;
4447  }
4448  $this->db->free($resqlCheckShipmentLine);
4449  }
4450  if ($error) {
4451  dol_syslog(__METHOD__.'Error ; '.$this->error, LOG_ERR);
4452  return -1;
4453  }
4454 
4455  $this->db->begin();
4456 
4457  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid = ".((int) $this->id);
4458 
4459  dol_syslog("OrderLine::delete", LOG_DEBUG);
4460  $resql = $this->db->query($sql);
4461  if ($resql) {
4462  if (!$error && !$notrigger) {
4463  // Call trigger
4464  $result = $this->call_trigger('LINEORDER_DELETE', $user);
4465  if ($result < 0) {
4466  $error++;
4467  }
4468  // End call triggers
4469  }
4470 
4471  // Remove extrafields
4472  if (!$error) {
4473  $result = $this->deleteExtraFields();
4474  if ($result < 0) {
4475  $error++;
4476  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4477  }
4478  }
4479 
4480  if (!$error) {
4481  $this->db->commit();
4482  return 1;
4483  }
4484 
4485  foreach ($this->errors as $errmsg) {
4486  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4487  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4488  }
4489  $this->db->rollback();
4490  return -1 * $error;
4491  } else {
4492  $this->error = $this->db->lasterror();
4493  return -1;
4494  }
4495  }
4496 
4504  public function insert($user = null, $notrigger = 0)
4505  {
4506  global $langs, $conf;
4507 
4508  $error = 0;
4509 
4510  $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'.
4511 
4512  dol_syslog(get_class($this)."::insert rang=".$this->rang);
4513 
4514  // Clean parameters
4515  if (empty($this->tva_tx)) {
4516  $this->tva_tx = 0;
4517  }
4518  if (empty($this->localtax1_tx)) {
4519  $this->localtax1_tx = 0;
4520  }
4521  if (empty($this->localtax2_tx)) {
4522  $this->localtax2_tx = 0;
4523  }
4524  if (empty($this->localtax1_type)) {
4525  $this->localtax1_type = 0;
4526  }
4527  if (empty($this->localtax2_type)) {
4528  $this->localtax2_type = 0;
4529  }
4530  if (empty($this->total_localtax1)) {
4531  $this->total_localtax1 = 0;
4532  }
4533  if (empty($this->total_localtax2)) {
4534  $this->total_localtax2 = 0;
4535  }
4536  if (empty($this->rang)) {
4537  $this->rang = 0;
4538  }
4539  if (empty($this->remise_percent)) {
4540  $this->remise_percent = 0;
4541  }
4542  if (empty($this->info_bits)) {
4543  $this->info_bits = 0;
4544  }
4545  if (empty($this->special_code)) {
4546  $this->special_code = 0;
4547  }
4548  if (empty($this->fk_parent_line)) {
4549  $this->fk_parent_line = 0;
4550  }
4551  if (empty($this->pa_ht)) {
4552  $this->pa_ht = 0;
4553  }
4554  if (empty($this->ref_ext)) {
4555  $this->ref_ext = '';
4556  }
4557 
4558  // if buy price not defined, define buyprice as configured in margin admin
4559  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4560  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4561  if ($result < 0) {
4562  return $result;
4563  } else {
4564  $this->pa_ht = $result;
4565  }
4566  }
4567 
4568  // Check parameters
4569  if ($this->product_type < 0) {
4570  return -1;
4571  }
4572 
4573  $this->db->begin();
4574 
4575  // Insertion dans base de la ligne
4576  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4577  $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4578  $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4579  $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4580  $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4581  $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4582  $sql .= ' fk_unit';
4583  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4584  $sql .= ')';
4585  $sql .= " VALUES (".$this->fk_commande.",";
4586  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4587  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4588  $sql .= " '".$this->db->escape($this->desc)."',";
4589  $sql .= " '".price2num($this->qty)."',";
4590  $sql .= " '".$this->db->escape($this->ref_ext)."',";
4591  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4592  $sql .= " '".price2num($this->tva_tx)."',";
4593  $sql .= " '".price2num($this->localtax1_tx)."',";
4594  $sql .= " '".price2num($this->localtax2_tx)."',";
4595  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4596  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4597  $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4598  $sql .= " '".$this->db->escape($this->product_type)."',";
4599  $sql .= " '".price2num($this->remise_percent)."',";
4600  $sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4601  $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4602  $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4603  $sql .= ' '.((int) $this->special_code).',';
4604  $sql .= ' '.((int) $this->rang).',';
4605  $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4606  $sql .= ' '.price2num($this->pa_ht).',';
4607  $sql .= " ".((int) $this->info_bits).",";
4608  $sql .= " ".price2num($this->total_ht, 'MT').",";
4609  $sql .= " ".price2num($this->total_tva, 'MT').",";
4610  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4611  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4612  $sql .= " ".price2num($this->total_ttc, 'MT').",";
4613  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4614  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4615  $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4616  $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4617  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4618  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4619  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4620  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4621  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4622  $sql .= ')';
4623 
4624  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4625  $resql = $this->db->query($sql);
4626  if ($resql) {
4627  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4628  $this->rowid = $this->id;
4629 
4630  if (!$error) {
4631  $result = $this->insertExtraFields();
4632  if ($result < 0) {
4633  $error++;
4634  }
4635  }
4636 
4637  if (!$error && !$notrigger) {
4638  // Call trigger
4639  $result = $this->call_trigger('LINEORDER_INSERT', $user);
4640  if ($result < 0) {
4641  $error++;
4642  }
4643  // End call triggers
4644  }
4645 
4646  if (!$error) {
4647  $this->db->commit();
4648  return 1;
4649  }
4650 
4651  foreach ($this->errors as $errmsg) {
4652  dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4653  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4654  }
4655  $this->db->rollback();
4656  return -1 * $error;
4657  } else {
4658  $this->error = $this->db->error();
4659  $this->db->rollback();
4660  return -2;
4661  }
4662  }
4663 
4671  public function update(User $user, $notrigger = 0)
4672  {
4673  global $conf, $langs;
4674 
4675  $error = 0;
4676 
4677  $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'.
4678 
4679  // Clean parameters
4680  if (empty($this->tva_tx)) {
4681  $this->tva_tx = 0;
4682  }
4683  if (empty($this->localtax1_tx)) {
4684  $this->localtax1_tx = 0;
4685  }
4686  if (empty($this->localtax2_tx)) {
4687  $this->localtax2_tx = 0;
4688  }
4689  if (empty($this->localtax1_type)) {
4690  $this->localtax1_type = 0;
4691  }
4692  if (empty($this->localtax2_type)) {
4693  $this->localtax2_type = 0;
4694  }
4695  if (empty($this->qty)) {
4696  $this->qty = 0;
4697  }
4698  if (empty($this->total_localtax1)) {
4699  $this->total_localtax1 = 0;
4700  }
4701  if (empty($this->total_localtax2)) {
4702  $this->total_localtax2 = 0;
4703  }
4704  if (empty($this->marque_tx)) {
4705  $this->marque_tx = 0;
4706  }
4707  if (empty($this->marge_tx)) {
4708  $this->marge_tx = 0;
4709  }
4710  if (empty($this->remise_percent)) {
4711  $this->remise_percent = 0;
4712  }
4713  if (empty($this->remise)) {
4714  $this->remise = 0;
4715  }
4716  if (empty($this->info_bits)) {
4717  $this->info_bits = 0;
4718  }
4719  if (empty($this->special_code)) {
4720  $this->special_code = 0;
4721  }
4722  if (empty($this->product_type)) {
4723  $this->product_type = 0;
4724  }
4725  if (empty($this->fk_parent_line)) {
4726  $this->fk_parent_line = 0;
4727  }
4728  if (empty($this->pa_ht)) {
4729  $this->pa_ht = 0;
4730  }
4731  if (empty($this->ref_ext)) {
4732  $this->ref_ext = '';
4733  }
4734 
4735  // if buy price not defined, define buyprice as configured in margin admin
4736  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4737  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4738  if ($result < 0) {
4739  return $result;
4740  } else {
4741  $this->pa_ht = $result;
4742  }
4743  }
4744 
4745  $this->db->begin();
4746 
4747  // Mise a jour ligne en base
4748  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4749  $sql .= " description='".$this->db->escape($this->desc)."'";
4750  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4751  $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4752  $sql .= " , tva_tx=".price2num($this->tva_tx);
4753  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4754  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4755  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4756  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4757  $sql .= " , qty=".price2num($this->qty);
4758  $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4759  $sql .= " , subprice=".price2num($this->subprice);
4760  $sql .= " , remise_percent=".price2num($this->remise_percent);
4761  $sql .= " , price=".price2num($this->price); // TODO A virer
4762  $sql .= " , remise=".price2num($this->remise); // TODO A virer
4763  if (empty($this->skip_update_total)) {
4764  $sql .= " , total_ht=".price2num($this->total_ht);
4765  $sql .= " , total_tva=".price2num($this->total_tva);
4766  $sql .= " , total_ttc=".price2num($this->total_ttc);
4767  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4768  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4769  }
4770  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4771  $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4772  $sql .= " , info_bits=".((int) $this->info_bits);
4773  $sql .= " , special_code=".((int) $this->special_code);
4774  $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4775  $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4776  $sql .= " , product_type=".$this->product_type;
4777  $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4778  if (!empty($this->rang)) {
4779  $sql .= ", rang=".((int) $this->rang);
4780  }
4781  $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4782 
4783  // Multicurrency
4784  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4785  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4786  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4787  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4788 
4789  $sql .= " WHERE rowid = ".((int) $this->rowid);
4790 
4791  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4792  $resql = $this->db->query($sql);
4793  if ($resql) {
4794  if (!$error) {
4795  $this->id = $this->rowid;
4796  $result = $this->insertExtraFields();
4797  if ($result < 0) {
4798  $error++;
4799  }
4800  }
4801 
4802  if (!$error && !$notrigger) {
4803  // Call trigger
4804  $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4805  if ($result < 0) {
4806  $error++;
4807  }
4808  // End call triggers
4809  }
4810 
4811  if (!$error) {
4812  $this->db->commit();
4813  return 1;
4814  }
4815 
4816  foreach ($this->errors as $errmsg) {
4817  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4818  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4819  }
4820  $this->db->rollback();
4821  return -1 * $error;
4822  } else {
4823  $this->error = $this->db->error();
4824  $this->db->rollback();
4825  return -2;
4826  }
4827  }
4828 
4829  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4836  public function update_total()
4837  {
4838  // phpcs:enable
4839  $this->db->begin();
4840 
4841  // Clean parameters
4842  if (empty($this->total_localtax1)) {
4843  $this->total_localtax1 = 0;
4844  }
4845  if (empty($this->total_localtax2)) {
4846  $this->total_localtax2 = 0;
4847  }
4848 
4849  // Mise a jour ligne en base
4850  $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4851  $sql .= " total_ht='".price2num($this->total_ht)."'";
4852  $sql .= ",total_tva='".price2num($this->total_tva)."'";
4853  $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4854  $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4855  $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4856  $sql .= " WHERE rowid = ".((int) $this->rowid);
4857 
4858  dol_syslog("OrderLine::update_total", LOG_DEBUG);
4859 
4860  $resql = $this->db->query($sql);
4861  if ($resql) {
4862  $this->db->commit();
4863  return 1;
4864  } else {
4865  $this->error = $this->db->error();
4866  $this->db->rollback();
4867  return -2;
4868  }
4869  }
4870 }
$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.
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.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
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:1485
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:1334
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:1537
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