dolibarr 21.0.3
commande.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2011 Jean Heimburger <jean@tiaris.info>
8 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
9 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
10 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
11 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
12 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
13 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2021-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
17 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
40require_once DOL_DOCUMENT_ROOT.'/commande/class/orderline.class.php';
41require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
42require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
43require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
44require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
45
46
50class Commande extends CommonOrder
51{
55 public $element = 'commande';
56
60 public $table_element = 'commande';
61
65 public $table_element_line = 'commandedet';
66
70 public $class_element_line = 'OrderLine';
71
75 public $fk_element = 'fk_commande';
76
80 public $picto = 'order';
81
86 public $restrictiononfksoc = 1;
87
91 protected $table_ref_field = 'ref';
92
96 public $socid;
97
101 public $ref_client;
102
106 public $ref_customer;
107
111 public $contactid;
112
118 public $statut;
119
124 public $status;
125
129 public $billed;
130
134 public $date_lim_reglement;
138 public $cond_reglement_code;
139
143 public $cond_reglement_doc;
144
150 public $deposit_percent;
151
155 public $fk_account;
156
160 public $mode_reglement;
161
165 public $mode_reglement_id;
166
170 public $mode_reglement_code;
171
176 public $availability_id;
177
182 public $availability_code;
183
188 public $availability;
189
193 public $demand_reason_id;
194
198 public $demand_reason_code;
199
203 public $date;
204
210 public $date_commande;
211
215 public $delivery_date;
216
220 public $fk_remise_except;
221
226 public $remise_percent;
227
231 public $source; // Order mode. How we received order (by phone, by email, ...)
232
237 public $signed_status = 0;
238
242 public $warehouse_id;
243
247 public $extraparams = array();
248
249 public $linked_objects = array();
250
254 public $user_author_id;
255
259 public $line;
260
264 public $lines = array();
265
266
270 public $module_source;
274 public $pos_source;
275
279 public $expeditions;
280
284 public $online_payment_url;
285
286
287
312 // BEGIN MODULEBUILDER PROPERTIES
316 public $fields = array(
317 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
318 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
319 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 25),
320 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 26),
321 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 28),
322 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 20),
323 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 25),
324 'date_commande' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'csslist' => 'nowraponall'),
325 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 62, 'csslist' => 'nowraponall'),
326 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'csslist' => 'nowraponall'),
327 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 85),
328 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => -1, 'position' => 90),
329 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => -1, 'position' => 95),
330 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
331 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
332 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
333 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
334 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
335 'signed_status' => array('type' => 'smallint(6)', 'label' => 'SignedStatus', 'enabled' => 1, 'visible' => -1, 'position' => 146, 'arrayofkeyval' => array(0 => 'NoSignature', 1 => 'SignedSender', 2 => 'SignedReceiver', 9 => 'SignedAll')),
336 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 150),
337 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 155),
338 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 160),
339 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 170),
340 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 175),
341 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 180),
342 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 181),
343 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 185),
344 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'csslist' => 'nowraponall'),
345 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 195),
346 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'DefaultWarehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 200, 'nodepth' => 1),
347 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 205),
348 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 210),
349 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
350 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 225),
351 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 230),
352 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 235),
353 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
354 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245),
355 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
356 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
357 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 260, 'isameasure' => 1),
358 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 265, 'isameasure' => 1),
359 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 270),
360 'module_source' => array('type' => 'varchar(32)', 'label' => 'POSModule', 'enabled' => 1, 'visible' => -1, 'position' => 275),
361 'pos_source' => array('type' => 'varchar(32)', 'label' => 'POSTerminal', 'enabled' => 1, 'visible' => -1, 'position' => 280),
362 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 300),
363 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 302),
364 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 304, 'csslist' => 'nowraponall'),
365 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 306),
366 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 400),
367 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'position' => 500),
368 );
369 // END MODULEBUILDER PROPERTIES
370
375
379 const STATUS_CANCELED = -1;
383 const STATUS_DRAFT = 0;
391 const STATUS_SHIPMENTONPROCESS = 2; // We set this status when a shipment is validated
392
398
402 const STATUS_CLOSED = 3;
403
404 /*
405 * No signature
406 */
407 const STATUS_NO_SIGNATURE = 0;
408
409 /*
410 * Signed by sender
411 */
412 const STATUS_SIGNED_SENDER = 1;
413
414 /*
415 * Signed by receiver
416 */
417 const STATUS_SIGNED_RECEIVER = 2;
418
419 /*
420 * Signed by all
421 */
422 const STATUS_SIGNED_ALL = 9; // To handle future kind of signature (ex: tripartite contract)
423
424
430 public function __construct($db)
431 {
432 $this->db = $db;
433
434 $this->ismultientitymanaged = 1;
435 $this->isextrafieldmanaged = 1;
436
437 $this->fields['ref_ext']['visible'] = getDolGlobalInt('MAIN_LIST_SHOW_REF_EXT');
438 }
439
447 public function getNextNumRef($soc)
448 {
449 global $langs, $conf;
450 $langs->load("order");
451
452 if (getDolGlobalString('COMMANDE_ADDON')) {
453 $mybool = false;
454
455 $file = getDolGlobalString('COMMANDE_ADDON') . ".php";
456 $classname = getDolGlobalString('COMMANDE_ADDON');
457
458 // Include file with class
459 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
460 foreach ($dirmodels as $reldir) {
461 $dir = dol_buildpath($reldir."core/modules/commande/");
462
463 // Load file with numbering class (if found)
464 $mybool = ((bool) @include_once $dir.$file) || $mybool;
465 }
466
467 if (!$mybool) {
468 dol_print_error(null, "Failed to include file ".$file);
469 return '';
470 }
471
472 $obj = new $classname();
473 '@phan-var-force ModeleNumRefCommandes $obj';
474
475 $numref = $obj->getNextValue($soc, $this);
476
477 if ($numref != "") {
478 return $numref;
479 } else {
480 $this->error = $obj->error;
481 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
482 return "";
483 }
484 } else {
485 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
486 return "";
487 }
488 }
489
490
499 public function valid($user, $idwarehouse = 0, $notrigger = 0)
500 {
501 global $conf, $langs;
502
503 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
504
505 $error = 0;
506
507 // Protection
508 if ($this->statut == self::STATUS_VALIDATED) {
509 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
510 return 0;
511 }
512
513 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
514 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
515 $this->error = 'NotEnoughPermissions';
516 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
517 return -1;
518 }
519
520 $now = dol_now();
521
522 $this->db->begin();
523
524 // Definition du nom de module de numerotation de commande
525 $soc = new Societe($this->db);
526 $soc->fetch($this->socid);
527
528 // Class of company linked to order
529 $result = $soc->setAsCustomer();
530
531 // Define new ref
532 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
533 $num = $this->getNextNumRef($soc);
534 } else {
535 $num = $this->ref;
536 }
537 $this->newref = dol_sanitizeFileName($num);
538
539 // Validate
540 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
541 $sql .= " SET ref = '".$this->db->escape($num)."',";
542 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
543 $sql .= " date_valid='".$this->db->idate($now)."',";
544 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
545 $sql .= " fk_user_modif = ".((int) $user->id);
546 $sql .= " WHERE rowid = ".((int) $this->id);
547
548 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
549 $resql = $this->db->query($sql);
550 if (!$resql) {
551 dol_print_error($this->db);
552 $this->error = $this->db->lasterror();
553 $error++;
554 }
555
556 if (!$error) {
557 // If stock is incremented on validate order, we must increment it
558 if ($result >= 0 && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
559 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
560 $langs->load("agenda");
561
562 // Loop on each line
563 $cpt = count($this->lines);
564 for ($i = 0; $i < $cpt; $i++) {
565 if ($this->lines[$i]->fk_product > 0) {
566 $mouvP = new MouvementStock($this->db);
567 $mouvP->origin = &$this;
568 $mouvP->setOrigin($this->element, $this->id);
569 // We decrement stock of product (and sub-products)
570 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
571 if ($result < 0) {
572 $error++;
573 $this->error = $mouvP->error;
574 }
575 }
576 if ($error) {
577 break;
578 }
579 }
580 }
581 }
582
583 if (!$error && !$notrigger) {
584 // Call trigger
585 $result = $this->call_trigger('ORDER_VALIDATE', $user);
586 if ($result < 0) {
587 $error++;
588 }
589 // End call triggers
590 }
591
592 if (!$error) {
593 $this->oldref = $this->ref;
594
595 // Rename directory if dir was a temporary ref
596 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
597 // Now we rename also files into index
598 $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)."'";
599 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
600 $resql = $this->db->query($sql);
601 if (!$resql) {
602 $error++;
603 $this->error = $this->db->lasterror();
604 }
605 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
606 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
607 $resql = $this->db->query($sql);
608 if (!$resql) {
609 $error++;
610 $this->error = $this->db->lasterror();
611 }
612
613 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
614 $oldref = dol_sanitizeFileName($this->ref);
615 $newref = dol_sanitizeFileName($num);
616 $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
617 $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
618 if (!$error && file_exists($dirsource)) {
619 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
620
621 if (@rename($dirsource, $dirdest)) {
622 dol_syslog("Rename ok");
623 // Rename docs starting with $oldref with $newref
624 $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
625 foreach ($listoffiles as $fileentry) {
626 $dirsource = $fileentry['name'];
627 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
628 $dirsource = $fileentry['path'].'/'.$dirsource;
629 $dirdest = $fileentry['path'].'/'.$dirdest;
630 @rename($dirsource, $dirdest);
631 }
632 }
633 }
634 }
635 }
636
637 // Set new ref and current status
638 if (!$error) {
639 $this->ref = $num;
640 $this->statut = self::STATUS_VALIDATED; // deprecated
642 }
643
644 if (!$error) {
645 $this->db->commit();
646 return 1;
647 } else {
648 $this->db->rollback();
649 return -1;
650 }
651 }
652
653 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
661 public function setDraft($user, $idwarehouse = -1)
662 {
663 //phpcs:enable
664 global $conf, $langs;
665
666 $error = 0;
667
668 // Protection
669 if ($this->statut <= self::STATUS_DRAFT && !getDolGlobalInt('ORDER_REOPEN_TO_DRAFT')) {
670 return 0;
671 }
672
673 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
674 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
675 $this->error = 'Permission denied';
676 return -1;
677 }
678
679 dol_syslog(__METHOD__, LOG_DEBUG);
680
681 $this->db->begin();
682
683 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
684 $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
685 $sql .= " fk_user_modif = ".((int) $user->id);
686 $sql .= " WHERE rowid = ".((int) $this->id);
687
688 if ($this->db->query($sql)) {
689 if (!$error) {
690 $this->oldcopy = clone $this;
691 }
692
693 // If stock is decremented on validate order, we must reincrement it
694 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
695 $result = 0;
696
697 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
698 $langs->load("agenda");
699
700 $num = count($this->lines);
701 for ($i = 0; $i < $num; $i++) {
702 if ($this->lines[$i]->fk_product > 0) {
703 $mouvP = new MouvementStock($this->db);
704 $mouvP->origin = &$this;
705 $mouvP->setOrigin($this->element, $this->id);
706 // We increment stock of product (and sub-products)
707 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
708 if ($result < 0) {
709 $error++;
710 $this->error = $mouvP->error;
711 break;
712 }
713 }
714 }
715 }
716
717 if (!$error) {
718 // Call trigger
719 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
720 if ($result < 0) {
721 $error++;
722 }
723 }
724
725 if (!$error) {
726 $this->statut = self::STATUS_DRAFT;
727 $this->db->commit();
728 return 1;
729 } else {
730 $this->db->rollback();
731 return -1;
732 }
733 } else {
734 $this->error = $this->db->error();
735 $this->db->rollback();
736 return -1;
737 }
738 }
739
740
741 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
749 public function set_reopen($user)
750 {
751 // phpcs:enable
752 $error = 0;
753
754 if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
755 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
756 return 0;
757 }
758
759 $this->db->begin();
760
761 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
762 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
763 $sql .= " fk_user_modif = ".((int) $user->id);
764 $sql .= " WHERE rowid = ".((int) $this->id);
765
766 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
767 $resql = $this->db->query($sql);
768 if ($resql) {
769 // Call trigger
770 $result = $this->call_trigger('ORDER_REOPEN', $user);
771 if ($result < 0) {
772 $error++;
773 }
774 // End call triggers
775 } else {
776 $error++;
777 $this->error = $this->db->lasterror();
778 dol_print_error($this->db);
779 }
780
781 if (!$error) {
782 $this->statut = self::STATUS_VALIDATED;
783 $this->billed = 0;
784
785 $this->db->commit();
786 return 1;
787 } else {
788 foreach ($this->errors as $errmsg) {
789 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
790 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
791 }
792 $this->db->rollback();
793 return -1 * $error;
794 }
795 }
796
804 public function cloture($user, $notrigger = 0)
805 {
806 $error = 0;
807
808 $usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
809 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'close')));
810
811 if ($usercanclose) {
812 if ($this->statut == self::STATUS_CLOSED) {
813 return 0;
814 }
815 $this->db->begin();
816
817 $now = dol_now();
818
819 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
820 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
821 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
822 $sql .= " date_cloture = '".$this->db->idate($now)."',";
823 $sql .= " fk_user_modif = ".((int) $user->id);
824 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
825
826 if ($this->db->query($sql)) {
827 if (!$notrigger) {
828 // Call trigger
829 $result = $this->call_trigger('ORDER_CLOSE', $user);
830 if ($result < 0) {
831 $error++;
832 }
833 // End call triggers
834 }
835
836 if (!$error) {
837 $this->statut = self::STATUS_CLOSED;
838
839 $this->db->commit();
840 return 1;
841 } else {
842 $this->db->rollback();
843 return -1;
844 }
845 } else {
846 $this->error = $this->db->lasterror();
847
848 $this->db->rollback();
849 return -1;
850 }
851 }
852 return 0;
853 }
854
862 public function cancel($idwarehouse = -1)
863 {
864 global $user, $langs;
865
866 $error = 0;
867
868 $this->db->begin();
869
870 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
871 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
872 $sql .= " fk_user_modif = ".((int) $user->id);
873 $sql .= " WHERE rowid = ".((int) $this->id);
874 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
875
876 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
877 if ($this->db->query($sql)) {
878 // If stock is decremented on validate order, we must reincrement it
879 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
880 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
881 $langs->load("agenda");
882
883 $num = count($this->lines);
884 for ($i = 0; $i < $num; $i++) {
885 if ($this->lines[$i]->fk_product > 0) {
886 $mouvP = new MouvementStock($this->db);
887 $mouvP->setOrigin($this->element, $this->id);
888 // We increment stock of product (and sub-products)
889 $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
890 if ($result < 0) {
891 $error++;
892 $this->error = $mouvP->error;
893 break;
894 }
895 }
896 }
897 }
898
899 if (!$error) {
900 // Call trigger
901 $result = $this->call_trigger('ORDER_CANCEL', $user);
902 if ($result < 0) {
903 $error++;
904 }
905 // End call triggers
906 }
907
908 if (!$error) {
909 $this->statut = self::STATUS_CANCELED;
910 $this->db->commit();
911 return 1;
912 } else {
913 foreach ($this->errors as $errmsg) {
914 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
915 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
916 }
917 $this->db->rollback();
918 return -1 * $error;
919 }
920 } else {
921 $this->error = $this->db->error();
922 $this->db->rollback();
923 return -1;
924 }
925 }
926
935 public function create($user, $notrigger = 0)
936 {
937 global $conf, $langs, $mysoc;
938 $error = 0;
939
940 // Clean parameters
941
942 // Set tmp vars
943 $date = ($this->date_commande ? $this->date_commande : $this->date);
944 $delivery_date = $this->delivery_date;
945
946 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
947 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
948 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
949 } else {
950 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
951 }
952 if (empty($this->fk_multicurrency)) {
953 $this->multicurrency_code = $conf->currency;
954 $this->fk_multicurrency = 0;
955 $this->multicurrency_tx = 1;
956 }
957 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
958 $this->entity = setEntity($this);
959
960 dol_syslog(get_class($this)."::create user=".$user->id);
961
962 // Check parameters
963 if (!empty($this->ref)) { // We check that ref is not already used
964 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
965 if ($result > 0) {
966 $this->error = 'ErrorRefAlreadyExists';
967 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
968 $this->db->rollback();
969 return -1;
970 }
971 }
972
973 $soc = new Societe($this->db);
974 $result = $soc->fetch($this->socid);
975 if ($result < 0) {
976 $this->error = "Failed to fetch company";
977 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
978 return -2;
979 }
980 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
981 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
982 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
983 return -1;
984 }
985
986 $now = dol_now();
987
988 $this->db->begin();
989
990 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
991 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
992 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
993 $sql .= ", fk_shipping_method";
994 $sql .= ", fk_warehouse";
995 $sql .= ", fk_incoterms, location_incoterms";
996 $sql .= ", entity, module_source, pos_source";
997 $sql .= ", fk_multicurrency";
998 $sql .= ", multicurrency_code";
999 $sql .= ", multicurrency_tx";
1000 $sql .= ")";
1001 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
1002 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1003 $sql .= ", '".$this->db->idate($date)."'";
1004 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
1005 $sql .= ", '".$this->db->escape($this->note_private)."'";
1006 $sql .= ", '".$this->db->escape($this->note_public)."'";
1007 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
1008 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1009 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1010 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1011 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1012 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1013 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1014 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1015 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1016 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1017 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1018 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1019 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1020 $sql .= ", ".(int) $this->fk_incoterms;
1021 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1022 $sql .= ", ".(int) $this->entity;
1023 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1024 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1025 $sql .= ", ".(int) $this->fk_multicurrency;
1026 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1027 $sql .= ", ".(float) $this->multicurrency_tx;
1028 $sql .= ")";
1029
1030 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1031 $resql = $this->db->query($sql);
1032 if ($resql) {
1033 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1034
1035 if ($this->id) {
1036 $fk_parent_line = 0;
1037 $num = count($this->lines);
1038
1039 /*
1040 * Insert products details into db
1041 */
1042 for ($i = 0; $i < $num; $i++) {
1043 $line = $this->lines[$i];
1044
1045 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1046 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1047 if (!is_object($line)) {
1048 $line = (object) $line;
1049 }
1050
1051 // Reset fk_parent_line for no child products and special product
1052 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1053 $fk_parent_line = 0;
1054 }
1055
1056 // Complete vat rate with code
1057 $vatrate = $line->tva_tx;
1058 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1059 $vatrate .= ' ('.$line->vat_src_code.')';
1060 }
1061
1062 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1063 $originid = $line->origin_id;
1064 $origintype = $line->origin;
1065 } else {
1066 $originid = $line->id;
1067 $origintype = $this->element;
1068 }
1069
1070 // ref_ext
1071 if (empty($line->ref_ext)) {
1072 $line->ref_ext = '';
1073 }
1074
1075 $result = $this->addline(
1076 $line->desc,
1077 $line->subprice,
1078 $line->qty,
1079 $vatrate,
1080 $line->localtax1_tx,
1081 $line->localtax2_tx,
1082 $line->fk_product,
1083 $line->remise_percent,
1084 $line->info_bits,
1085 $line->fk_remise_except,
1086 'HT',
1087 0,
1088 $line->date_start,
1089 $line->date_end,
1090 $line->product_type,
1091 $line->rang,
1092 $line->special_code,
1093 $fk_parent_line,
1094 $line->fk_fournprice,
1095 $line->pa_ht,
1096 $line->label,
1097 $line->array_options,
1098 $line->fk_unit,
1099 $origintype,
1100 $originid,
1101 0,
1102 $line->ref_ext,
1103 1
1104 );
1105
1106 if ($result < 0) {
1107 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1108 $this->error = $this->db->lasterror();
1109 $this->errors[] = $this->error;
1110 dol_print_error($this->db);
1111 }
1112 $this->db->rollback();
1113 return -1;
1114 }
1115 // Defined the new fk_parent_line
1116 if ($result > 0 && $line->product_type == 9) {
1117 $fk_parent_line = $result;
1118 }
1119 }
1120
1121 $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.
1122
1123 // update ref
1124 $initialref = '(PROV'.$this->id.')';
1125 if (!empty($this->ref)) {
1126 $initialref = $this->ref;
1127 }
1128
1129 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1130 if ($this->db->query($sql)) {
1131 $this->ref = $initialref;
1132
1133 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1134 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1135 }
1136
1137 // Add object linked
1138 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1139 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1140 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, ...))
1141 foreach ($tmp_origin_id as $origin_id) {
1142 $ret = $this->add_object_linked($origin, $origin_id);
1143 if (!$ret) {
1144 $this->error = $this->db->lasterror();
1145 $error++;
1146 }
1147 }
1148 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1149 $origin_id = $tmp_origin_id;
1150 $ret = $this->add_object_linked($origin, $origin_id);
1151 if (!$ret) {
1152 $this->error = $this->db->lasterror();
1153 $error++;
1154 }
1155 }
1156 }
1157 }
1158
1159 if (!$error && $this->id && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1160 $originforcontact = $this->origin;
1161 $originidforcontact = $this->origin_id;
1162 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1163 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1164 $exp = new Expedition($this->db);
1165 $exp->fetch($this->origin_id);
1166 $exp->fetchObjectLinked();
1167 if (count($exp->linkedObjectsIds['commande']) > 0) {
1168 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1169 $originforcontact = 'commande';
1170 if (is_object($value)) {
1171 $originidforcontact = $value->id;
1172 } else {
1173 $originidforcontact = $value;
1174 }
1175 break; // We take first one
1176 }
1177 }
1178 }
1179
1180 $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";
1181 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1182
1183 $resqlcontact = $this->db->query($sqlcontact);
1184 if ($resqlcontact) {
1185 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1186 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1187 $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
1188 }
1189 } else {
1190 dol_print_error($this->db);
1191 }
1192 }
1193
1194 if (!$error) {
1195 $result = $this->insertExtraFields();
1196 if ($result < 0) {
1197 $error++;
1198 }
1199 }
1200
1201 if (!$error && !$notrigger) {
1202 // Call trigger
1203 $result = $this->call_trigger('ORDER_CREATE', $user);
1204 if ($result < 0) {
1205 $error++;
1206 }
1207 // End call triggers
1208 }
1209
1210 if (!$error) {
1211 $this->db->commit();
1212 return $this->id;
1213 } else {
1214 $this->db->rollback();
1215 return -1 * $error;
1216 }
1217 } else {
1218 $this->error = $this->db->lasterror();
1219 $this->db->rollback();
1220 return -1;
1221 }
1222 }
1223
1224 return 0;
1225 } else {
1226 $this->error = $this->db->lasterror();
1227 $this->db->rollback();
1228 return -1;
1229 }
1230 }
1231
1232
1240 public function createFromClone(User $user, $socid = 0)
1241 {
1242 global $conf, $user, $hookmanager;
1243
1244 $error = 0;
1245
1246 $this->db->begin();
1247
1248 // get lines so they will be clone
1249 foreach ($this->lines as $line) {
1250 $line->fetch_optionals();
1251 }
1252
1253 // Load source object
1254 $objFrom = clone $this;
1255
1256 // Change socid if needed
1257 if (!empty($socid) && $socid != $this->socid) {
1258 $objsoc = new Societe($this->db);
1259
1260 if ($objsoc->fetch($socid) > 0) {
1261 $this->socid = $objsoc->id;
1262 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1263 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1264 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1265 $this->fk_project = 0;
1266 $this->fk_delivery_address = 0;
1267 }
1268
1269 // TODO Change product price if multi-prices
1270 }
1271
1272 $this->id = 0;
1273 $this->ref = '';
1274 $this->statut = self::STATUS_DRAFT;
1275
1276 // Clear fields
1277 $this->user_author_id = $user->id;
1278 $this->user_validation_id = 0;
1279 $this->date = dol_now();
1280 $this->date_commande = dol_now();
1281 $this->date_creation = '';
1282 $this->date_validation = '';
1283 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1284 $this->ref_client = '';
1285 $this->ref_customer = '';
1286 }
1287
1288 // Do not clone ref_ext
1289 $num = count($this->lines);
1290 for ($i = 0; $i < $num; $i++) {
1291 $this->lines[$i]->ref_ext = '';
1292 }
1293
1294 // Create clone
1295 $this->context['createfromclone'] = 'createfromclone';
1296 $result = $this->create($user);
1297 if ($result < 0) {
1298 $error++;
1299 }
1300
1301 if (!$error) {
1302 // copy internal contacts
1303 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1304 $error++;
1305 }
1306 }
1307
1308 if (!$error) {
1309 // copy external contacts if same company
1310 if ($this->socid == $objFrom->socid) {
1311 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1312 $error++;
1313 }
1314 }
1315 }
1316
1317 if (!$error) {
1318 // Hook of thirdparty module
1319 if (is_object($hookmanager)) {
1320 $parameters = array('objFrom' => $objFrom);
1321 $action = '';
1322 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1323 if ($reshook < 0) {
1324 $this->setErrorsFromObject($hookmanager);
1325 $error++;
1326 }
1327 }
1328 }
1329
1330 unset($this->context['createfromclone']);
1331
1332 // End
1333 if (!$error) {
1334 $this->db->commit();
1335 return $this->id;
1336 } else {
1337 $this->db->rollback();
1338 return -1;
1339 }
1340 }
1341
1342
1350 public function createFromProposal($object, User $user)
1351 {
1352 global $conf, $hookmanager;
1353
1354 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1355 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1356
1357 $error = 0;
1358
1359 $this->date_commande = dol_now();
1360 $this->date = dol_now();
1361 $this->source = 0;
1362
1363 $num = count($object->lines);
1364 for ($i = 0; $i < $num; $i++) {
1365 $line = new OrderLine($this->db);
1366
1367 $line->libelle = $object->lines[$i]->libelle;
1368 $line->label = $object->lines[$i]->label;
1369 $line->desc = $object->lines[$i]->desc;
1370 $line->price = $object->lines[$i]->price;
1371 $line->subprice = $object->lines[$i]->subprice;
1372 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1373 $line->tva_tx = $object->lines[$i]->tva_tx;
1374 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1375 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1376 $line->qty = $object->lines[$i]->qty;
1377 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1378 $line->remise_percent = $object->lines[$i]->remise_percent;
1379 $line->fk_product = $object->lines[$i]->fk_product;
1380 $line->info_bits = $object->lines[$i]->info_bits;
1381 $line->product_type = $object->lines[$i]->product_type;
1382 $line->rang = $object->lines[$i]->rang;
1383 $line->special_code = $object->lines[$i]->special_code;
1384 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1385 $line->fk_unit = $object->lines[$i]->fk_unit;
1386
1387 $line->date_start = $object->lines[$i]->date_start;
1388 $line->date_end = $object->lines[$i]->date_end;
1389
1390 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1391 $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);
1392 $line->pa_ht = $marginInfos[0];
1393 $line->marge_tx = $marginInfos[1];
1394 $line->marque_tx = $marginInfos[2];
1395
1396 $line->origin = $object->element;
1397 $line->origin_id = $object->lines[$i]->id;
1398
1399 // get extrafields from original line
1400 $object->lines[$i]->fetch_optionals();
1401 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1402 $line->array_options[$options_key] = $value;
1403 }
1404
1405 $this->lines[$i] = $line;
1406 }
1407
1408 $this->entity = $object->entity;
1409 $this->socid = $object->socid;
1410 $this->fk_project = $object->fk_project;
1411 $this->cond_reglement_id = $object->cond_reglement_id;
1412 $this->deposit_percent = $object->deposit_percent;
1413 $this->mode_reglement_id = $object->mode_reglement_id;
1414 $this->fk_account = $object->fk_account;
1415 $this->availability_id = $object->availability_id;
1416 $this->demand_reason_id = $object->demand_reason_id;
1417 $this->delivery_date = $object->delivery_date;
1418 $this->shipping_method_id = $object->shipping_method_id;
1419 $this->warehouse_id = $object->warehouse_id;
1420 $this->fk_delivery_address = $object->fk_delivery_address;
1421 $this->contact_id = $object->contact_id;
1422 $this->ref_client = $object->ref_client;
1423 $this->ref_customer = $object->ref_client;
1424
1425 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1426 $this->note_private = $object->note_private;
1427 $this->note_public = $object->note_public;
1428 }
1429
1430 $this->origin = $object->element;
1431 $this->origin_id = $object->id;
1432
1433 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1434 if (isModEnabled('multicurrency')) {
1435 if (!empty($object->multicurrency_code)) {
1436 $this->multicurrency_code = $object->multicurrency_code;
1437 }
1438 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1439 $this->multicurrency_tx = $object->multicurrency_tx;
1440 }
1441
1442 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1443 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1444 $this->fk_multicurrency = $tmparray[0];
1445 $this->multicurrency_tx = $tmparray[1];
1446 } else {
1447 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1448 }
1449 if (empty($this->fk_multicurrency)) {
1450 $this->multicurrency_code = $conf->currency;
1451 $this->fk_multicurrency = 0;
1452 $this->multicurrency_tx = 1;
1453 }
1454 }
1455
1456 // get extrafields from original line
1457 $object->fetch_optionals();
1458
1459 $e = new ExtraFields($this->db);
1460 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1461
1462 foreach ($object->array_options as $options_key => $value) {
1463 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1464 $this->array_options[$options_key] = $value;
1465 }
1466 }
1467 // Possibility to add external linked objects with hooks
1468 $this->linked_objects[$this->origin] = $this->origin_id;
1469 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1470 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1471 }
1472
1473 $ret = $this->create($user);
1474
1475 if ($ret > 0) {
1476 // Actions hooked (by external module)
1477 $hookmanager->initHooks(array('orderdao'));
1478
1479 $parameters = array('objFrom' => $object);
1480 $action = '';
1481 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1482 if ($reshook < 0) {
1483 $this->setErrorsFromObject($hookmanager);
1484 $error++;
1485 }
1486
1487 if (!$error) {
1488 // Validate immediately the order
1489 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1490 $this->fetch($ret);
1491 $this->valid($user);
1492 }
1493 return $ret;
1494 } else {
1495 return -1;
1496 }
1497 } else {
1498 return -1;
1499 }
1500 }
1501
1502
1543 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)
1544 {
1545 global $mysoc, $langs, $user;
1546
1547 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1548 $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";
1549 $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 rang=$rang";
1550 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1551
1552 if ($this->statut == self::STATUS_DRAFT) {
1553 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1554
1555 // Clean parameters
1556
1557 if (empty($remise_percent)) {
1558 $remise_percent = 0;
1559 }
1560 if (empty($qty)) {
1561 $qty = 0;
1562 }
1563 if (empty($info_bits)) {
1564 $info_bits = 0;
1565 }
1566 if (empty($rang)) {
1567 $rang = 0;
1568 }
1569 if (empty($txtva)) {
1570 $txtva = 0;
1571 }
1572 if (empty($txlocaltax1)) {
1573 $txlocaltax1 = 0;
1574 }
1575 if (empty($txlocaltax2)) {
1576 $txlocaltax2 = 0;
1577 }
1578 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1579 $fk_parent_line = 0;
1580 }
1581 if (empty($this->fk_multicurrency)) {
1582 $this->fk_multicurrency = 0;
1583 }
1584 if (empty($ref_ext)) {
1585 $ref_ext = '';
1586 }
1587
1588 $remise_percent = (float) price2num($remise_percent);
1589 $qty = (float) price2num($qty);
1590 $pu_ht = price2num($pu_ht);
1591 $pu_ht_devise = price2num($pu_ht_devise);
1592 $pu_ttc = price2num($pu_ttc);
1593 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
1594 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1595 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1596 }
1597 $txlocaltax1 = price2num($txlocaltax1);
1598 $txlocaltax2 = price2num($txlocaltax2);
1599 if ($price_base_type == 'HT') {
1600 $pu = $pu_ht;
1601 } else {
1602 $pu = $pu_ttc;
1603 }
1604 $label = trim($label);
1605 $desc = trim($desc);
1606
1607 // Check parameters
1608 if ($type < 0) {
1609 return -1;
1610 }
1611
1612 if ($date_start && $date_end && $date_start > $date_end) {
1613 $langs->load("errors");
1614 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1615 return -1;
1616 }
1617
1618 $this->db->begin();
1619
1620 $product_type = $type;
1621 if (!empty($fk_product) && $fk_product > 0) {
1622 $product = new Product($this->db);
1623 $result = $product->fetch($fk_product);
1624 $product_type = $product->type;
1625
1626 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
1627 $langs->load("errors");
1628 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1629 $this->errors[] = $this->error;
1630 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1631 $this->db->rollback();
1633 }
1634 }
1635 // Calcul du total TTC et de la TVA pour la ligne a partir de
1636 // qty, pu, remise_percent et txtva
1637 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1638 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1639
1640 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1641
1642 // Clean vat code
1643 $reg = array();
1644 $vat_src_code = '';
1645 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1646 $vat_src_code = $reg[1];
1647 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1648 }
1649
1650 $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);
1651
1652 /*var_dump($txlocaltax1);
1653 var_dump($txlocaltax2);
1654 var_dump($localtaxes_type);
1655 var_dump($tabprice);
1656 var_dump($tabprice[9]);
1657 var_dump($tabprice[10]);
1658 exit;*/
1659
1660 $total_ht = $tabprice[0];
1661 $total_tva = $tabprice[1];
1662 $total_ttc = $tabprice[2];
1663 $total_localtax1 = $tabprice[9];
1664 $total_localtax2 = $tabprice[10];
1665 $pu_ht = $tabprice[3];
1666
1667 // MultiCurrency
1668 $multicurrency_total_ht = $tabprice[16];
1669 $multicurrency_total_tva = $tabprice[17];
1670 $multicurrency_total_ttc = $tabprice[18];
1671 $pu_ht_devise = $tabprice[19];
1672
1673 // Rang to use
1674 $ranktouse = $rang;
1675
1676 if ($ranktouse == -1) {
1677 $rangmax = $this->line_max($fk_parent_line);
1678 $ranktouse = $rangmax + 1;
1679 }
1680
1681 // TODO A virer
1682 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1683 $price = $pu;
1684 $remise = 0;
1685 if ($remise_percent > 0) {
1686 $remise = round(((float) $pu * $remise_percent / 100), 2);
1687 $price = (float) $pu - $remise;
1688 }
1689
1690 // Insert line
1691 $this->line = new OrderLine($this->db);
1692
1693 $this->line->context = $this->context;
1694
1695 $this->line->fk_commande = $this->id;
1696 $this->line->label = $label;
1697 $this->line->desc = $desc;
1698 $this->line->qty = $qty;
1699 $this->line->ref_ext = $ref_ext;
1700
1701 $this->line->vat_src_code = $vat_src_code;
1702 $this->line->tva_tx = $txtva;
1703 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1704 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1705 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1706 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1707 $this->line->fk_product = $fk_product;
1708 $this->line->product_type = $product_type;
1709 $this->line->fk_remise_except = $fk_remise_except;
1710 $this->line->remise_percent = $remise_percent;
1711 $this->line->subprice = (float) $pu_ht;
1712 $this->line->rang = $ranktouse;
1713 $this->line->info_bits = $info_bits;
1714 $this->line->total_ht = (float) $total_ht;
1715 $this->line->total_tva = (float) $total_tva;
1716 $this->line->total_localtax1 = (float) $total_localtax1;
1717 $this->line->total_localtax2 = (float) $total_localtax2;
1718 $this->line->total_ttc = (float) $total_ttc;
1719 $this->line->special_code = $special_code;
1720 $this->line->origin = $origin;
1721 $this->line->origin_id = $origin_id;
1722 $this->line->fk_parent_line = $fk_parent_line;
1723 $this->line->fk_unit = $fk_unit;
1724
1725 $this->line->date_start = $date_start;
1726 $this->line->date_end = $date_end;
1727
1728 $this->line->fk_fournprice = $fk_fournprice;
1729 $this->line->pa_ht = $pa_ht; // Can be '' when not defined or 0 if defined to 0 or a price value
1730
1731 // Multicurrency
1732 $this->line->fk_multicurrency = $this->fk_multicurrency;
1733 $this->line->multicurrency_code = $this->multicurrency_code;
1734 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
1735 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
1736 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
1737 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
1738
1739 // TODO Do not use anymore
1740 $this->line->price = $price;
1741
1742 if (is_array($array_options) && count($array_options) > 0) {
1743 $this->line->array_options = $array_options;
1744 }
1745
1746 $result = $this->line->insert($user);
1747 if ($result > 0) {
1748 // Update denormalized fields at the order level
1749 if (empty($noupdateafterinsertline)) {
1750 $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.
1751 }
1752
1753 if ($result > 0) {
1754 if (!isset($this->context['createfromclone'])) {
1755 if (!empty($fk_parent_line)) {
1756 // Always reorder if child line
1757 $this->line_order(true, 'DESC');
1758 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1759 // Update all rank of all other lines starting from the same $ranktouse
1760 foreach ($this->lines as $tmpline) {
1761 if ($tmpline->rang >= $ranktouse) {
1762 if (!empty($tmpline->id)) {
1763 $this->updateRangOfLine($tmpline->id, $tmpline->rang + 1);
1764 }
1765 }
1766 }
1767 }
1768
1769 $this->lines[] = $this->line;
1770 }
1771
1772 $this->db->commit();
1773 return $this->line->id;
1774 } else {
1775 $this->db->rollback();
1776 return -1;
1777 }
1778 } else {
1779 $this->error = $this->line->error;
1780 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1781 $this->db->rollback();
1782 return -2;
1783 }
1784 } else {
1785 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1786 return -3;
1787 }
1788 }
1789
1790
1791 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1805 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1806 {
1807 // phpcs:enable
1808 global $conf, $mysoc;
1809
1810 if (!$qty) {
1811 $qty = 1;
1812 }
1813
1814 if ($idproduct > 0) {
1815 $prod = new Product($this->db);
1816 $prod->fetch($idproduct);
1817
1818 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1819 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1820 if (empty($tva_tx)) {
1821 $tva_npr = 0;
1822 }
1823 $vat_src_code = ''; // May be defined into tva_tx
1824
1825 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1826 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1827
1828 // multiprix
1829 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
1830 $price = $prod->multiprices[$this->thirdparty->price_level];
1831 } else {
1832 $price = $prod->price;
1833 }
1834
1835 $line = new OrderLine($this->db);
1836
1837 $line->context = $this->context;
1838
1839 $line->fk_product = $idproduct;
1840 $line->desc = $prod->description;
1841 $line->qty = $qty;
1842 $line->subprice = $price;
1843 $line->remise_percent = $remise_percent;
1844 $line->vat_src_code = $vat_src_code;
1845 $line->tva_tx = $tva_tx;
1846 $line->localtax1_tx = $localtax1_tx;
1847 $line->localtax2_tx = $localtax2_tx;
1848
1849 $line->product_ref = $prod->ref;
1850 $line->product_label = $prod->label;
1851 $line->product_desc = $prod->description;
1852 $line->fk_unit = $prod->fk_unit;
1853
1854 // Save the start and end date of the line in the object
1855 if ($date_start) {
1856 $line->date_start = $date_start;
1857 }
1858 if ($date_end) {
1859 $line->date_end = $date_end;
1860 }
1861
1862 $this->lines[] = $line;
1863
1881 }
1882 }
1883
1884
1894 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1895 {
1896 // Check parameters
1897 if (empty($id) && empty($ref) && empty($ref_ext)) {
1898 return -1;
1899 }
1900
1901 $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 as status';
1902 $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';
1903 $sql .= ', c.fk_account';
1904 $sql .= ', c.date_commande, c.date_valid, c.tms';
1905 $sql .= ', c.date_livraison as delivery_date';
1906 $sql .= ', c.fk_shipping_method';
1907 $sql .= ', c.fk_warehouse';
1908 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1909 $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';
1910 $sql .= ', c.fk_incoterms, c.location_incoterms';
1911 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1912 $sql .= ", c.module_source, c.pos_source";
1913 $sql .= ", i.libelle as label_incoterms";
1914 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1915 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1916 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1917 $sql .= ', dr.code as demand_reason_code';
1918 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
1919 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1920 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1921 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1922 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1923 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1924
1925 if ($id) {
1926 $sql .= " WHERE c.rowid=".((int) $id);
1927 } else {
1928 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
1929 }
1930
1931 if ($ref) {
1932 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1933 }
1934 if ($ref_ext) {
1935 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1936 }
1937
1938 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1939 $result = $this->db->query($sql);
1940 if ($result) {
1941 $obj = $this->db->fetch_object($result);
1942 if ($obj) {
1943 $this->id = $obj->rowid;
1944 $this->entity = $obj->entity;
1945
1946 $this->ref = $obj->ref;
1947 $this->ref_client = $obj->ref_client;
1948 $this->ref_customer = $obj->ref_client;
1949 $this->ref_ext = $obj->ref_ext;
1950
1951 $this->socid = $obj->fk_soc;
1952 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1953
1954 $this->fk_project = $obj->fk_project;
1955 $this->project = null; // Clear if another value was already set by fetch_projet
1956
1957 $this->statut = $obj->status;
1958 $this->status = $obj->status;
1959
1960 $this->user_author_id = $obj->fk_user_author;
1961 $this->user_creation_id = $obj->fk_user_author;
1962 $this->user_validation_id = $obj->fk_user_valid;
1963 $this->user_modification_id = $obj->fk_user_modif;
1964 $this->total_ht = $obj->total_ht;
1965 $this->total_tva = $obj->total_tva;
1966 $this->total_localtax1 = $obj->total_localtax1;
1967 $this->total_localtax2 = $obj->total_localtax2;
1968 $this->total_ttc = $obj->total_ttc;
1969 $this->date = $this->db->jdate($obj->date_commande);
1970 $this->date_commande = $this->db->jdate($obj->date_commande);
1971 $this->date_creation = $this->db->jdate($obj->date_creation);
1972 $this->date_validation = $this->db->jdate($obj->date_valid);
1973 $this->date_modification = $this->db->jdate($obj->tms);
1974 $this->source = $obj->source;
1975 $this->billed = $obj->billed;
1976 $this->note = $obj->note_private; // deprecated
1977 $this->note_private = $obj->note_private;
1978 $this->note_public = $obj->note_public;
1979 $this->model_pdf = $obj->model_pdf;
1980 $this->last_main_doc = $obj->last_main_doc;
1981 $this->mode_reglement_id = $obj->fk_mode_reglement;
1982 $this->mode_reglement_code = $obj->mode_reglement_code;
1983 $this->mode_reglement = $obj->mode_reglement_libelle;
1984 $this->cond_reglement_id = $obj->fk_cond_reglement;
1985 $this->cond_reglement_code = $obj->cond_reglement_code;
1986 $this->cond_reglement = $obj->cond_reglement_libelle;
1987 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1988 $this->deposit_percent = $obj->deposit_percent;
1989 $this->fk_account = $obj->fk_account;
1990 $this->availability_id = $obj->fk_availability;
1991 $this->availability_code = $obj->availability_code;
1992 $this->availability = $obj->availability_label;
1993 $this->demand_reason_id = $obj->fk_input_reason;
1994 $this->demand_reason_code = $obj->demand_reason_code;
1995 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1996 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1997 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1998 $this->fk_delivery_address = $obj->fk_delivery_address;
1999 $this->module_source = $obj->module_source;
2000 $this->pos_source = $obj->pos_source;
2001
2002 //Incoterms
2003 $this->fk_incoterms = $obj->fk_incoterms;
2004 $this->location_incoterms = $obj->location_incoterms;
2005 $this->label_incoterms = $obj->label_incoterms;
2006
2007 // Multicurrency
2008 $this->fk_multicurrency = $obj->fk_multicurrency;
2009 $this->multicurrency_code = $obj->multicurrency_code;
2010 $this->multicurrency_tx = $obj->multicurrency_tx;
2011 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
2012 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
2013 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2014
2015 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2016
2017 $this->lines = array();
2018
2019 // Retrieve all extrafield
2020 // fetch optionals attributes and labels
2021 $this->fetch_optionals();
2022
2023 $this->db->free($result);
2024
2025 // Lines
2026 $result = $this->fetch_lines();
2027 if ($result < 0) {
2028 return -3;
2029 }
2030 return 1;
2031 } else {
2032 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2033 return 0;
2034 }
2035 } else {
2036 $this->error = $this->db->error();
2037 return -1;
2038 }
2039 }
2040
2041
2042 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2049 public function insert_discount($idremise)
2050 {
2051 // phpcs:enable
2052 global $langs;
2053
2054 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2055 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2056
2057 $this->db->begin();
2058
2059 $remise = new DiscountAbsolute($this->db);
2060 $result = $remise->fetch($idremise);
2061
2062 if ($result > 0) {
2063 if ($remise->fk_facture) { // Protection against multiple submission
2064 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2065 $this->db->rollback();
2066 return -5;
2067 }
2068
2069 $line = new OrderLine($this->db);
2070
2071 $line->fk_commande = $this->id;
2072 $line->fk_remise_except = $remise->id;
2073 $line->desc = $remise->description; // Description ligne
2074 $line->vat_src_code = $remise->vat_src_code;
2075 $line->tva_tx = $remise->tva_tx;
2076 $line->subprice = -(float) $remise->amount_ht;
2077 $line->price = -(float) $remise->amount_ht;
2078 $line->fk_product = 0; // Id produit predefini
2079 $line->qty = 1;
2080 $line->remise_percent = 0;
2081 $line->rang = -1;
2082 $line->info_bits = 2;
2083
2084 $line->total_ht = -(float) $remise->amount_ht;
2085 $line->total_tva = -(float) $remise->amount_tva;
2086 $line->total_ttc = -(float) $remise->amount_ttc;
2087
2088 $result = $line->insert();
2089 if ($result > 0) {
2090 $result = $this->update_price(1);
2091 if ($result > 0) {
2092 $this->db->commit();
2093 return 1;
2094 } else {
2095 $this->db->rollback();
2096 return -1;
2097 }
2098 } else {
2099 $this->error = $line->error;
2100 $this->errors = $line->errors;
2101 $this->db->rollback();
2102 return -2;
2103 }
2104 } else {
2105 $this->db->rollback();
2106 return -2;
2107 }
2108 }
2109
2110
2111 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2119 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2120 {
2121 // phpcs:enable
2122 global $langs, $conf;
2123
2124 $this->lines = array();
2125
2126 $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,';
2127 $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,';
2128 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2129 $sql .= ' l.fk_unit,';
2130 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2131 $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,';
2132 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2133 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2134 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2135 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2136 if ($only_product) {
2137 $sql .= ' AND p.fk_product_type = 0';
2138 }
2139 $sql .= ' ORDER BY l.rang, l.rowid';
2140
2141 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2142 $result = $this->db->query($sql);
2143 if ($result) {
2144 $num = $this->db->num_rows($result);
2145
2146 $i = 0;
2147 while ($i < $num) {
2148 $objp = $this->db->fetch_object($result);
2149
2150 $line = new OrderLine($this->db);
2151
2152 $line->rowid = $objp->rowid;
2153 $line->id = $objp->rowid;
2154 $line->fk_commande = $objp->fk_commande;
2155 $line->commande_id = $objp->fk_commande;
2156 $line->label = $objp->custom_label;
2157 $line->desc = $objp->description;
2158 $line->description = $objp->description; // Description line
2159 $line->product_type = $objp->product_type;
2160 $line->qty = $objp->qty;
2161 $line->ref_ext = $objp->ref_ext;
2162
2163 $line->vat_src_code = $objp->vat_src_code;
2164 $line->tva_tx = $objp->tva_tx;
2165 $line->localtax1_tx = $objp->localtax1_tx;
2166 $line->localtax2_tx = $objp->localtax2_tx;
2167 $line->localtax1_type = $objp->localtax1_type;
2168 $line->localtax2_type = $objp->localtax2_type;
2169 $line->total_ht = $objp->total_ht;
2170 $line->total_ttc = $objp->total_ttc;
2171 $line->total_tva = $objp->total_tva;
2172 $line->total_localtax1 = $objp->total_localtax1;
2173 $line->total_localtax2 = $objp->total_localtax2;
2174 $line->subprice = $objp->subprice;
2175 $line->fk_remise_except = $objp->fk_remise_except;
2176 $line->remise_percent = $objp->remise_percent;
2177 $line->price = $objp->price;
2178 $line->fk_product = $objp->fk_product;
2179 $line->fk_fournprice = $objp->fk_fournprice;
2180 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2181 $line->pa_ht = $marginInfos[0];
2182 $line->marge_tx = $marginInfos[1];
2183 $line->marque_tx = $marginInfos[2];
2184 $line->rang = $objp->rang;
2185 $line->info_bits = $objp->info_bits;
2186 $line->special_code = $objp->special_code;
2187 $line->fk_parent_line = $objp->fk_parent_line;
2188
2189 $line->ref = $objp->product_ref;
2190 $line->libelle = $objp->product_label;
2191
2192 $line->product_ref = $objp->product_ref;
2193 $line->product_label = $objp->product_label;
2194 $line->product_tosell = $objp->product_tosell;
2195 $line->product_tobuy = $objp->product_tobuy;
2196 $line->product_desc = $objp->product_desc;
2197 $line->product_tobatch = $objp->product_tobatch;
2198 $line->product_barcode = $objp->product_barcode;
2199
2200 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2201 $line->fk_unit = $objp->fk_unit;
2202
2203 $line->weight = $objp->weight;
2204 $line->weight_units = $objp->weight_units;
2205 $line->volume = $objp->volume;
2206 $line->volume_units = $objp->volume_units;
2207
2208 $line->date_start = $this->db->jdate($objp->date_start);
2209 $line->date_end = $this->db->jdate($objp->date_end);
2210
2211 // Multicurrency
2212 $line->fk_multicurrency = $objp->fk_multicurrency;
2213 $line->multicurrency_code = $objp->multicurrency_code;
2214 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2215 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2216 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2217 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2218
2219 $line->fetch_optionals();
2220
2221 // multilangs
2222 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2223 $tmpproduct = new Product($this->db);
2224 $tmpproduct->fetch($objp->fk_product);
2225 $tmpproduct->getMultiLangs();
2226
2227 $line->multilangs = $tmpproduct->multilangs;
2228 }
2229
2230 $this->lines[$i] = $line;
2231
2232 $i++;
2233 }
2234
2235 $this->db->free($result);
2236
2237 return 1;
2238 } else {
2239 $this->error = $this->db->error();
2240 return -3;
2241 }
2242 }
2243
2244
2250 public function getNbOfProductsLines()
2251 {
2252 $nb = 0;
2253 foreach ($this->lines as $line) {
2254 if ($line->product_type == 0) {
2255 $nb++;
2256 }
2257 }
2258 return $nb;
2259 }
2260
2266 public function getNbOfServicesLines()
2267 {
2268 $nb = 0;
2269 foreach ($this->lines as $line) {
2270 if ($line->product_type == 1) {
2271 $nb++;
2272 }
2273 }
2274 return $nb;
2275 }
2276
2282 public function getNbOfShipments()
2283 {
2284 $nb = 0;
2285
2286 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2287 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2288 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2289 $sql .= ' WHERE';
2290 $sql .= ' ed.fk_elementdet = cd.rowid';
2291 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2292 //print $sql;
2293
2294 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2295 $resql = $this->db->query($sql);
2296 if ($resql) {
2297 $obj = $this->db->fetch_object($resql);
2298 if ($obj) {
2299 $nb = $obj->nb;
2300 }
2301
2302 $this->db->free($resql);
2303 return $nb;
2304 } else {
2305 $this->error = $this->db->lasterror();
2306 return -1;
2307 }
2308 }
2309
2318 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2319 {
2320 $this->expeditions = array();
2321
2322 $sql = 'SELECT cd.rowid, cd.fk_product,';
2323 $sql .= ' sum(ed.qty) as qty';
2324 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2325 if ($filtre_statut >= 0) {
2326 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2327 }
2328 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2329 $sql .= ' WHERE';
2330 if ($filtre_statut >= 0) {
2331 $sql .= ' ed.fk_expedition = e.rowid AND';
2332 }
2333 $sql .= ' ed.fk_elementdet = cd.rowid';
2334 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2335 if ($fk_product > 0) {
2336 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2337 }
2338 if ($filtre_statut >= 0) {
2339 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2340 }
2341 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2342 //print $sql;
2343
2344 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2345 $resql = $this->db->query($sql);
2346 if ($resql) {
2347 $num = $this->db->num_rows($resql);
2348 $i = 0;
2349 while ($i < $num) {
2350 $obj = $this->db->fetch_object($resql);
2351 $this->expeditions[$obj->rowid] = $obj->qty;
2352 $i++;
2353 }
2354 $this->db->free($resql);
2355 return $num;
2356 } else {
2357 $this->error = $this->db->lasterror();
2358 return -1;
2359 }
2360 }
2361
2367 public function countNbOfShipments()
2368 {
2369 $sql = 'SELECT count(*)';
2370 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2371 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2372 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2373 $sql .= " AND el.sourcetype = 'commande'";
2374 $sql .= " AND el.fk_target = e.rowid";
2375 $sql .= " AND el.targettype = 'shipping'";
2376
2377 $resql = $this->db->query($sql);
2378 if ($resql) {
2379 $row = $this->db->fetch_row($resql);
2380 return $row[0];
2381 } else {
2382 dol_print_error($this->db);
2383 }
2384
2385 return 0;
2386 }
2387
2388 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2397 /*public function stock_array($filtre_statut = self::STATUS_CANCELED)
2398 {
2399 // phpcs:enable
2400 $this->stocks = array();
2401
2402 // Tableau des id de produit de la commande
2403 $array_of_product = array();
2404
2405 // Recherche total en stock pour chaque produit
2406 // TODO $array_of_product est défini vide juste au dessus !!
2407 if (count($array_of_product)) {
2408 $sql = "SELECT fk_product, sum(ps.reel) as total";
2409 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2410 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2411 $sql .= ' GROUP BY fk_product';
2412 $resql = $this->db->query($sql);
2413 if ($resql) {
2414 $num = $this->db->num_rows($resql);
2415 $i = 0;
2416 while ($i < $num) {
2417 $obj = $this->db->fetch_object($resql);
2418 $this->stocks[$obj->fk_product] = $obj->total;
2419 $i++;
2420 }
2421 $this->db->free($resql);
2422 }
2423 }
2424 return 0;
2425 }*/
2426
2435 public function deleteLine($user = null, $lineid = 0, $id = 0)
2436 {
2437 if ($this->statut == self::STATUS_DRAFT) {
2438 $this->db->begin();
2439
2440 // Delete line
2441 $line = new OrderLine($this->db);
2442
2443 $line->context = $this->context;
2444
2445 // Load data
2446 $line->fetch($lineid);
2447
2448 if ($id > 0 && $line->fk_commande != $id) {
2449 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2450 return -1;
2451 }
2452
2453 // Memorize previous line for triggers
2454 $staticline = clone $line;
2455 $line->oldline = $staticline;
2456
2457 if ($line->delete($user) > 0) {
2458 $result = $this->update_price(1);
2459
2460 if ($result > 0) {
2461 $this->db->commit();
2462 return 1;
2463 } else {
2464 $this->db->rollback();
2465 $this->error = $this->db->lasterror();
2466 return -1;
2467 }
2468 } else {
2469 $this->db->rollback();
2470 $this->error = $line->error;
2471 return -1;
2472 }
2473 } else {
2474 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2475 return -1;
2476 }
2477 }
2478
2479 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2490 public function set_remise($user, $remise, $notrigger = 0)
2491 {
2492 // phpcs:enable
2493 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2494 // @phan-suppress-next-line PhanDeprecatedFunction
2495 return $this->setDiscount($user, $remise, $notrigger);
2496 }
2497
2506 public function setDiscount($user, $remise, $notrigger = 0)
2507 {
2508 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2509
2510 if ($user->hasRight('commande', 'creer')) {
2511 $error = 0;
2512
2513 $this->db->begin();
2514
2515 $remise = price2num($remise, 2);
2516
2517 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2518 $sql .= ' SET remise_percent = '.((float) $remise);
2519 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2520
2521 dol_syslog(__METHOD__, LOG_DEBUG);
2522 $resql = $this->db->query($sql);
2523 if (!$resql) {
2524 $this->errors[] = $this->db->error();
2525 $error++;
2526 }
2527
2528 if (!$error) {
2529 $this->oldcopy = clone $this;
2530 $this->remise_percent = $remise;
2531 $this->update_price(1);
2532 }
2533
2534 if (!$notrigger && empty($error)) {
2535 // Call trigger
2536 $result = $this->call_trigger('ORDER_MODIFY', $user);
2537 if ($result < 0) {
2538 $error++;
2539 }
2540 // End call triggers
2541 }
2542
2543 if (!$error) {
2544 $this->db->commit();
2545 return 1;
2546 } else {
2547 foreach ($this->errors as $errmsg) {
2548 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2549 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2550 }
2551 $this->db->rollback();
2552 return -1 * $error;
2553 }
2554 }
2555
2556 return 0;
2557 }
2558
2559
2560 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2569 /*
2570 public function set_remise_absolue($user, $remise, $notrigger = 0)
2571 {
2572 // phpcs:enable
2573 if (empty($remise)) {
2574 $remise = 0;
2575 }
2576
2577 $remise = price2num($remise);
2578
2579 if ($user->hasRight('commande', 'creer')) {
2580 $error = 0;
2581
2582 $this->db->begin();
2583
2584 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2585 $sql .= ' SET remise_absolue = '.((float) $remise);
2586 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2587
2588 dol_syslog(__METHOD__, LOG_DEBUG);
2589 $resql = $this->db->query($sql);
2590 if (!$resql) {
2591 $this->errors[] = $this->db->error();
2592 $error++;
2593 }
2594
2595 if (!$error) {
2596 $this->oldcopy = clone $this;
2597 $this->remise_absolue = $remise;
2598 $this->update_price(1);
2599 }
2600
2601 if (!$notrigger && empty($error)) {
2602 // Call trigger
2603 $result = $this->call_trigger('ORDER_MODIFY', $user);
2604 if ($result < 0) {
2605 $error++;
2606 }
2607 // End call triggers
2608 }
2609
2610 if (!$error) {
2611 $this->db->commit();
2612 return 1;
2613 } else {
2614 foreach ($this->errors as $errmsg) {
2615 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2616 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2617 }
2618 $this->db->rollback();
2619 return -1 * $error;
2620 }
2621 }
2622
2623 return 0;
2624 }
2625 */
2626
2627 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2636 public function set_date($user, $date, $notrigger = 0)
2637 {
2638 // phpcs:enable
2639 if ($user->hasRight('commande', 'creer')) {
2640 $error = 0;
2641
2642 $this->db->begin();
2643
2644 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2645 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2646 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2647
2648 dol_syslog(__METHOD__, LOG_DEBUG);
2649 $resql = $this->db->query($sql);
2650 if (!$resql) {
2651 $this->errors[] = $this->db->error();
2652 $error++;
2653 }
2654
2655 if (!$error) {
2656 $this->oldcopy = clone $this;
2657 $this->date = $date;
2658 }
2659
2660 if (!$notrigger && empty($error)) {
2661 // Call trigger
2662 $result = $this->call_trigger('ORDER_MODIFY', $user);
2663 if ($result < 0) {
2664 $error++;
2665 }
2666 // End call triggers
2667 }
2668
2669 if (!$error) {
2670 $this->db->commit();
2671 return 1;
2672 } else {
2673 foreach ($this->errors as $errmsg) {
2674 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2675 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2676 }
2677 $this->db->rollback();
2678 return -1 * $error;
2679 }
2680 } else {
2681 return -2;
2682 }
2683 }
2684
2685 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2695 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2696 {
2697 // phpcs:enable
2698 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2699 }
2700
2709 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2710 {
2711 if ($user->hasRight('commande', 'creer')) {
2712 $error = 0;
2713
2714 $this->db->begin();
2715
2716 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2717 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2718 $sql .= " WHERE rowid = ".((int) $this->id);
2719
2720 dol_syslog(__METHOD__, LOG_DEBUG);
2721 $resql = $this->db->query($sql);
2722 if (!$resql) {
2723 $this->errors[] = $this->db->error();
2724 $error++;
2725 }
2726
2727 if (!$error) {
2728 $this->oldcopy = clone $this;
2729 $this->delivery_date = $delivery_date;
2730 }
2731
2732 if (!$notrigger && empty($error)) {
2733 // Call trigger
2734 $result = $this->call_trigger('ORDER_MODIFY', $user);
2735 if ($result < 0) {
2736 $error++;
2737 }
2738 // End call triggers
2739 }
2740
2741 if (!$error) {
2742 $this->db->commit();
2743 return 1;
2744 } else {
2745 foreach ($this->errors as $errmsg) {
2746 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2747 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2748 }
2749 $this->db->rollback();
2750 return -1 * $error;
2751 }
2752 } else {
2753 return -2;
2754 }
2755 }
2756
2757 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2771 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2772 {
2773 // phpcs:enable
2774 global $user;
2775
2776 $ga = array();
2777
2778 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2779 $sql .= " c.rowid as cid, c.ref";
2780 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2781 $sql .= ", sc.fk_soc, sc.fk_user";
2782 }
2783 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2784 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2785 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2786 }
2787 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2788 $sql .= " AND c.fk_soc = s.rowid";
2789 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2790 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2791 }
2792 if ($socid) {
2793 $sql .= " AND s.rowid = ".((int) $socid);
2794 }
2795 if ($draft) {
2796 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2797 }
2798 if (is_object($excluser)) {
2799 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2800 }
2801 $sql .= $this->db->order($sortfield, $sortorder);
2802 $sql .= $this->db->plimit($limit, $offset);
2803
2804 $result = $this->db->query($sql);
2805 if ($result) {
2806 $numc = $this->db->num_rows($result);
2807 if ($numc) {
2808 $i = 0;
2809 while ($i < $numc) {
2810 $obj = $this->db->fetch_object($result);
2811
2812 if ($shortlist == 1) {
2813 $ga[$obj->cid] = $obj->ref;
2814 } elseif ($shortlist == 2) {
2815 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2816 } else {
2817 $ga[$i]['id'] = $obj->cid;
2818 $ga[$i]['ref'] = $obj->ref;
2819 $ga[$i]['name'] = $obj->name;
2820 }
2821 $i++;
2822 }
2823 }
2824 return $ga;
2825 } else {
2826 dol_print_error($this->db);
2827 return -1;
2828 }
2829 }
2830
2838 public function availability($availability_id, $notrigger = 0)
2839 {
2840 global $user;
2841
2842 dol_syslog('Commande::availability('.$availability_id.')');
2843 if ($this->statut >= self::STATUS_DRAFT) {
2844 $error = 0;
2845
2846 $this->db->begin();
2847
2848 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2849 $sql .= ' SET fk_availability = '.((int) $availability_id);
2850 $sql .= ' WHERE rowid='.((int) $this->id);
2851
2852 dol_syslog(__METHOD__, LOG_DEBUG);
2853 $resql = $this->db->query($sql);
2854 if (!$resql) {
2855 $this->errors[] = $this->db->error();
2856 $error++;
2857 }
2858
2859 if (!$error) {
2860 $this->oldcopy = clone $this;
2861 $this->availability_id = $availability_id;
2862 }
2863
2864 if (!$notrigger && empty($error)) {
2865 // Call trigger
2866 $result = $this->call_trigger('ORDER_MODIFY', $user);
2867 if ($result < 0) {
2868 $error++;
2869 }
2870 // End call triggers
2871 }
2872
2873 if (!$error) {
2874 $this->db->commit();
2875 return 1;
2876 } else {
2877 foreach ($this->errors as $errmsg) {
2878 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2879 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2880 }
2881 $this->db->rollback();
2882 return -1 * $error;
2883 }
2884 } else {
2885 $error_str = 'Command status do not meet requirement '.$this->statut;
2886 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2887 $this->error = $error_str;
2888 $this->errors[] = $this->error;
2889 return -2;
2890 }
2891 }
2892
2893 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2901 public function demand_reason($demand_reason_id, $notrigger = 0)
2902 {
2903 // phpcs:enable
2904 global $user;
2905
2906 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2907 if ($this->statut >= self::STATUS_DRAFT) {
2908 $error = 0;
2909
2910 $this->db->begin();
2911
2912 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2913 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2914 $sql .= ' WHERE rowid='.((int) $this->id);
2915
2916 dol_syslog(__METHOD__, LOG_DEBUG);
2917 $resql = $this->db->query($sql);
2918 if (!$resql) {
2919 $this->errors[] = $this->db->error();
2920 $error++;
2921 }
2922
2923 if (!$error) {
2924 $this->oldcopy = clone $this;
2925 $this->demand_reason_id = $demand_reason_id;
2926 }
2927
2928 if (!$notrigger && empty($error)) {
2929 // Call trigger
2930 $result = $this->call_trigger('ORDER_MODIFY', $user);
2931 if ($result < 0) {
2932 $error++;
2933 }
2934 // End call triggers
2935 }
2936
2937 if (!$error) {
2938 $this->db->commit();
2939 return 1;
2940 } else {
2941 foreach ($this->errors as $errmsg) {
2942 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2943 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2944 }
2945 $this->db->rollback();
2946 return -1 * $error;
2947 }
2948 } else {
2949 $error_str = 'order status do not meet requirement '.$this->statut;
2950 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2951 $this->error = $error_str;
2952 $this->errors[] = $this->error;
2953 return -2;
2954 }
2955 }
2956
2957 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2966 public function set_ref_client($user, $ref_client, $notrigger = 0)
2967 {
2968 // phpcs:enable
2969 if ($user->hasRight('commande', 'creer')) {
2970 $error = 0;
2971
2972 $this->db->begin();
2973
2974 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2975 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2976 $sql .= ' WHERE rowid = '.((int) $this->id);
2977
2978 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2979 $resql = $this->db->query($sql);
2980 if (!$resql) {
2981 $this->errors[] = $this->db->error();
2982 $error++;
2983 }
2984
2985 if (!$error) {
2986 $this->oldcopy = clone $this;
2987 $this->ref_client = $ref_client;
2988 $this->ref_customer = $ref_client;
2989 }
2990
2991 if (!$notrigger && empty($error)) {
2992 // Call trigger
2993 $result = $this->call_trigger('ORDER_MODIFY', $user);
2994 if ($result < 0) {
2995 $error++;
2996 }
2997 // End call triggers
2998 }
2999 if (!$error) {
3000 $this->db->commit();
3001 return 1;
3002 } else {
3003 foreach ($this->errors as $errmsg) {
3004 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3005 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3006 }
3007 $this->db->rollback();
3008 return -1 * $error;
3009 }
3010 } else {
3011 return -1;
3012 }
3013 }
3014
3022 public function classifyBilled(User $user, $notrigger = 0)
3023 {
3024 $error = 0;
3025
3026 if ($this->billed) {
3027 return 0;
3028 }
3029
3030 $this->db->begin();
3031
3032 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3033 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3034
3035 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3036 if ($this->db->query($sql)) {
3037 if (!$error) {
3038 $this->oldcopy = clone $this;
3039 $this->billed = 1;
3040 }
3041
3042 if (!$notrigger && empty($error)) {
3043 // Call trigger
3044 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3045 if ($result < 0) {
3046 $error++;
3047 }
3048 // End call triggers
3049 }
3050
3051 if (!$error) {
3052 $this->db->commit();
3053 return 1;
3054 } else {
3055 foreach ($this->errors as $errmsg) {
3056 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3057 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3058 }
3059 $this->db->rollback();
3060 return -1 * $error;
3061 }
3062 } else {
3063 $this->error = $this->db->error();
3064 $this->db->rollback();
3065 return -1;
3066 }
3067 }
3068
3076 public function classifyUnBilled(User $user, $notrigger = 0)
3077 {
3078 $error = 0;
3079
3080 $this->db->begin();
3081
3082 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3083 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3084
3085 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3086 if ($this->db->query($sql)) {
3087 if (!$error) {
3088 $this->oldcopy = clone $this;
3089 $this->billed = 1;
3090 }
3091
3092 if (!$notrigger && empty($error)) {
3093 // Call trigger
3094 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3095 if ($result < 0) {
3096 $error++;
3097 }
3098 // End call triggers
3099 }
3100
3101 if (!$error) {
3102 $this->billed = 0;
3103
3104 $this->db->commit();
3105 return 1;
3106 } else {
3107 foreach ($this->errors as $errmsg) {
3108 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3109 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3110 }
3111 $this->db->rollback();
3112 return -1 * $error;
3113 }
3114 } else {
3115 $this->error = $this->db->error();
3116 $this->db->rollback();
3117 return -1;
3118 }
3119 }
3120
3121
3152 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)
3153 {
3154 global $conf, $mysoc, $langs, $user;
3155
3156 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");
3157 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3158
3159 if ($this->statut == Commande::STATUS_DRAFT) {
3160 // Clean parameters
3161 if (empty($qty)) {
3162 $qty = 0;
3163 }
3164 if (empty($info_bits)) {
3165 $info_bits = 0;
3166 }
3167 if (empty($txtva)) {
3168 $txtva = 0;
3169 }
3170 if (empty($txlocaltax1)) {
3171 $txlocaltax1 = 0;
3172 }
3173 if (empty($txlocaltax2)) {
3174 $txlocaltax2 = 0;
3175 }
3176 if (empty($remise_percent)) {
3177 $remise_percent = 0;
3178 }
3179 if (empty($special_code) || $special_code == 3) {
3180 $special_code = 0;
3181 }
3182 if (empty($ref_ext)) {
3183 $ref_ext = '';
3184 }
3185
3186 if ($date_start && $date_end && $date_start > $date_end) {
3187 $langs->load("errors");
3188 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3189 return -1;
3190 }
3191
3192 $remise_percent = (float) price2num($remise_percent);
3193 $qty = (float) price2num($qty);
3194 $pu = price2num($pu);
3195 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
3196 $pu_ht_devise = price2num($pu_ht_devise);
3197 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3198 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3199 }
3200 $txlocaltax1 = (float) price2num($txlocaltax1);
3201 $txlocaltax2 = (float) price2num($txlocaltax2);
3202
3203 $this->db->begin();
3204
3205 // Calcul du total TTC et de la TVA pour la ligne a partir de
3206 // qty, pu, remise_percent et txtva
3207 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3208 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3209
3210 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3211
3212 // Clean vat code
3213 $vat_src_code = '';
3214 $reg = array();
3215 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3216 $vat_src_code = $reg[1];
3217 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3218 }
3219
3220 $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);
3221
3222 $total_ht = $tabprice[0];
3223 $total_tva = $tabprice[1];
3224 $total_ttc = $tabprice[2];
3225 $total_localtax1 = $tabprice[9];
3226 $total_localtax2 = $tabprice[10];
3227 $pu_ht = $tabprice[3];
3228 $pu_tva = $tabprice[4];
3229 $pu_ttc = $tabprice[5];
3230
3231 // MultiCurrency
3232 $multicurrency_total_ht = $tabprice[16];
3233 $multicurrency_total_tva = $tabprice[17];
3234 $multicurrency_total_ttc = $tabprice[18];
3235 $pu_ht_devise = $tabprice[19];
3236
3237 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3238 $price = $pu_ht;
3239 if ($price_base_type == 'TTC') {
3240 $subprice = $pu_ttc;
3241 } else {
3242 $subprice = $pu_ht;
3243 }
3244 $remise = 0;
3245 if ($remise_percent > 0) {
3246 $remise = round(((float) $pu * $remise_percent / 100), 2);
3247 $price = ((float) $pu - $remise);
3248 }
3249
3250 // Fetch current line from the database and then clone the object and set it in $oldline property
3251 $line = new OrderLine($this->db);
3252 $line->fetch($rowid);
3253 $line->fetch_optionals();
3254
3255 if (!empty($line->fk_product)) {
3256 $product = new Product($this->db);
3257 $result = $product->fetch($line->fk_product);
3258 $product_type = $product->type;
3259
3260 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
3261 $langs->load("errors");
3262 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3263 $this->errors[] = $this->error;
3264
3265 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3266
3267 $this->db->rollback();
3269 }
3270 }
3271
3272 $staticline = clone $line;
3273
3274 $line->oldline = $staticline;
3275 $this->line = $line;
3276 $this->line->context = $this->context;
3277 $this->line->rang = $rang;
3278
3279 // Reorder if fk_parent_line change
3280 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3281 $rangmax = $this->line_max($fk_parent_line);
3282 $this->line->rang = $rangmax + 1;
3283 }
3284
3285 $this->line->id = $rowid;
3286 $this->line->label = $label;
3287 $this->line->desc = $desc;
3288 $this->line->qty = $qty;
3289 $this->line->ref_ext = $ref_ext;
3290
3291 $this->line->vat_src_code = $vat_src_code;
3292 $this->line->tva_tx = $txtva;
3293 $this->line->localtax1_tx = $txlocaltax1;
3294 $this->line->localtax2_tx = $txlocaltax2;
3295 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3296 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3297 $this->line->remise_percent = $remise_percent;
3298 $this->line->subprice = (float) $pu_ht;
3299 $this->line->info_bits = $info_bits;
3300 $this->line->special_code = $special_code;
3301 $this->line->total_ht = (float) $total_ht;
3302 $this->line->total_tva = (float) $total_tva;
3303 $this->line->total_localtax1 = (float) $total_localtax1;
3304 $this->line->total_localtax2 = (float) $total_localtax2;
3305 $this->line->total_ttc = (float) $total_ttc;
3306 $this->line->date_start = $date_start;
3307 $this->line->date_end = $date_end;
3308 $this->line->product_type = $type;
3309 $this->line->fk_parent_line = $fk_parent_line;
3310 $this->line->skip_update_total = $skip_update_total;
3311 $this->line->fk_unit = $fk_unit;
3312
3313 $this->line->fk_fournprice = $fk_fournprice;
3314 $this->line->pa_ht = $pa_ht;
3315
3316 // Multicurrency
3317 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3318 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3319 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3320 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3321
3322 // TODO deprecated
3323 $this->line->price = $price;
3324
3325 if (is_array($array_options) && count($array_options) > 0) {
3326 // We replace values in this->line->array_options only for entries defined into $array_options
3327 foreach ($array_options as $key => $value) {
3328 $this->line->array_options[$key] = $array_options[$key];
3329 }
3330 }
3331
3332 $result = $this->line->update($user, $notrigger);
3333 if ($result > 0) {
3334 // Reorder if child line
3335 if (!empty($fk_parent_line)) {
3336 $this->line_order(true, 'DESC');
3337 }
3338
3339 // Mise a jour info denormalisees
3340 $this->update_price(1, 'auto');
3341
3342 $this->db->commit();
3343 return $result;
3344 } else {
3345 $this->error = $this->line->error;
3346
3347 $this->db->rollback();
3348 return -1;
3349 }
3350 } else {
3351 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3352 $this->errors = array('OrderStatusMakeOperationForbidden');
3353 return -2;
3354 }
3355 }
3356
3364 public function update(User $user, $notrigger = 0)
3365 {
3366 global $conf;
3367
3368 $error = 0;
3369
3370 // Clean parameters
3371 if (isset($this->ref)) {
3372 $this->ref = trim($this->ref);
3373 }
3374 if (isset($this->ref_client)) {
3375 $this->ref_client = trim($this->ref_client);
3376 }
3377 if (isset($this->ref_customer)) {
3378 $this->ref_customer = trim($this->ref_customer);
3379 }
3380 if (isset($this->note) || isset($this->note_private)) {
3381 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3382 }
3383 if (isset($this->note_public)) {
3384 $this->note_public = trim($this->note_public);
3385 }
3386 if (isset($this->model_pdf)) {
3387 $this->model_pdf = trim($this->model_pdf);
3388 }
3389 if (isset($this->import_key)) {
3390 $this->import_key = trim($this->import_key);
3391 }
3392 $delivery_date = $this->delivery_date;
3393
3394 // Check parameters
3395 // Put here code to add control on parameters values
3396
3397 // Update request
3398 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3399
3400 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3401 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3402 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3403 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3404 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3405 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3406 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3407 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3408 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3409 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3410 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3411 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3412 $sql .= " fk_user_modif=".(isset($user->id) ? $user->id : "null").",";
3413 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? $this->user_validation_id : "null").",";
3414 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3415 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3416 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3417 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3418 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3419 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3420 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3421 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3422 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3423 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3424 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3425 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3426
3427 $sql .= " WHERE rowid=".((int) $this->id);
3428
3429 $this->db->begin();
3430
3431 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3432 $resql = $this->db->query($sql);
3433 if (!$resql) {
3434 $error++;
3435 $this->errors[] = "Error ".$this->db->lasterror();
3436 }
3437
3438 if (!$error) {
3439 $result = $this->insertExtraFields();
3440 if ($result < 0) {
3441 $error++;
3442 }
3443 }
3444
3445 if (!$error && !$notrigger) {
3446 // Call trigger
3447 $result = $this->call_trigger('ORDER_MODIFY', $user);
3448 if ($result < 0) {
3449 $error++;
3450 }
3451 // End call triggers
3452 }
3453
3454 // Commit or rollback
3455 if ($error) {
3456 foreach ($this->errors as $errmsg) {
3457 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3458 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3459 }
3460 $this->db->rollback();
3461 return -1 * $error;
3462 } else {
3463 $this->db->commit();
3464 return 1;
3465 }
3466 }
3467
3475 public function delete($user, $notrigger = 0)
3476 {
3477 global $conf, $langs;
3478 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3479
3480 $error = 0;
3481
3482 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3483
3484 $this->db->begin();
3485
3486 if (!$notrigger) {
3487 // Call trigger
3488 $result = $this->call_trigger('ORDER_DELETE', $user);
3489 if ($result < 0) {
3490 $error++;
3491 }
3492 // End call triggers
3493 }
3494
3495 // Test we can delete
3496 if ($this->countNbOfShipments() != 0) {
3497 $this->errors[] = $langs->trans('SomeShipmentExists');
3498 $error++;
3499 }
3500
3501 // Delete extrafields of lines and lines
3502 if (!$error && !empty($this->table_element_line)) {
3503 $tabletodelete = $this->table_element_line;
3504 $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).")";
3505 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3506 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3507 $error++;
3508 $this->error = $this->db->lasterror();
3509 $this->errors[] = $this->error;
3510 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3511 }
3512 }
3513
3514 if (!$error) {
3515 // Delete linked object
3516 $res = $this->deleteObjectLinked();
3517 if ($res < 0) {
3518 $error++;
3519 }
3520 }
3521
3522 if (!$error) {
3523 // Delete linked contacts
3524 $res = $this->delete_linked_contact();
3525 if ($res < 0) {
3526 $error++;
3527 }
3528 }
3529
3530 // Removed extrafields of object
3531 if (!$error) {
3532 $result = $this->deleteExtraFields();
3533 if ($result < 0) {
3534 $error++;
3535 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3536 }
3537 }
3538
3539 // Delete main record
3540 if (!$error) {
3541 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3542 $res = $this->db->query($sql);
3543 if (!$res) {
3544 $error++;
3545 $this->error = $this->db->lasterror();
3546 $this->errors[] = $this->error;
3547 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3548 }
3549 }
3550
3551 // Delete record into ECM index and physically
3552 if (!$error) {
3553 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3554 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3555 if (!$res) {
3556 $error++;
3557 }
3558 }
3559
3560 if (!$error) {
3561 // We remove directory
3562 $ref = dol_sanitizeFileName($this->ref);
3563 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3564 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3565 $file = $dir."/".$ref.".pdf";
3566 if (file_exists($file)) {
3567 dol_delete_preview($this);
3568
3569 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3570 $this->error = 'ErrorFailToDeleteFile';
3571 $this->errors[] = $this->error;
3572 $this->db->rollback();
3573 return 0;
3574 }
3575 }
3576 if (file_exists($dir)) {
3577 $res = @dol_delete_dir_recursive($dir);
3578 if (!$res) {
3579 $this->error = 'ErrorFailToDeleteDir';
3580 $this->errors[] = $this->error;
3581 $this->db->rollback();
3582 return 0;
3583 }
3584 }
3585 }
3586 }
3587
3588 if (!$error) {
3589 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3590 $this->db->commit();
3591 return 1;
3592 } else {
3593 $this->db->rollback();
3594 return -1;
3595 }
3596 }
3597
3598
3599 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3607 public function load_board($user, $mode)
3608 {
3609 // phpcs:enable
3610 global $conf, $langs;
3611
3612 $clause = " WHERE";
3613
3614 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3615 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3616 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3617 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3618 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3619 $clause = " AND";
3620 }
3621 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3622 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3623 if ($mode == 'toship') {
3624 // An order to ship is an open order (validated or in progress)
3625 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3626 }
3627 if ($mode == 'tobill') {
3628 // An order to bill is an order not already billed
3629 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3630 }
3631 if ($mode == 'shippedtobill') {
3632 // An order shipped and to bill is a delivered order not already billed
3633 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3634 }
3635 if ($user->socid) {
3636 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3637 }
3638
3639 $resql = $this->db->query($sql);
3640 if ($resql) {
3641 $delay_warning = 0;
3642 $label = $labelShort = $url = '';
3643 if ($mode == 'toship') {
3644 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3645 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3646 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3647 $labelShort = $langs->transnoentitiesnoconv("Opened");
3648 }
3649 if ($mode == 'tobill') {
3650 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3651 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3652 $labelShort = $langs->trans("ToBill");
3653 }
3654 if ($mode == 'shippedtobill') {
3655 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3656 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3657 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3658 }
3659
3660 $response = new WorkboardResponse();
3661
3662 $response->warning_delay = $delay_warning;
3663 $response->label = $label;
3664 $response->labelShort = $labelShort;
3665 $response->url = $url;
3666 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3667 $response->img = img_object('', "order");
3668
3669 $generic_commande = new Commande($this->db);
3670
3671 while ($obj = $this->db->fetch_object($resql)) {
3672 $response->nbtodo++;
3673 $response->total += $obj->total_ht;
3674
3675 $generic_commande->statut = $obj->fk_statut;
3676 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3677 $generic_commande->date = $this->db->jdate($obj->date_commande);
3678 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3679
3680 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3681 $response->nbtodolate++;
3682 }
3683 }
3684
3685 return $response;
3686 } else {
3687 $this->error = $this->db->error();
3688 return -1;
3689 }
3690 }
3691
3697 public function getLabelSource()
3698 {
3699 global $langs;
3700
3701 $label = $langs->trans('OrderSource'.$this->source);
3702
3703 if ($label == 'OrderSource') {
3704 return '';
3705 }
3706 return $label;
3707 }
3708
3715 public function getLibStatut($mode)
3716 {
3717 return $this->LibStatut($this->statut, $this->billed, $mode);
3718 }
3719
3720 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3730 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3731 {
3732 // phpcs:enable
3733 global $langs, $hookmanager;
3734
3735 $billedtext = '';
3736 if (empty($donotshowbilled)) {
3737 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3738 }
3739
3740 $labelTooltip = '';
3741
3742 if ($status == self::STATUS_CANCELED) {
3743 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3744 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3745 $statusType = 'status9';
3746 } elseif ($status == self::STATUS_DRAFT) {
3747 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3748 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3749 $statusType = 'status0';
3750 } elseif ($status == self::STATUS_VALIDATED) {
3751 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3752 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3753 $statusType = 'status1';
3754 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3755 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3756 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3757 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3758 if (!empty($this->delivery_date)) {
3759 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3760 }
3761 $statusType = 'status4';
3762 } elseif ($status == self::STATUS_CLOSED) {
3763 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtext;
3764 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3765 $statusType = 'status6';
3766 } else {
3767 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3768 $labelStatusShort = '';
3769 $statusType = '';
3770 $mode = 0;
3771 }
3772
3773 $parameters = array(
3774 'status' => $status,
3775 'mode' => $mode,
3776 'billed' => $billed,
3777 'donotshowbilled' => $donotshowbilled
3778 );
3779
3780 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3781
3782 if ($reshook > 0) {
3783 return $hookmanager->resPrint;
3784 }
3785
3786 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3787 }
3788
3796 public function getTooltipContentArray($params)
3797 {
3798 global $conf, $langs, $user;
3799
3800 $langs->load('orders');
3801 $datas = [];
3802 $nofetch = !empty($params['nofetch']);
3803
3804 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3805 return ['optimize' => $langs->trans("Order")];
3806 }
3807
3808 if ($user->hasRight('commande', 'lire')) {
3809 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').'<u>'.$langs->trans("Order").'</u>';
3810 if (isset($this->statut)) {
3811 $datas['status'] = ' '.$this->getLibStatut(5);
3812 }
3813 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3814 if (!$nofetch) {
3815 $langs->load('companies');
3816 if (empty($this->thirdparty)) {
3817 $this->fetch_thirdparty();
3818 }
3819 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3820 }
3821 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3822 if (!$nofetch) {
3823 $langs->load('project');
3824 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3825 $res = $this->fetchProject();
3826 if ($res > 0 && $this->project instanceof Project) {
3827 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3828 }
3829 }
3830 }
3831 if (!empty($this->total_ht)) {
3832 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3833 }
3834 if (!empty($this->total_tva)) {
3835 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3836 }
3837 if (!empty($this->total_ttc)) {
3838 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3839 }
3840 if (!empty($this->date)) {
3841 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3842 }
3843 if (!empty($this->delivery_date)) {
3844 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3845 }
3846 }
3847
3848 return $datas;
3849 }
3850
3864 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3865 {
3866 global $conf, $langs, $user, $hookmanager;
3867
3868 if (!empty($conf->dol_no_mouse_hover)) {
3869 $notooltip = 1; // Force disable tooltips
3870 }
3871
3872 $result = '';
3873
3874 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3875 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3876 } else {
3877 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3878 }
3879
3880 if (!$user->hasRight('commande', 'lire')) {
3881 $option = 'nolink';
3882 }
3883
3884 if ($option !== 'nolink') {
3885 // Add param to save lastsearch_values or not
3886 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3887 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3888 $add_save_lastsearch_values = 1;
3889 }
3890 if ($add_save_lastsearch_values) {
3891 $url .= '&save_lastsearch_values=1';
3892 }
3893 }
3894
3895 if ($short) {
3896 return $url;
3897 }
3898 $params = [
3899 'id' => $this->id,
3900 'objecttype' => $this->element,
3901 'option' => $option,
3902 'nofetch' => 1,
3903 ];
3904 $classfortooltip = 'classfortooltip';
3905 $dataparams = '';
3906 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3907 $classfortooltip = 'classforajaxtooltip';
3908 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3909 $label = '';
3910 } else {
3911 $label = implode($this->getTooltipContentArray($params));
3912 }
3913
3914 $linkclose = '';
3915 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3916 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3917 $label = $langs->trans("Order");
3918 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
3919 }
3920 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
3921 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3922
3923 $target_value = array('_self', '_blank', '_parent', '_top');
3924 if (in_array($target, $target_value)) {
3925 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3926 }
3927 }
3928
3929 $linkstart = '<a href="'.$url.'"';
3930 $linkstart .= $linkclose.'>';
3931 $linkend = '</a>';
3932
3933 if ($option === 'nolink') {
3934 $linkstart = '';
3935 $linkend = '';
3936 }
3937
3938 $result .= $linkstart;
3939 if ($withpicto) {
3940 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3941 }
3942 if ($withpicto != 2) {
3943 $result .= $this->ref;
3944 }
3945 $result .= $linkend;
3946
3947 if ($addlinktonotes) {
3948 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3949 if ($txttoshow) {
3950 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3951 $result .= ' <span class="note inline-block">';
3952 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3953 $result .= img_picto('', 'note');
3954 $result .= '</a>';
3955 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3956 //$result.='</a>';
3957 $result .= '</span>';
3958 }
3959 }
3960
3961 global $action;
3962 $hookmanager->initHooks(array($this->element . 'dao'));
3963 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3964 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3965 if ($reshook > 0) {
3966 $result = $hookmanager->resPrint;
3967 } else {
3968 $result .= $hookmanager->resPrint;
3969 }
3970 return $result;
3971 }
3972
3973
3980 public function info($id)
3981 {
3982 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3983 $sql .= ' date_valid as datev,';
3984 $sql .= ' date_cloture as datecloture,';
3985 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3986 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
3987 $sql .= ' WHERE c.rowid = '.((int) $id);
3988 $result = $this->db->query($sql);
3989 if ($result) {
3990 if ($this->db->num_rows($result)) {
3991 $obj = $this->db->fetch_object($result);
3992 $this->id = $obj->rowid;
3993 if ($obj->fk_user_author) {
3994 $this->user_creation_id = $obj->fk_user_author;
3995 }
3996 if ($obj->fk_user_valid) {
3997 $this->user_validation_id = $obj->fk_user_valid;
3998 }
3999 if ($obj->fk_user_cloture) {
4000 $this->user_closing_id = $obj->fk_user_cloture;
4001 }
4002
4003 $this->date_creation = $this->db->jdate($obj->datec);
4004 $this->date_modification = $this->db->jdate($obj->datem);
4005 $this->date_validation = $this->db->jdate($obj->datev);
4006 $this->date_cloture = $this->db->jdate($obj->datecloture);
4007 }
4008
4009 $this->db->free($result);
4010 } else {
4011 dol_print_error($this->db);
4012 }
4013 }
4014
4015
4023 public function initAsSpecimen()
4024 {
4025 global $conf, $langs;
4026
4027 dol_syslog(get_class($this)."::initAsSpecimen");
4028
4029 // Load array of products prodids
4030 $num_prods = 0;
4031 $prodids = array();
4032 $sql = "SELECT rowid";
4033 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4034 $sql .= " WHERE entity IN (".getEntity('product').")";
4035 $sql .= $this->db->plimit(100);
4036
4037 $resql = $this->db->query($sql);
4038 if ($resql) {
4039 $num_prods = $this->db->num_rows($resql);
4040 $i = 0;
4041 while ($i < $num_prods) {
4042 $i++;
4043 $row = $this->db->fetch_row($resql);
4044 $prodids[$i] = $row[0];
4045 }
4046 }
4047
4048 // Initialise parameters
4049 $this->id = 0;
4050 $this->ref = 'SPECIMEN';
4051 $this->specimen = 1;
4052 $this->entity = $conf->entity;
4053 $this->socid = 1;
4054 $this->date = time();
4055 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4056 $this->cond_reglement_code = 'RECEP';
4057 $this->mode_reglement_code = 'CHQ';
4058 $this->availability_code = 'DSP';
4059 $this->demand_reason_code = 'SRC_00';
4060
4061 $this->note_public = 'This is a comment (public)';
4062 $this->note_private = 'This is a comment (private)';
4063
4064 $this->multicurrency_tx = 1;
4065 $this->multicurrency_code = $conf->currency;
4066
4067 $this->status = $this::STATUS_DRAFT;
4068
4069 // Lines
4070 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
4071 $xnbp = 0;
4072
4073 while ($xnbp < $nbp) {
4074 $line = new OrderLine($this->db);
4075
4076 $line->desc = $langs->trans("Description")." ".$xnbp;
4077 $line->qty = 1;
4078 $line->subprice = 100;
4079 $line->price = 100;
4080 $line->tva_tx = 20;
4081 if ($xnbp == 2) {
4082 $line->total_ht = 50;
4083 $line->total_ttc = 60;
4084 $line->total_tva = 10;
4085 $line->remise_percent = 50;
4086 } else {
4087 $line->total_ht = 100;
4088 $line->total_ttc = 120;
4089 $line->total_tva = 20;
4090 $line->remise_percent = 0;
4091 }
4092 if ($num_prods > 0) {
4093 $prodid = mt_rand(1, $num_prods);
4094 $line->fk_product = $prodids[$prodid];
4095 $line->product_ref = 'SPECIMEN';
4096 }
4097
4098 $this->lines[$xnbp] = $line;
4099
4100 $this->total_ht += $line->total_ht;
4101 $this->total_tva += $line->total_tva;
4102 $this->total_ttc += $line->total_ttc;
4103
4104 $xnbp++;
4105 }
4106
4107 return 1;
4108 }
4109
4110
4116 public function loadStateBoard()
4117 {
4118 global $user;
4119
4120 $this->nb = array();
4121 $clause = "WHERE";
4122
4123 $sql = "SELECT count(co.rowid) as nb";
4124 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as co";
4125 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4126 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
4127 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4128 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4129 $clause = "AND";
4130 }
4131 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4132
4133 $resql = $this->db->query($sql);
4134 if ($resql) {
4135 while ($obj = $this->db->fetch_object($resql)) {
4136 $this->nb["orders"] = $obj->nb;
4137 }
4138 $this->db->free($resql);
4139 return 1;
4140 } else {
4141 dol_print_error($this->db);
4142 $this->error = $this->db->error();
4143 return -1;
4144 }
4145 }
4146
4152 public function getLinesArray()
4153 {
4154 return $this->fetch_lines();
4155 }
4156
4168 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4169 {
4170 global $conf, $langs;
4171
4172 $langs->load("orders");
4173 $outputlangs->load("products");
4174
4175 if (!dol_strlen($modele)) {
4176 $modele = 'einstein';
4177
4178 if (!empty($this->model_pdf)) {
4179 $modele = $this->model_pdf;
4180 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4181 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4182 }
4183 }
4184
4185 $modelpath = "core/modules/commande/doc/";
4186
4187 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4188 }
4189
4190
4199 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4200 {
4201 $tables = array(
4202 'commande'
4203 );
4204
4205 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4206 }
4207
4216 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4217 {
4218 $tables = array(
4219 'commandedet',
4220 );
4221
4222 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4223 }
4224
4230 public function hasDelay()
4231 {
4232 global $conf;
4233
4234 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4235 return false; // Never late if not inside this status range
4236 }
4237
4238 $now = dol_now();
4239
4240 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4241 }
4242
4248 public function showDelay()
4249 {
4250 global $conf, $langs;
4251
4252 if (empty($this->delivery_date)) {
4253 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4254 } else {
4255 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4256 }
4257 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4258
4259 return $text;
4260 }
4261
4271 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4272 {
4273 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4274 }
4275}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
$object ref
Definition info.php:89
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.
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 clickable 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 a discount line into a sale order (as a sale order line) using an existing absolute discount (Con...
getNbOfProductsLines()
Return number of line with type product.
update(User $user, $notrigger=0)
Update database.
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.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetchProject()
Load the project with id $this->fk_project into this->project.
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)
deleteExtraFields()
Delete all extra fields values for the current object.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Superclass for orders classes.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
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.
Class to manage products or services.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
setSignedStatusCommon(User $user, int $status, int $notrigger=0, string $triggercode='')
Set signed status & call trigger with context message.
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_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
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.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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 '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_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)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a 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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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:90