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