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