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