dolibarr 21.0.4
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;
839
840 $this->db->commit();
841 return 1;
842 } else {
843 $this->db->rollback();
844 return -1;
845 }
846 } else {
847 $this->error = $this->db->lasterror();
848
849 $this->db->rollback();
850 return -1;
851 }
852 }
853 return 0;
854 }
855
863 public function cancel($idwarehouse = -1)
864 {
865 global $user, $langs;
866
867 $error = 0;
868
869 $this->db->begin();
870
871 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
872 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
873 $sql .= " fk_user_modif = ".((int) $user->id);
874 $sql .= " WHERE rowid = ".((int) $this->id);
875 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
876
877 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
878 if ($this->db->query($sql)) {
879 // If stock is decremented on validate order, we must reincrement it
880 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
881 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
882 $langs->load("agenda");
883
884 $num = count($this->lines);
885 for ($i = 0; $i < $num; $i++) {
886 if ($this->lines[$i]->fk_product > 0) {
887 $mouvP = new MouvementStock($this->db);
888 $mouvP->setOrigin($this->element, $this->id);
889 // We increment stock of product (and sub-products)
890 $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
891 if ($result < 0) {
892 $error++;
893 $this->error = $mouvP->error;
894 break;
895 }
896 }
897 }
898 }
899
900 if (!$error) {
901 // Call trigger
902 $result = $this->call_trigger('ORDER_CANCEL', $user);
903 if ($result < 0) {
904 $error++;
905 }
906 // End call triggers
907 }
908
909 if (!$error) {
910 $this->statut = self::STATUS_CANCELED;
911 $this->db->commit();
912 return 1;
913 } else {
914 foreach ($this->errors as $errmsg) {
915 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
916 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
917 }
918 $this->db->rollback();
919 return -1 * $error;
920 }
921 } else {
922 $this->error = $this->db->error();
923 $this->db->rollback();
924 return -1;
925 }
926 }
927
936 public function create($user, $notrigger = 0)
937 {
938 global $conf, $langs, $mysoc;
939 $error = 0;
940
941 // Clean parameters
942
943 // Set tmp vars
944 $date = ($this->date_commande ? $this->date_commande : $this->date);
945 $delivery_date = $this->delivery_date;
946
947 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
948 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
949 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
950 } else {
951 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
952 }
953 if (empty($this->fk_multicurrency)) {
954 $this->multicurrency_code = $conf->currency;
955 $this->fk_multicurrency = 0;
956 $this->multicurrency_tx = 1;
957 }
958 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
959 $this->entity = setEntity($this);
960
961 dol_syslog(get_class($this)."::create user=".$user->id);
962
963 // Check parameters
964 if (!empty($this->ref)) { // We check that ref is not already used
965 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
966 if ($result > 0) {
967 $this->error = 'ErrorRefAlreadyExists';
968 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
969 $this->db->rollback();
970 return -1;
971 }
972 }
973
974 $soc = new Societe($this->db);
975 $result = $soc->fetch($this->socid);
976 if ($result < 0) {
977 $this->error = "Failed to fetch company";
978 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
979 return -2;
980 }
981 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
982 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
983 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
984 return -1;
985 }
986
987 $now = dol_now();
988
989 $this->db->begin();
990
991 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
992 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
993 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
994 $sql .= ", fk_shipping_method";
995 $sql .= ", fk_warehouse";
996 $sql .= ", fk_incoterms, location_incoterms";
997 $sql .= ", entity, module_source, pos_source";
998 $sql .= ", fk_multicurrency";
999 $sql .= ", multicurrency_code";
1000 $sql .= ", multicurrency_tx";
1001 $sql .= ")";
1002 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
1003 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1004 $sql .= ", '".$this->db->idate($date)."'";
1005 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
1006 $sql .= ", '".$this->db->escape($this->note_private)."'";
1007 $sql .= ", '".$this->db->escape($this->note_public)."'";
1008 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
1009 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1010 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1011 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1012 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1013 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1014 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1015 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1016 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1017 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1018 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1019 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1020 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1021 $sql .= ", ".(int) $this->fk_incoterms;
1022 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1023 $sql .= ", ".(int) $this->entity;
1024 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1025 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1026 $sql .= ", ".(int) $this->fk_multicurrency;
1027 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1028 $sql .= ", ".(float) $this->multicurrency_tx;
1029 $sql .= ")";
1030
1031 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1032 $resql = $this->db->query($sql);
1033 if ($resql) {
1034 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1035
1036 if ($this->id) {
1037 $fk_parent_line = 0;
1038 $num = count($this->lines);
1039
1040 /*
1041 * Insert products details into db
1042 */
1043 for ($i = 0; $i < $num; $i++) {
1044 $line = $this->lines[$i];
1045
1046 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1047 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1048 if (!is_object($line)) {
1049 $line = (object) $line;
1050 }
1051
1052 // Reset fk_parent_line for no child products and special product
1053 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1054 $fk_parent_line = 0;
1055 }
1056
1057 // Complete vat rate with code
1058 $vatrate = $line->tva_tx;
1059 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1060 $vatrate .= ' ('.$line->vat_src_code.')';
1061 }
1062
1063 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1064 $originid = $line->origin_id;
1065 $origintype = $line->origin;
1066 } else {
1067 $originid = $line->id;
1068 $origintype = $this->element;
1069 }
1070
1071 // ref_ext
1072 if (empty($line->ref_ext)) {
1073 $line->ref_ext = '';
1074 }
1075
1076 $result = $this->addline(
1077 $line->desc,
1078 $line->subprice,
1079 $line->qty,
1080 $vatrate,
1081 $line->localtax1_tx,
1082 $line->localtax2_tx,
1083 $line->fk_product,
1084 $line->remise_percent,
1085 $line->info_bits,
1086 $line->fk_remise_except,
1087 'HT',
1088 0,
1089 $line->date_start,
1090 $line->date_end,
1091 $line->product_type,
1092 $line->rang,
1093 $line->special_code,
1094 $fk_parent_line,
1095 $line->fk_fournprice,
1096 $line->pa_ht,
1097 $line->label,
1098 $line->array_options,
1099 $line->fk_unit,
1100 $origintype,
1101 $originid,
1102 0,
1103 $line->ref_ext,
1104 1
1105 );
1106
1107 if ($result < 0) {
1108 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1109 $this->error = $this->db->lasterror();
1110 $this->errors[] = $this->error;
1111 dol_print_error($this->db);
1112 }
1113 $this->db->rollback();
1114 return -1;
1115 }
1116 // Defined the new fk_parent_line
1117 if ($result > 0 && $line->product_type == 9) {
1118 $fk_parent_line = $result;
1119 }
1120 }
1121
1122 $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.
1123
1124 // update ref
1125 $initialref = '(PROV'.$this->id.')';
1126 if (!empty($this->ref)) {
1127 $initialref = $this->ref;
1128 }
1129
1130 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1131 if ($this->db->query($sql)) {
1132 $this->ref = $initialref;
1133
1134 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1135 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1136 }
1137
1138 // Add object linked
1139 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1140 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1141 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, ...))
1142 foreach ($tmp_origin_id as $origin_id) {
1143 $ret = $this->add_object_linked($origin, $origin_id);
1144 if (!$ret) {
1145 $this->error = $this->db->lasterror();
1146 $error++;
1147 }
1148 }
1149 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1150 $origin_id = $tmp_origin_id;
1151 $ret = $this->add_object_linked($origin, $origin_id);
1152 if (!$ret) {
1153 $this->error = $this->db->lasterror();
1154 $error++;
1155 }
1156 }
1157 }
1158 }
1159
1160 if (!$error && $this->id && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1161 $originforcontact = $this->origin;
1162 $originidforcontact = $this->origin_id;
1163 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1164 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1165 $exp = new Expedition($this->db);
1166 $exp->fetch($this->origin_id);
1167 $exp->fetchObjectLinked();
1168 if (count($exp->linkedObjectsIds['commande']) > 0) {
1169 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1170 $originforcontact = 'commande';
1171 if (is_object($value)) {
1172 $originidforcontact = $value->id;
1173 } else {
1174 $originidforcontact = $value;
1175 }
1176 break; // We take first one
1177 }
1178 }
1179 }
1180
1181 $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";
1182 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1183
1184 $resqlcontact = $this->db->query($sqlcontact);
1185 if ($resqlcontact) {
1186 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1187 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1188 $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
1189 }
1190 } else {
1191 dol_print_error($this->db);
1192 }
1193 }
1194
1195 if (!$error) {
1196 $result = $this->insertExtraFields();
1197 if ($result < 0) {
1198 $error++;
1199 }
1200 }
1201
1202 if (!$error && !$notrigger) {
1203 // Call trigger
1204 $result = $this->call_trigger('ORDER_CREATE', $user);
1205 if ($result < 0) {
1206 $error++;
1207 }
1208 // End call triggers
1209 }
1210
1211 if (!$error) {
1212 $this->db->commit();
1213 return $this->id;
1214 } else {
1215 $this->db->rollback();
1216 return -1 * $error;
1217 }
1218 } else {
1219 $this->error = $this->db->lasterror();
1220 $this->db->rollback();
1221 return -1;
1222 }
1223 }
1224
1225 return 0;
1226 } else {
1227 $this->error = $this->db->lasterror();
1228 $this->db->rollback();
1229 return -1;
1230 }
1231 }
1232
1233
1241 public function createFromClone(User $user, $socid = 0)
1242 {
1243 global $conf, $user, $hookmanager;
1244
1245 $error = 0;
1246
1247 $this->db->begin();
1248
1249 // get lines so they will be clone
1250 foreach ($this->lines as $line) {
1251 $line->fetch_optionals();
1252 }
1253
1254 // Load source object
1255 $objFrom = clone $this;
1256
1257 // Change socid if needed
1258 if (!empty($socid) && $socid != $this->socid) {
1259 $objsoc = new Societe($this->db);
1260
1261 if ($objsoc->fetch($socid) > 0) {
1262 $this->socid = $objsoc->id;
1263 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1264 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1265 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1266 $this->fk_project = 0;
1267 $this->fk_delivery_address = 0;
1268 }
1269
1270 // TODO Change product price if multi-prices
1271 }
1272
1273 $this->id = 0;
1274 $this->ref = '';
1275 $this->statut = self::STATUS_DRAFT;
1276
1277 // Clear fields
1278 $this->user_author_id = $user->id;
1279 $this->user_validation_id = 0;
1280 $this->date = dol_now();
1281 $this->date_commande = dol_now();
1282 $this->date_creation = '';
1283 $this->date_validation = '';
1284 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1285 $this->ref_client = '';
1286 $this->ref_customer = '';
1287 }
1288
1289 // Do not clone ref_ext
1290 $num = count($this->lines);
1291 for ($i = 0; $i < $num; $i++) {
1292 $this->lines[$i]->ref_ext = '';
1293 }
1294
1295 // Create clone
1296 $this->context['createfromclone'] = 'createfromclone';
1297 $result = $this->create($user);
1298 if ($result < 0) {
1299 $error++;
1300 }
1301
1302 if (!$error) {
1303 // copy internal contacts
1304 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1305 $error++;
1306 }
1307 }
1308
1309 if (!$error) {
1310 // copy external contacts if same company
1311 if ($this->socid == $objFrom->socid) {
1312 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1313 $error++;
1314 }
1315 }
1316 }
1317
1318 if (!$error) {
1319 // Hook of thirdparty module
1320 if (is_object($hookmanager)) {
1321 $parameters = array('objFrom' => $objFrom);
1322 $action = '';
1323 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1324 if ($reshook < 0) {
1325 $this->setErrorsFromObject($hookmanager);
1326 $error++;
1327 }
1328 }
1329 }
1330
1331 unset($this->context['createfromclone']);
1332
1333 // End
1334 if (!$error) {
1335 $this->db->commit();
1336 return $this->id;
1337 } else {
1338 $this->db->rollback();
1339 return -1;
1340 }
1341 }
1342
1343
1351 public function createFromProposal($object, User $user)
1352 {
1353 global $conf, $hookmanager;
1354
1355 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1356 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1357
1358 $error = 0;
1359
1360 $this->date_commande = dol_now();
1361 $this->date = dol_now();
1362 $this->source = 0;
1363
1364 $num = count($object->lines);
1365 for ($i = 0; $i < $num; $i++) {
1366 $line = new OrderLine($this->db);
1367
1368 $line->libelle = $object->lines[$i]->libelle;
1369 $line->label = $object->lines[$i]->label;
1370 $line->desc = $object->lines[$i]->desc;
1371 $line->price = $object->lines[$i]->price;
1372 $line->subprice = $object->lines[$i]->subprice;
1373 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1374 $line->tva_tx = $object->lines[$i]->tva_tx;
1375 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1376 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1377 $line->qty = $object->lines[$i]->qty;
1378 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1379 $line->remise_percent = $object->lines[$i]->remise_percent;
1380 $line->fk_product = $object->lines[$i]->fk_product;
1381 $line->info_bits = $object->lines[$i]->info_bits;
1382 $line->product_type = $object->lines[$i]->product_type;
1383 $line->rang = $object->lines[$i]->rang;
1384 $line->special_code = $object->lines[$i]->special_code;
1385 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1386 $line->fk_unit = $object->lines[$i]->fk_unit;
1387
1388 $line->date_start = $object->lines[$i]->date_start;
1389 $line->date_end = $object->lines[$i]->date_end;
1390
1391 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1392 $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);
1393 $line->pa_ht = $marginInfos[0];
1394 $line->marge_tx = $marginInfos[1];
1395 $line->marque_tx = $marginInfos[2];
1396
1397 $line->origin = $object->element;
1398 $line->origin_id = $object->lines[$i]->id;
1399
1400 // get extrafields from original line
1401 $object->lines[$i]->fetch_optionals();
1402 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1403 $line->array_options[$options_key] = $value;
1404 }
1405
1406 $this->lines[$i] = $line;
1407 }
1408
1409 $this->entity = $object->entity;
1410 $this->socid = $object->socid;
1411 $this->fk_project = $object->fk_project;
1412 $this->cond_reglement_id = $object->cond_reglement_id;
1413 $this->deposit_percent = $object->deposit_percent;
1414 $this->mode_reglement_id = $object->mode_reglement_id;
1415 $this->fk_account = $object->fk_account;
1416 $this->availability_id = $object->availability_id;
1417 $this->demand_reason_id = $object->demand_reason_id;
1418 $this->delivery_date = $object->delivery_date;
1419 $this->shipping_method_id = $object->shipping_method_id;
1420 $this->warehouse_id = $object->warehouse_id;
1421 $this->fk_delivery_address = $object->fk_delivery_address;
1422 $this->contact_id = $object->contact_id;
1423 $this->ref_client = $object->ref_client;
1424 $this->ref_customer = $object->ref_client;
1425
1426 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1427 $this->note_private = $object->note_private;
1428 $this->note_public = $object->note_public;
1429 }
1430
1431 $this->origin = $object->element;
1432 $this->origin_id = $object->id;
1433
1434 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1435 if (isModEnabled('multicurrency')) {
1436 if (!empty($object->multicurrency_code)) {
1437 $this->multicurrency_code = $object->multicurrency_code;
1438 }
1439 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1440 $this->multicurrency_tx = $object->multicurrency_tx;
1441 }
1442
1443 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1444 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1445 $this->fk_multicurrency = $tmparray[0];
1446 $this->multicurrency_tx = $tmparray[1];
1447 } else {
1448 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1449 }
1450 if (empty($this->fk_multicurrency)) {
1451 $this->multicurrency_code = $conf->currency;
1452 $this->fk_multicurrency = 0;
1453 $this->multicurrency_tx = 1;
1454 }
1455 }
1456
1457 // get extrafields from original line
1458 $object->fetch_optionals();
1459
1460 $e = new ExtraFields($this->db);
1461 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1462
1463 foreach ($object->array_options as $options_key => $value) {
1464 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1465 $this->array_options[$options_key] = $value;
1466 }
1467 }
1468 // Possibility to add external linked objects with hooks
1469 $this->linked_objects[$this->origin] = $this->origin_id;
1470 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1471 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1472 }
1473
1474 $ret = $this->create($user);
1475
1476 if ($ret > 0) {
1477 // Actions hooked (by external module)
1478 $hookmanager->initHooks(array('orderdao'));
1479
1480 $parameters = array('objFrom' => $object);
1481 $action = '';
1482 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1483 if ($reshook < 0) {
1484 $this->setErrorsFromObject($hookmanager);
1485 $error++;
1486 }
1487
1488 if (!$error) {
1489 // Validate immediately the order
1490 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1491 $this->fetch($ret);
1492 $this->valid($user);
1493 }
1494 return $ret;
1495 } else {
1496 return -1;
1497 }
1498 } else {
1499 return -1;
1500 }
1501 }
1502
1503
1544 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)
1545 {
1546 global $mysoc, $langs, $user;
1547
1548 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1549 $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";
1550 $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";
1551 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1552
1553 if ($this->statut == self::STATUS_DRAFT) {
1554 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1555
1556 // Clean parameters
1557
1558 if (empty($remise_percent)) {
1559 $remise_percent = 0;
1560 }
1561 if (empty($qty)) {
1562 $qty = 0;
1563 }
1564 if (empty($info_bits)) {
1565 $info_bits = 0;
1566 }
1567 if (empty($rang)) {
1568 $rang = 0;
1569 }
1570 if (empty($txtva)) {
1571 $txtva = 0;
1572 }
1573 if (empty($txlocaltax1)) {
1574 $txlocaltax1 = 0;
1575 }
1576 if (empty($txlocaltax2)) {
1577 $txlocaltax2 = 0;
1578 }
1579 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1580 $fk_parent_line = 0;
1581 }
1582 if (empty($this->fk_multicurrency)) {
1583 $this->fk_multicurrency = 0;
1584 }
1585 if (empty($ref_ext)) {
1586 $ref_ext = '';
1587 }
1588
1589 $remise_percent = (float) price2num($remise_percent);
1590 $qty = (float) price2num($qty);
1591 $pu_ht = price2num($pu_ht);
1592 $pu_ht_devise = price2num($pu_ht_devise);
1593 $pu_ttc = price2num($pu_ttc);
1594 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
1595 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1596 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1597 }
1598 $txlocaltax1 = price2num($txlocaltax1);
1599 $txlocaltax2 = price2num($txlocaltax2);
1600 if ($price_base_type == 'HT') {
1601 $pu = $pu_ht;
1602 } else {
1603 $pu = $pu_ttc;
1604 }
1605 $label = trim($label);
1606 $desc = trim($desc);
1607
1608 // Check parameters
1609 if ($type < 0) {
1610 return -1;
1611 }
1612
1613 if ($date_start && $date_end && $date_start > $date_end) {
1614 $langs->load("errors");
1615 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1616 return -1;
1617 }
1618
1619 $this->db->begin();
1620
1621 $product_type = $type;
1622 if (!empty($fk_product) && $fk_product > 0) {
1623 $product = new Product($this->db);
1624 $result = $product->fetch($fk_product);
1625 $product_type = $product->type;
1626
1627 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
1628 $langs->load("errors");
1629 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1630 $this->errors[] = $this->error;
1631 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1632 $this->db->rollback();
1634 }
1635 }
1636 // Calcul du total TTC et de la TVA pour la ligne a partir de
1637 // qty, pu, remise_percent et txtva
1638 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1639 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1640
1641 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1642
1643 // Clean vat code
1644 $reg = array();
1645 $vat_src_code = '';
1646 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1647 $vat_src_code = $reg[1];
1648 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1649 }
1650
1651 $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);
1652
1653 /*var_dump($txlocaltax1);
1654 var_dump($txlocaltax2);
1655 var_dump($localtaxes_type);
1656 var_dump($tabprice);
1657 var_dump($tabprice[9]);
1658 var_dump($tabprice[10]);
1659 exit;*/
1660
1661 $total_ht = $tabprice[0];
1662 $total_tva = $tabprice[1];
1663 $total_ttc = $tabprice[2];
1664 $total_localtax1 = $tabprice[9];
1665 $total_localtax2 = $tabprice[10];
1666 $pu_ht = $tabprice[3];
1667
1668 // MultiCurrency
1669 $multicurrency_total_ht = $tabprice[16];
1670 $multicurrency_total_tva = $tabprice[17];
1671 $multicurrency_total_ttc = $tabprice[18];
1672 $pu_ht_devise = $tabprice[19];
1673
1674 // Rang to use
1675 $ranktouse = $rang;
1676
1677 if ($ranktouse == -1) {
1678 $rangmax = $this->line_max($fk_parent_line);
1679 $ranktouse = $rangmax + 1;
1680 }
1681
1682 // TODO A virer
1683 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1684 $price = $pu;
1685 $remise = 0;
1686 if ($remise_percent > 0) {
1687 $remise = round(((float) $pu * $remise_percent / 100), 2);
1688 $price = (float) $pu - $remise;
1689 }
1690
1691 // Insert line
1692 $this->line = new OrderLine($this->db);
1693
1694 $this->line->context = $this->context;
1695
1696 $this->line->fk_commande = $this->id;
1697 $this->line->label = $label;
1698 $this->line->desc = $desc;
1699 $this->line->qty = $qty;
1700 $this->line->ref_ext = $ref_ext;
1701
1702 $this->line->vat_src_code = $vat_src_code;
1703 $this->line->tva_tx = $txtva;
1704 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1705 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1706 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1707 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1708 $this->line->fk_product = $fk_product;
1709 $this->line->product_type = $product_type;
1710 $this->line->fk_remise_except = $fk_remise_except;
1711 $this->line->remise_percent = $remise_percent;
1712 $this->line->subprice = (float) $pu_ht;
1713 $this->line->rang = $ranktouse;
1714 $this->line->info_bits = $info_bits;
1715 $this->line->total_ht = (float) $total_ht;
1716 $this->line->total_tva = (float) $total_tva;
1717 $this->line->total_localtax1 = (float) $total_localtax1;
1718 $this->line->total_localtax2 = (float) $total_localtax2;
1719 $this->line->total_ttc = (float) $total_ttc;
1720 $this->line->special_code = $special_code;
1721 $this->line->origin = $origin;
1722 $this->line->origin_id = $origin_id;
1723 $this->line->fk_parent_line = $fk_parent_line;
1724 $this->line->fk_unit = $fk_unit;
1725
1726 $this->line->date_start = $date_start;
1727 $this->line->date_end = $date_end;
1728
1729 $this->line->fk_fournprice = $fk_fournprice;
1730 $this->line->pa_ht = $pa_ht; // Can be '' when not defined or 0 if defined to 0 or a price value
1731
1732 // Multicurrency
1733 $this->line->fk_multicurrency = $this->fk_multicurrency;
1734 $this->line->multicurrency_code = $this->multicurrency_code;
1735 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
1736 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
1737 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
1738 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
1739
1740 // TODO Do not use anymore
1741 $this->line->price = $price;
1742
1743 if (is_array($array_options) && count($array_options) > 0) {
1744 $this->line->array_options = $array_options;
1745 }
1746
1747 $result = $this->line->insert($user);
1748 if ($result > 0) {
1749 // Update denormalized fields at the order level
1750 if (empty($noupdateafterinsertline)) {
1751 $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.
1752 }
1753
1754 if ($result > 0) {
1755 if (!isset($this->context['createfromclone'])) {
1756 if (!empty($fk_parent_line)) {
1757 // Always reorder if child line
1758 $this->line_order(true, 'DESC');
1759 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1760 // Update all rank of all other lines starting from the same $ranktouse
1761 foreach ($this->lines as $tmpline) {
1762 if ($tmpline->rang >= $ranktouse) {
1763 if (!empty($tmpline->id)) {
1764 $this->updateRangOfLine($tmpline->id, $tmpline->rang + 1);
1765 }
1766 }
1767 }
1768 }
1769
1770 $this->lines[] = $this->line;
1771 }
1772
1773 $this->db->commit();
1774 return $this->line->id;
1775 } else {
1776 $this->db->rollback();
1777 return -1;
1778 }
1779 } else {
1780 $this->error = $this->line->error;
1781 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1782 $this->db->rollback();
1783 return -2;
1784 }
1785 } else {
1786 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1787 return -3;
1788 }
1789 }
1790
1791
1792 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1806 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1807 {
1808 // phpcs:enable
1809 global $conf, $mysoc;
1810
1811 if (!$qty) {
1812 $qty = 1;
1813 }
1814
1815 if ($idproduct > 0) {
1816 $prod = new Product($this->db);
1817 $prod->fetch($idproduct);
1818
1819 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1820 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1821 if (empty($tva_tx)) {
1822 $tva_npr = 0;
1823 }
1824 $vat_src_code = ''; // May be defined into tva_tx
1825
1826 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1827 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1828
1829 // multiprix
1830 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
1831 $price = $prod->multiprices[$this->thirdparty->price_level];
1832 } else {
1833 $price = $prod->price;
1834 }
1835
1836 $line = new OrderLine($this->db);
1837
1838 $line->context = $this->context;
1839
1840 $line->fk_product = $idproduct;
1841 $line->desc = $prod->description;
1842 $line->qty = $qty;
1843 $line->subprice = $price;
1844 $line->remise_percent = $remise_percent;
1845 $line->vat_src_code = $vat_src_code;
1846 $line->tva_tx = $tva_tx;
1847 $line->localtax1_tx = $localtax1_tx;
1848 $line->localtax2_tx = $localtax2_tx;
1849
1850 $line->product_ref = $prod->ref;
1851 $line->product_label = $prod->label;
1852 $line->product_desc = $prod->description;
1853 $line->fk_unit = $prod->fk_unit;
1854
1855 // Save the start and end date of the line in the object
1856 if ($date_start) {
1857 $line->date_start = $date_start;
1858 }
1859 if ($date_end) {
1860 $line->date_end = $date_end;
1861 }
1862
1863 $this->lines[] = $line;
1864
1882 }
1883 }
1884
1885
1895 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1896 {
1897 // Check parameters
1898 if (empty($id) && empty($ref) && empty($ref_ext)) {
1899 return -1;
1900 }
1901
1902 $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';
1903 $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';
1904 $sql .= ', c.fk_account';
1905 $sql .= ', c.date_commande, c.date_valid, c.tms';
1906 $sql .= ', c.date_livraison as delivery_date';
1907 $sql .= ', c.fk_shipping_method';
1908 $sql .= ', c.fk_warehouse';
1909 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1910 $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';
1911 $sql .= ', c.fk_incoterms, c.location_incoterms';
1912 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1913 $sql .= ", c.module_source, c.pos_source";
1914 $sql .= ", i.libelle as label_incoterms";
1915 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1916 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1917 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1918 $sql .= ', dr.code as demand_reason_code';
1919 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
1920 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1921 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1922 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1923 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1924 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1925
1926 if ($id) {
1927 $sql .= " WHERE c.rowid=".((int) $id);
1928 } else {
1929 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
1930 }
1931
1932 if ($ref) {
1933 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1934 }
1935 if ($ref_ext) {
1936 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1937 }
1938
1939 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1940 $result = $this->db->query($sql);
1941 if ($result) {
1942 $obj = $this->db->fetch_object($result);
1943 if ($obj) {
1944 $this->id = $obj->rowid;
1945 $this->entity = $obj->entity;
1946
1947 $this->ref = $obj->ref;
1948 $this->ref_client = $obj->ref_client;
1949 $this->ref_customer = $obj->ref_client;
1950 $this->ref_ext = $obj->ref_ext;
1951
1952 $this->socid = $obj->fk_soc;
1953 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1954
1955 $this->fk_project = $obj->fk_project;
1956 $this->project = null; // Clear if another value was already set by fetch_projet
1957
1958 $this->statut = $obj->status;
1959 $this->status = $obj->status;
1960
1961 $this->user_author_id = $obj->fk_user_author;
1962 $this->user_creation_id = $obj->fk_user_author;
1963 $this->user_validation_id = $obj->fk_user_valid;
1964 $this->user_modification_id = $obj->fk_user_modif;
1965 $this->total_ht = $obj->total_ht;
1966 $this->total_tva = $obj->total_tva;
1967 $this->total_localtax1 = $obj->total_localtax1;
1968 $this->total_localtax2 = $obj->total_localtax2;
1969 $this->total_ttc = $obj->total_ttc;
1970 $this->date = $this->db->jdate($obj->date_commande);
1971 $this->date_commande = $this->db->jdate($obj->date_commande);
1972 $this->date_creation = $this->db->jdate($obj->date_creation);
1973 $this->date_validation = $this->db->jdate($obj->date_valid);
1974 $this->date_modification = $this->db->jdate($obj->tms);
1975 $this->source = $obj->source;
1976 $this->billed = $obj->billed;
1977 $this->note = $obj->note_private; // deprecated
1978 $this->note_private = $obj->note_private;
1979 $this->note_public = $obj->note_public;
1980 $this->model_pdf = $obj->model_pdf;
1981 $this->last_main_doc = $obj->last_main_doc;
1982 $this->mode_reglement_id = $obj->fk_mode_reglement;
1983 $this->mode_reglement_code = $obj->mode_reglement_code;
1984 $this->mode_reglement = $obj->mode_reglement_libelle;
1985 $this->cond_reglement_id = $obj->fk_cond_reglement;
1986 $this->cond_reglement_code = $obj->cond_reglement_code;
1987 $this->cond_reglement = $obj->cond_reglement_libelle;
1988 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1989 $this->deposit_percent = $obj->deposit_percent;
1990 $this->fk_account = $obj->fk_account;
1991 $this->availability_id = $obj->fk_availability;
1992 $this->availability_code = $obj->availability_code;
1993 $this->availability = $obj->availability_label;
1994 $this->demand_reason_id = $obj->fk_input_reason;
1995 $this->demand_reason_code = $obj->demand_reason_code;
1996 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1997 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1998 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1999 $this->fk_delivery_address = $obj->fk_delivery_address;
2000 $this->module_source = $obj->module_source;
2001 $this->pos_source = $obj->pos_source;
2002
2003 //Incoterms
2004 $this->fk_incoterms = $obj->fk_incoterms;
2005 $this->location_incoterms = $obj->location_incoterms;
2006 $this->label_incoterms = $obj->label_incoterms;
2007
2008 // Multicurrency
2009 $this->fk_multicurrency = $obj->fk_multicurrency;
2010 $this->multicurrency_code = $obj->multicurrency_code;
2011 $this->multicurrency_tx = $obj->multicurrency_tx;
2012 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
2013 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
2014 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2015
2016 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2017
2018 $this->lines = array();
2019
2020 // Retrieve all extrafield
2021 // fetch optionals attributes and labels
2022 $this->fetch_optionals();
2023
2024 $this->db->free($result);
2025
2026 // Lines
2027 $result = $this->fetch_lines();
2028 if ($result < 0) {
2029 return -3;
2030 }
2031 return 1;
2032 } else {
2033 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2034 return 0;
2035 }
2036 } else {
2037 $this->error = $this->db->error();
2038 return -1;
2039 }
2040 }
2041
2042
2043 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2050 public function insert_discount($idremise)
2051 {
2052 // phpcs:enable
2053 global $langs;
2054
2055 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2056 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2057
2058 $this->db->begin();
2059
2060 $remise = new DiscountAbsolute($this->db);
2061 $result = $remise->fetch($idremise);
2062
2063 if ($result > 0) {
2064 if ($remise->fk_facture) { // Protection against multiple submission
2065 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2066 $this->db->rollback();
2067 return -5;
2068 }
2069
2070 $line = new OrderLine($this->db);
2071
2072 $line->fk_commande = $this->id;
2073 $line->fk_remise_except = $remise->id;
2074 $line->desc = $remise->description; // Description ligne
2075 $line->vat_src_code = $remise->vat_src_code;
2076 $line->tva_tx = $remise->tva_tx;
2077 $line->subprice = -(float) $remise->amount_ht;
2078 $line->price = -(float) $remise->amount_ht;
2079 $line->fk_product = 0; // Id produit predefini
2080 $line->qty = 1;
2081 $line->remise_percent = 0;
2082 $line->rang = -1;
2083 $line->info_bits = 2;
2084
2085 $line->total_ht = -(float) $remise->amount_ht;
2086 $line->total_tva = -(float) $remise->amount_tva;
2087 $line->total_ttc = -(float) $remise->amount_ttc;
2088
2089 $result = $line->insert();
2090 if ($result > 0) {
2091 $result = $this->update_price(1);
2092 if ($result > 0) {
2093 $this->db->commit();
2094 return 1;
2095 } else {
2096 $this->db->rollback();
2097 return -1;
2098 }
2099 } else {
2100 $this->error = $line->error;
2101 $this->errors = $line->errors;
2102 $this->db->rollback();
2103 return -2;
2104 }
2105 } else {
2106 $this->db->rollback();
2107 return -2;
2108 }
2109 }
2110
2111
2112 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2120 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2121 {
2122 // phpcs:enable
2123 global $langs, $conf;
2124
2125 $this->lines = array();
2126
2127 $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,';
2128 $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,';
2129 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2130 $sql .= ' l.fk_unit,';
2131 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2132 $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,';
2133 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2134 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2135 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2136 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2137 if ($only_product) {
2138 $sql .= ' AND p.fk_product_type = 0';
2139 }
2140 $sql .= ' ORDER BY l.rang, l.rowid';
2141
2142 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2143 $result = $this->db->query($sql);
2144 if ($result) {
2145 $num = $this->db->num_rows($result);
2146
2147 $i = 0;
2148 while ($i < $num) {
2149 $objp = $this->db->fetch_object($result);
2150
2151 $line = new OrderLine($this->db);
2152
2153 $line->rowid = $objp->rowid;
2154 $line->id = $objp->rowid;
2155 $line->fk_commande = $objp->fk_commande;
2156 $line->commande_id = $objp->fk_commande;
2157 $line->label = $objp->custom_label;
2158 $line->desc = $objp->description;
2159 $line->description = $objp->description; // Description line
2160 $line->product_type = $objp->product_type;
2161 $line->qty = $objp->qty;
2162 $line->ref_ext = $objp->ref_ext;
2163
2164 $line->vat_src_code = $objp->vat_src_code;
2165 $line->tva_tx = $objp->tva_tx;
2166 $line->localtax1_tx = $objp->localtax1_tx;
2167 $line->localtax2_tx = $objp->localtax2_tx;
2168 $line->localtax1_type = $objp->localtax1_type;
2169 $line->localtax2_type = $objp->localtax2_type;
2170 $line->total_ht = $objp->total_ht;
2171 $line->total_ttc = $objp->total_ttc;
2172 $line->total_tva = $objp->total_tva;
2173 $line->total_localtax1 = $objp->total_localtax1;
2174 $line->total_localtax2 = $objp->total_localtax2;
2175 $line->subprice = $objp->subprice;
2176 $line->fk_remise_except = $objp->fk_remise_except;
2177 $line->remise_percent = $objp->remise_percent;
2178 $line->price = $objp->price;
2179 $line->fk_product = $objp->fk_product;
2180 $line->fk_fournprice = $objp->fk_fournprice;
2181 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2182 $line->pa_ht = $marginInfos[0];
2183 $line->marge_tx = $marginInfos[1];
2184 $line->marque_tx = $marginInfos[2];
2185 $line->rang = $objp->rang;
2186 $line->info_bits = $objp->info_bits;
2187 $line->special_code = $objp->special_code;
2188 $line->fk_parent_line = $objp->fk_parent_line;
2189
2190 $line->ref = $objp->product_ref;
2191 $line->libelle = $objp->product_label;
2192
2193 $line->product_ref = $objp->product_ref;
2194 $line->product_label = $objp->product_label;
2195 $line->product_tosell = $objp->product_tosell;
2196 $line->product_tobuy = $objp->product_tobuy;
2197 $line->product_desc = $objp->product_desc;
2198 $line->product_tobatch = $objp->product_tobatch;
2199 $line->product_barcode = $objp->product_barcode;
2200
2201 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2202 $line->fk_unit = $objp->fk_unit;
2203
2204 $line->weight = $objp->weight;
2205 $line->weight_units = $objp->weight_units;
2206 $line->volume = $objp->volume;
2207 $line->volume_units = $objp->volume_units;
2208
2209 $line->date_start = $this->db->jdate($objp->date_start);
2210 $line->date_end = $this->db->jdate($objp->date_end);
2211
2212 // Multicurrency
2213 $line->fk_multicurrency = $objp->fk_multicurrency;
2214 $line->multicurrency_code = $objp->multicurrency_code;
2215 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2216 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2217 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2218 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2219
2220 $line->fetch_optionals();
2221
2222 // multilangs
2223 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2224 $tmpproduct = new Product($this->db);
2225 $tmpproduct->fetch($objp->fk_product);
2226 $tmpproduct->getMultiLangs();
2227
2228 $line->multilangs = $tmpproduct->multilangs;
2229 }
2230
2231 $this->lines[$i] = $line;
2232
2233 $i++;
2234 }
2235
2236 $this->db->free($result);
2237
2238 return 1;
2239 } else {
2240 $this->error = $this->db->error();
2241 return -3;
2242 }
2243 }
2244
2245
2251 public function getNbOfProductsLines()
2252 {
2253 $nb = 0;
2254 foreach ($this->lines as $line) {
2255 if ($line->product_type == 0) {
2256 $nb++;
2257 }
2258 }
2259 return $nb;
2260 }
2261
2267 public function getNbOfServicesLines()
2268 {
2269 $nb = 0;
2270 foreach ($this->lines as $line) {
2271 if ($line->product_type == 1) {
2272 $nb++;
2273 }
2274 }
2275 return $nb;
2276 }
2277
2283 public function getNbOfShipments()
2284 {
2285 $nb = 0;
2286
2287 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2288 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2289 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2290 $sql .= ' WHERE';
2291 $sql .= ' ed.fk_elementdet = cd.rowid';
2292 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2293 //print $sql;
2294
2295 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2296 $resql = $this->db->query($sql);
2297 if ($resql) {
2298 $obj = $this->db->fetch_object($resql);
2299 if ($obj) {
2300 $nb = $obj->nb;
2301 }
2302
2303 $this->db->free($resql);
2304 return $nb;
2305 } else {
2306 $this->error = $this->db->lasterror();
2307 return -1;
2308 }
2309 }
2310
2319 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2320 {
2321 $this->expeditions = array();
2322
2323 $sql = 'SELECT cd.rowid, cd.fk_product,';
2324 $sql .= ' sum(ed.qty) as qty';
2325 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2326 if ($filtre_statut >= 0) {
2327 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2328 }
2329 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2330 $sql .= ' WHERE';
2331 if ($filtre_statut >= 0) {
2332 $sql .= ' ed.fk_expedition = e.rowid AND';
2333 }
2334 $sql .= ' ed.fk_elementdet = cd.rowid';
2335 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2336 if ($fk_product > 0) {
2337 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2338 }
2339 if ($filtre_statut >= 0) {
2340 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2341 }
2342 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2343 //print $sql;
2344
2345 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2346 $resql = $this->db->query($sql);
2347 if ($resql) {
2348 $num = $this->db->num_rows($resql);
2349 $i = 0;
2350 while ($i < $num) {
2351 $obj = $this->db->fetch_object($resql);
2352 $this->expeditions[$obj->rowid] = $obj->qty;
2353 $i++;
2354 }
2355 $this->db->free($resql);
2356 return $num;
2357 } else {
2358 $this->error = $this->db->lasterror();
2359 return -1;
2360 }
2361 }
2362
2368 public function countNbOfShipments()
2369 {
2370 $sql = 'SELECT count(*)';
2371 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2372 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2373 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2374 $sql .= " AND el.sourcetype = 'commande'";
2375 $sql .= " AND el.fk_target = e.rowid";
2376 $sql .= " AND el.targettype = 'shipping'";
2377
2378 $resql = $this->db->query($sql);
2379 if ($resql) {
2380 $row = $this->db->fetch_row($resql);
2381 return $row[0];
2382 } else {
2383 dol_print_error($this->db);
2384 }
2385
2386 return 0;
2387 }
2388
2389 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2398 /*public function stock_array($filtre_statut = self::STATUS_CANCELED)
2399 {
2400 // phpcs:enable
2401 $this->stocks = array();
2402
2403 // Tableau des id de produit de la commande
2404 $array_of_product = array();
2405
2406 // Recherche total en stock pour chaque produit
2407 // TODO $array_of_product est défini vide juste au dessus !!
2408 if (count($array_of_product)) {
2409 $sql = "SELECT fk_product, sum(ps.reel) as total";
2410 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2411 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2412 $sql .= ' GROUP BY fk_product';
2413 $resql = $this->db->query($sql);
2414 if ($resql) {
2415 $num = $this->db->num_rows($resql);
2416 $i = 0;
2417 while ($i < $num) {
2418 $obj = $this->db->fetch_object($resql);
2419 $this->stocks[$obj->fk_product] = $obj->total;
2420 $i++;
2421 }
2422 $this->db->free($resql);
2423 }
2424 }
2425 return 0;
2426 }*/
2427
2436 public function deleteLine($user = null, $lineid = 0, $id = 0)
2437 {
2438 if ($this->statut == self::STATUS_DRAFT) {
2439 $this->db->begin();
2440
2441 // Delete line
2442 $line = new OrderLine($this->db);
2443
2444 $line->context = $this->context;
2445
2446 // Load data
2447 $line->fetch($lineid);
2448
2449 if ($id > 0 && $line->fk_commande != $id) {
2450 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2451 return -1;
2452 }
2453
2454 // Memorize previous line for triggers
2455 $staticline = clone $line;
2456 $line->oldline = $staticline;
2457
2458 if ($line->delete($user) > 0) {
2459 $result = $this->update_price(1);
2460
2461 if ($result > 0) {
2462 $this->db->commit();
2463 return 1;
2464 } else {
2465 $this->db->rollback();
2466 $this->error = $this->db->lasterror();
2467 return -1;
2468 }
2469 } else {
2470 $this->db->rollback();
2471 $this->error = $line->error;
2472 return -1;
2473 }
2474 } else {
2475 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2476 return -1;
2477 }
2478 }
2479
2480 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2491 public function set_remise($user, $remise, $notrigger = 0)
2492 {
2493 // phpcs:enable
2494 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2495 // @phan-suppress-next-line PhanDeprecatedFunction
2496 return $this->setDiscount($user, $remise, $notrigger);
2497 }
2498
2507 public function setDiscount($user, $remise, $notrigger = 0)
2508 {
2509 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2510
2511 if ($user->hasRight('commande', 'creer')) {
2512 $error = 0;
2513
2514 $this->db->begin();
2515
2516 $remise = price2num($remise, 2);
2517
2518 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2519 $sql .= ' SET remise_percent = '.((float) $remise);
2520 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2521
2522 dol_syslog(__METHOD__, LOG_DEBUG);
2523 $resql = $this->db->query($sql);
2524 if (!$resql) {
2525 $this->errors[] = $this->db->error();
2526 $error++;
2527 }
2528
2529 if (!$error) {
2530 $this->oldcopy = clone $this;
2531 $this->remise_percent = $remise;
2532 $this->update_price(1);
2533 }
2534
2535 if (!$notrigger && empty($error)) {
2536 // Call trigger
2537 $result = $this->call_trigger('ORDER_MODIFY', $user);
2538 if ($result < 0) {
2539 $error++;
2540 }
2541 // End call triggers
2542 }
2543
2544 if (!$error) {
2545 $this->db->commit();
2546 return 1;
2547 } else {
2548 foreach ($this->errors as $errmsg) {
2549 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2550 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2551 }
2552 $this->db->rollback();
2553 return -1 * $error;
2554 }
2555 }
2556
2557 return 0;
2558 }
2559
2560
2561 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2570 /*
2571 public function set_remise_absolue($user, $remise, $notrigger = 0)
2572 {
2573 // phpcs:enable
2574 if (empty($remise)) {
2575 $remise = 0;
2576 }
2577
2578 $remise = price2num($remise);
2579
2580 if ($user->hasRight('commande', 'creer')) {
2581 $error = 0;
2582
2583 $this->db->begin();
2584
2585 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2586 $sql .= ' SET remise_absolue = '.((float) $remise);
2587 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2588
2589 dol_syslog(__METHOD__, LOG_DEBUG);
2590 $resql = $this->db->query($sql);
2591 if (!$resql) {
2592 $this->errors[] = $this->db->error();
2593 $error++;
2594 }
2595
2596 if (!$error) {
2597 $this->oldcopy = clone $this;
2598 $this->remise_absolue = $remise;
2599 $this->update_price(1);
2600 }
2601
2602 if (!$notrigger && empty($error)) {
2603 // Call trigger
2604 $result = $this->call_trigger('ORDER_MODIFY', $user);
2605 if ($result < 0) {
2606 $error++;
2607 }
2608 // End call triggers
2609 }
2610
2611 if (!$error) {
2612 $this->db->commit();
2613 return 1;
2614 } else {
2615 foreach ($this->errors as $errmsg) {
2616 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2617 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2618 }
2619 $this->db->rollback();
2620 return -1 * $error;
2621 }
2622 }
2623
2624 return 0;
2625 }
2626 */
2627
2628 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2637 public function set_date($user, $date, $notrigger = 0)
2638 {
2639 // phpcs:enable
2640 if ($user->hasRight('commande', 'creer')) {
2641 $error = 0;
2642
2643 $this->db->begin();
2644
2645 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2646 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2647 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2648
2649 dol_syslog(__METHOD__, LOG_DEBUG);
2650 $resql = $this->db->query($sql);
2651 if (!$resql) {
2652 $this->errors[] = $this->db->error();
2653 $error++;
2654 }
2655
2656 if (!$error) {
2657 $this->oldcopy = clone $this;
2658 $this->date = $date;
2659 }
2660
2661 if (!$notrigger && empty($error)) {
2662 // Call trigger
2663 $result = $this->call_trigger('ORDER_MODIFY', $user);
2664 if ($result < 0) {
2665 $error++;
2666 }
2667 // End call triggers
2668 }
2669
2670 if (!$error) {
2671 $this->db->commit();
2672 return 1;
2673 } else {
2674 foreach ($this->errors as $errmsg) {
2675 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2676 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2677 }
2678 $this->db->rollback();
2679 return -1 * $error;
2680 }
2681 } else {
2682 return -2;
2683 }
2684 }
2685
2686 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2696 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2697 {
2698 // phpcs:enable
2699 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2700 }
2701
2710 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2711 {
2712 if ($user->hasRight('commande', 'creer')) {
2713 $error = 0;
2714
2715 $this->db->begin();
2716
2717 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2718 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2719 $sql .= " WHERE rowid = ".((int) $this->id);
2720
2721 dol_syslog(__METHOD__, LOG_DEBUG);
2722 $resql = $this->db->query($sql);
2723 if (!$resql) {
2724 $this->errors[] = $this->db->error();
2725 $error++;
2726 }
2727
2728 if (!$error) {
2729 $this->oldcopy = clone $this;
2730 $this->delivery_date = $delivery_date;
2731 }
2732
2733 if (!$notrigger && empty($error)) {
2734 // Call trigger
2735 $result = $this->call_trigger('ORDER_MODIFY', $user);
2736 if ($result < 0) {
2737 $error++;
2738 }
2739 // End call triggers
2740 }
2741
2742 if (!$error) {
2743 $this->db->commit();
2744 return 1;
2745 } else {
2746 foreach ($this->errors as $errmsg) {
2747 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2748 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2749 }
2750 $this->db->rollback();
2751 return -1 * $error;
2752 }
2753 } else {
2754 return -2;
2755 }
2756 }
2757
2758 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2772 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2773 {
2774 // phpcs:enable
2775 global $user;
2776
2777 $ga = array();
2778
2779 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2780 $sql .= " c.rowid as cid, c.ref";
2781 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2782 $sql .= ", sc.fk_soc, sc.fk_user";
2783 }
2784 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2785 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2786 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2787 }
2788 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2789 $sql .= " AND c.fk_soc = s.rowid";
2790 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2791 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2792 }
2793 if ($socid) {
2794 $sql .= " AND s.rowid = ".((int) $socid);
2795 }
2796 if ($draft) {
2797 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2798 }
2799 if (is_object($excluser)) {
2800 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2801 }
2802 $sql .= $this->db->order($sortfield, $sortorder);
2803 $sql .= $this->db->plimit($limit, $offset);
2804
2805 $result = $this->db->query($sql);
2806 if ($result) {
2807 $numc = $this->db->num_rows($result);
2808 if ($numc) {
2809 $i = 0;
2810 while ($i < $numc) {
2811 $obj = $this->db->fetch_object($result);
2812
2813 if ($shortlist == 1) {
2814 $ga[$obj->cid] = $obj->ref;
2815 } elseif ($shortlist == 2) {
2816 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2817 } else {
2818 $ga[$i]['id'] = $obj->cid;
2819 $ga[$i]['ref'] = $obj->ref;
2820 $ga[$i]['name'] = $obj->name;
2821 }
2822 $i++;
2823 }
2824 }
2825 return $ga;
2826 } else {
2827 dol_print_error($this->db);
2828 return -1;
2829 }
2830 }
2831
2839 public function availability($availability_id, $notrigger = 0)
2840 {
2841 global $user;
2842
2843 dol_syslog('Commande::availability('.$availability_id.')');
2844 if ($this->statut >= self::STATUS_DRAFT) {
2845 $error = 0;
2846
2847 $this->db->begin();
2848
2849 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2850 $sql .= ' SET fk_availability = '.((int) $availability_id);
2851 $sql .= ' WHERE rowid='.((int) $this->id);
2852
2853 dol_syslog(__METHOD__, LOG_DEBUG);
2854 $resql = $this->db->query($sql);
2855 if (!$resql) {
2856 $this->errors[] = $this->db->error();
2857 $error++;
2858 }
2859
2860 if (!$error) {
2861 $this->oldcopy = clone $this;
2862 $this->availability_id = $availability_id;
2863 }
2864
2865 if (!$notrigger && empty($error)) {
2866 // Call trigger
2867 $result = $this->call_trigger('ORDER_MODIFY', $user);
2868 if ($result < 0) {
2869 $error++;
2870 }
2871 // End call triggers
2872 }
2873
2874 if (!$error) {
2875 $this->db->commit();
2876 return 1;
2877 } else {
2878 foreach ($this->errors as $errmsg) {
2879 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2880 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2881 }
2882 $this->db->rollback();
2883 return -1 * $error;
2884 }
2885 } else {
2886 $error_str = 'Command status do not meet requirement '.$this->statut;
2887 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2888 $this->error = $error_str;
2889 $this->errors[] = $this->error;
2890 return -2;
2891 }
2892 }
2893
2894 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2902 public function demand_reason($demand_reason_id, $notrigger = 0)
2903 {
2904 // phpcs:enable
2905 global $user;
2906
2907 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2908 if ($this->statut >= self::STATUS_DRAFT) {
2909 $error = 0;
2910
2911 $this->db->begin();
2912
2913 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2914 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2915 $sql .= ' WHERE rowid='.((int) $this->id);
2916
2917 dol_syslog(__METHOD__, LOG_DEBUG);
2918 $resql = $this->db->query($sql);
2919 if (!$resql) {
2920 $this->errors[] = $this->db->error();
2921 $error++;
2922 }
2923
2924 if (!$error) {
2925 $this->oldcopy = clone $this;
2926 $this->demand_reason_id = $demand_reason_id;
2927 }
2928
2929 if (!$notrigger && empty($error)) {
2930 // Call trigger
2931 $result = $this->call_trigger('ORDER_MODIFY', $user);
2932 if ($result < 0) {
2933 $error++;
2934 }
2935 // End call triggers
2936 }
2937
2938 if (!$error) {
2939 $this->db->commit();
2940 return 1;
2941 } else {
2942 foreach ($this->errors as $errmsg) {
2943 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2944 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2945 }
2946 $this->db->rollback();
2947 return -1 * $error;
2948 }
2949 } else {
2950 $error_str = 'order status do not meet requirement '.$this->statut;
2951 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2952 $this->error = $error_str;
2953 $this->errors[] = $this->error;
2954 return -2;
2955 }
2956 }
2957
2958 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2967 public function set_ref_client($user, $ref_client, $notrigger = 0)
2968 {
2969 // phpcs:enable
2970 if ($user->hasRight('commande', 'creer')) {
2971 $error = 0;
2972
2973 $this->db->begin();
2974
2975 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2976 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2977 $sql .= ' WHERE rowid = '.((int) $this->id);
2978
2979 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2980 $resql = $this->db->query($sql);
2981 if (!$resql) {
2982 $this->errors[] = $this->db->error();
2983 $error++;
2984 }
2985
2986 if (!$error) {
2987 $this->oldcopy = clone $this;
2988 $this->ref_client = $ref_client;
2989 $this->ref_customer = $ref_client;
2990 }
2991
2992 if (!$notrigger && empty($error)) {
2993 // Call trigger
2994 $result = $this->call_trigger('ORDER_MODIFY', $user);
2995 if ($result < 0) {
2996 $error++;
2997 }
2998 // End call triggers
2999 }
3000 if (!$error) {
3001 $this->db->commit();
3002 return 1;
3003 } else {
3004 foreach ($this->errors as $errmsg) {
3005 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3006 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3007 }
3008 $this->db->rollback();
3009 return -1 * $error;
3010 }
3011 } else {
3012 return -1;
3013 }
3014 }
3015
3023 public function classifyBilled(User $user, $notrigger = 0)
3024 {
3025 $error = 0;
3026
3027 if ($this->billed) {
3028 return 0;
3029 }
3030
3031 $this->db->begin();
3032
3033 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3034 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3035
3036 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3037 if ($this->db->query($sql)) {
3038 if (!$error) {
3039 $this->oldcopy = clone $this;
3040 $this->billed = 1;
3041 }
3042
3043 if (!$notrigger && empty($error)) {
3044 // Call trigger
3045 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3046 if ($result < 0) {
3047 $error++;
3048 }
3049 // End call triggers
3050 }
3051
3052 if (!$error) {
3053 $this->db->commit();
3054 return 1;
3055 } else {
3056 foreach ($this->errors as $errmsg) {
3057 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3058 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3059 }
3060 $this->db->rollback();
3061 return -1 * $error;
3062 }
3063 } else {
3064 $this->error = $this->db->error();
3065 $this->db->rollback();
3066 return -1;
3067 }
3068 }
3069
3077 public function classifyUnBilled(User $user, $notrigger = 0)
3078 {
3079 $error = 0;
3080
3081 $this->db->begin();
3082
3083 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3084 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3085
3086 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3087 if ($this->db->query($sql)) {
3088 if (!$error) {
3089 $this->oldcopy = clone $this;
3090 $this->billed = 1;
3091 }
3092
3093 if (!$notrigger && empty($error)) {
3094 // Call trigger
3095 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3096 if ($result < 0) {
3097 $error++;
3098 }
3099 // End call triggers
3100 }
3101
3102 if (!$error) {
3103 $this->billed = 0;
3104
3105 $this->db->commit();
3106 return 1;
3107 } else {
3108 foreach ($this->errors as $errmsg) {
3109 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3110 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3111 }
3112 $this->db->rollback();
3113 return -1 * $error;
3114 }
3115 } else {
3116 $this->error = $this->db->error();
3117 $this->db->rollback();
3118 return -1;
3119 }
3120 }
3121
3122
3153 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)
3154 {
3155 global $conf, $mysoc, $langs, $user;
3156
3157 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");
3158 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3159
3160 if ($this->statut == Commande::STATUS_DRAFT) {
3161 // Clean parameters
3162 if (empty($qty)) {
3163 $qty = 0;
3164 }
3165 if (empty($info_bits)) {
3166 $info_bits = 0;
3167 }
3168 if (empty($txtva)) {
3169 $txtva = 0;
3170 }
3171 if (empty($txlocaltax1)) {
3172 $txlocaltax1 = 0;
3173 }
3174 if (empty($txlocaltax2)) {
3175 $txlocaltax2 = 0;
3176 }
3177 if (empty($remise_percent)) {
3178 $remise_percent = 0;
3179 }
3180 if (empty($special_code) || $special_code == 3) {
3181 $special_code = 0;
3182 }
3183 if (empty($ref_ext)) {
3184 $ref_ext = '';
3185 }
3186
3187 if ($date_start && $date_end && $date_start > $date_end) {
3188 $langs->load("errors");
3189 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3190 return -1;
3191 }
3192
3193 $remise_percent = (float) price2num($remise_percent);
3194 $qty = (float) price2num($qty);
3195 $pu = price2num($pu);
3196 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
3197 $pu_ht_devise = price2num($pu_ht_devise);
3198 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3199 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3200 }
3201 $txlocaltax1 = (float) price2num($txlocaltax1);
3202 $txlocaltax2 = (float) price2num($txlocaltax2);
3203
3204 $this->db->begin();
3205
3206 // Calcul du total TTC et de la TVA pour la ligne a partir de
3207 // qty, pu, remise_percent et txtva
3208 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3209 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3210
3211 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3212
3213 // Clean vat code
3214 $vat_src_code = '';
3215 $reg = array();
3216 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3217 $vat_src_code = $reg[1];
3218 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3219 }
3220
3221 $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);
3222
3223 $total_ht = $tabprice[0];
3224 $total_tva = $tabprice[1];
3225 $total_ttc = $tabprice[2];
3226 $total_localtax1 = $tabprice[9];
3227 $total_localtax2 = $tabprice[10];
3228 $pu_ht = $tabprice[3];
3229 $pu_tva = $tabprice[4];
3230 $pu_ttc = $tabprice[5];
3231
3232 // MultiCurrency
3233 $multicurrency_total_ht = $tabprice[16];
3234 $multicurrency_total_tva = $tabprice[17];
3235 $multicurrency_total_ttc = $tabprice[18];
3236 $pu_ht_devise = $tabprice[19];
3237
3238 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3239 $price = $pu_ht;
3240 if ($price_base_type == 'TTC') {
3241 $subprice = $pu_ttc;
3242 } else {
3243 $subprice = $pu_ht;
3244 }
3245 $remise = 0;
3246 if ($remise_percent > 0) {
3247 $remise = round(((float) $pu * $remise_percent / 100), 2);
3248 $price = ((float) $pu - $remise);
3249 }
3250
3251 // Fetch current line from the database and then clone the object and set it in $oldline property
3252 $line = new OrderLine($this->db);
3253 $line->fetch($rowid);
3254 $line->fetch_optionals();
3255
3256 if (!empty($line->fk_product)) {
3257 $product = new Product($this->db);
3258 $result = $product->fetch($line->fk_product);
3259 $product_type = $product->type;
3260
3261 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
3262 $langs->load("errors");
3263 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3264 $this->errors[] = $this->error;
3265
3266 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3267
3268 $this->db->rollback();
3270 }
3271 }
3272
3273 $staticline = clone $line;
3274
3275 $line->oldline = $staticline;
3276 $this->line = $line;
3277 $this->line->context = $this->context;
3278 $this->line->rang = $rang;
3279
3280 // Reorder if fk_parent_line change
3281 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3282 $rangmax = $this->line_max($fk_parent_line);
3283 $this->line->rang = $rangmax + 1;
3284 }
3285
3286 $this->line->id = $rowid;
3287 $this->line->label = $label;
3288 $this->line->desc = $desc;
3289 $this->line->qty = $qty;
3290 $this->line->ref_ext = $ref_ext;
3291
3292 $this->line->vat_src_code = $vat_src_code;
3293 $this->line->tva_tx = $txtva;
3294 $this->line->localtax1_tx = $txlocaltax1;
3295 $this->line->localtax2_tx = $txlocaltax2;
3296 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3297 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3298 $this->line->remise_percent = $remise_percent;
3299 $this->line->subprice = (float) $pu_ht;
3300 $this->line->info_bits = $info_bits;
3301 $this->line->special_code = $special_code;
3302 $this->line->total_ht = (float) $total_ht;
3303 $this->line->total_tva = (float) $total_tva;
3304 $this->line->total_localtax1 = (float) $total_localtax1;
3305 $this->line->total_localtax2 = (float) $total_localtax2;
3306 $this->line->total_ttc = (float) $total_ttc;
3307 $this->line->date_start = $date_start;
3308 $this->line->date_end = $date_end;
3309 $this->line->product_type = $type;
3310 $this->line->fk_parent_line = $fk_parent_line;
3311 $this->line->skip_update_total = $skip_update_total;
3312 $this->line->fk_unit = $fk_unit;
3313
3314 $this->line->fk_fournprice = $fk_fournprice;
3315 $this->line->pa_ht = $pa_ht;
3316
3317 // Multicurrency
3318 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3319 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3320 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3321 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3322
3323 // TODO deprecated
3324 $this->line->price = $price;
3325
3326 if (is_array($array_options) && count($array_options) > 0) {
3327 // We replace values in this->line->array_options only for entries defined into $array_options
3328 foreach ($array_options as $key => $value) {
3329 $this->line->array_options[$key] = $array_options[$key];
3330 }
3331 }
3332
3333 $result = $this->line->update($user, $notrigger);
3334 if ($result > 0) {
3335 // Reorder if child line
3336 if (!empty($fk_parent_line)) {
3337 $this->line_order(true, 'DESC');
3338 }
3339
3340 // Mise a jour info denormalisees
3341 $this->update_price(1, 'auto');
3342
3343 $this->db->commit();
3344 return $result;
3345 } else {
3346 $this->error = $this->line->error;
3347
3348 $this->db->rollback();
3349 return -1;
3350 }
3351 } else {
3352 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3353 $this->errors = array('OrderStatusMakeOperationForbidden');
3354 return -2;
3355 }
3356 }
3357
3365 public function update(User $user, $notrigger = 0)
3366 {
3367 global $conf;
3368
3369 $error = 0;
3370
3371 // Clean parameters
3372 if (isset($this->ref)) {
3373 $this->ref = trim($this->ref);
3374 }
3375 if (isset($this->ref_client)) {
3376 $this->ref_client = trim($this->ref_client);
3377 }
3378 if (isset($this->ref_customer)) {
3379 $this->ref_customer = trim($this->ref_customer);
3380 }
3381 if (isset($this->note) || isset($this->note_private)) {
3382 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3383 }
3384 if (isset($this->note_public)) {
3385 $this->note_public = trim($this->note_public);
3386 }
3387 if (isset($this->model_pdf)) {
3388 $this->model_pdf = trim($this->model_pdf);
3389 }
3390 if (isset($this->import_key)) {
3391 $this->import_key = trim($this->import_key);
3392 }
3393 $delivery_date = $this->delivery_date;
3394
3395 // Check parameters
3396 // Put here code to add control on parameters values
3397
3398 // Update request
3399 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3400
3401 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3402 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3403 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3404 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3405 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3406 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3407 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3408 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3409 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3410 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3411 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3412 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3413 $sql .= " fk_user_modif=".(isset($user->id) ? $user->id : "null").",";
3414 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? $this->user_validation_id : "null").",";
3415 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3416 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3417 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3418 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3419 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3420 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3421 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3422 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3423 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3424 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3425 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3426 $sql .= " fk_warehouse=".($this->warehouse_id > 0 ? $this->warehouse_id : "null").",";
3427 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3428
3429 $sql .= " WHERE rowid=".((int) $this->id);
3430
3431 $this->db->begin();
3432
3433 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3434 $resql = $this->db->query($sql);
3435 if (!$resql) {
3436 $error++;
3437 $this->errors[] = "Error ".$this->db->lasterror();
3438 }
3439
3440 if (!$error) {
3441 $result = $this->insertExtraFields();
3442 if ($result < 0) {
3443 $error++;
3444 }
3445 }
3446
3447 if (!$error && !$notrigger) {
3448 // Call trigger
3449 $result = $this->call_trigger('ORDER_MODIFY', $user);
3450 if ($result < 0) {
3451 $error++;
3452 }
3453 // End call triggers
3454 }
3455
3456 // Commit or rollback
3457 if ($error) {
3458 foreach ($this->errors as $errmsg) {
3459 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3460 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3461 }
3462 $this->db->rollback();
3463 return -1 * $error;
3464 } else {
3465 $this->db->commit();
3466 return 1;
3467 }
3468 }
3469
3477 public function delete($user, $notrigger = 0)
3478 {
3479 global $conf, $langs;
3480 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3481
3482 $error = 0;
3483
3484 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3485
3486 $this->db->begin();
3487
3488 if (!$notrigger) {
3489 // Call trigger
3490 $result = $this->call_trigger('ORDER_DELETE', $user);
3491 if ($result < 0) {
3492 $error++;
3493 }
3494 // End call triggers
3495 }
3496
3497 // Test we can delete
3498 if ($this->countNbOfShipments() != 0) {
3499 $this->errors[] = $langs->trans('SomeShipmentExists');
3500 $error++;
3501 }
3502
3503 // Delete extrafields of lines and lines
3504 if (!$error && !empty($this->table_element_line)) {
3505 $tabletodelete = $this->table_element_line;
3506 $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).")";
3507 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3508 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3509 $error++;
3510 $this->error = $this->db->lasterror();
3511 $this->errors[] = $this->error;
3512 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3513 }
3514 }
3515
3516 if (!$error) {
3517 // Delete linked object
3518 $res = $this->deleteObjectLinked();
3519 if ($res < 0) {
3520 $error++;
3521 }
3522 }
3523
3524 if (!$error) {
3525 // Delete linked contacts
3526 $res = $this->delete_linked_contact();
3527 if ($res < 0) {
3528 $error++;
3529 }
3530 }
3531
3532 // Removed extrafields of object
3533 if (!$error) {
3534 $result = $this->deleteExtraFields();
3535 if ($result < 0) {
3536 $error++;
3537 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3538 }
3539 }
3540
3541 // Delete main record
3542 if (!$error) {
3543 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3544 $res = $this->db->query($sql);
3545 if (!$res) {
3546 $error++;
3547 $this->error = $this->db->lasterror();
3548 $this->errors[] = $this->error;
3549 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3550 }
3551 }
3552
3553 // Delete record into ECM index and physically
3554 if (!$error) {
3555 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3556 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3557 if (!$res) {
3558 $error++;
3559 }
3560 }
3561
3562 if (!$error) {
3563 // We remove directory
3564 $ref = dol_sanitizeFileName($this->ref);
3565 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3566 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3567 $file = $dir."/".$ref.".pdf";
3568 if (file_exists($file)) {
3569 dol_delete_preview($this);
3570
3571 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3572 $this->error = 'ErrorFailToDeleteFile';
3573 $this->errors[] = $this->error;
3574 $this->db->rollback();
3575 return 0;
3576 }
3577 }
3578 if (file_exists($dir)) {
3579 $res = @dol_delete_dir_recursive($dir);
3580 if (!$res) {
3581 $this->error = 'ErrorFailToDeleteDir';
3582 $this->errors[] = $this->error;
3583 $this->db->rollback();
3584 return 0;
3585 }
3586 }
3587 }
3588 }
3589
3590 if (!$error) {
3591 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3592 $this->db->commit();
3593 return 1;
3594 } else {
3595 $this->db->rollback();
3596 return -1;
3597 }
3598 }
3599
3600
3601 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3609 public function load_board($user, $mode)
3610 {
3611 // phpcs:enable
3612 global $conf, $langs;
3613
3614 $clause = " WHERE";
3615
3616 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3617 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3618 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3619 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3620 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3621 $clause = " AND";
3622 }
3623 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3624 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3625 if ($mode == 'toship') {
3626 // An order to ship is an open order (validated or in progress)
3627 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3628 }
3629 if ($mode == 'tobill') {
3630 // An order to bill is an order not already billed
3631 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3632 }
3633 if ($mode == 'shippedtobill') {
3634 // An order shipped and to bill is a delivered order not already billed
3635 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3636 }
3637 if ($user->socid) {
3638 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3639 }
3640
3641 $resql = $this->db->query($sql);
3642 if ($resql) {
3643 $delay_warning = 0;
3644 $label = $labelShort = $url = '';
3645 if ($mode == 'toship') {
3646 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3647 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3648 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3649 $labelShort = $langs->transnoentitiesnoconv("Opened");
3650 }
3651 if ($mode == 'tobill') {
3652 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3653 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3654 $labelShort = $langs->trans("ToBill");
3655 }
3656 if ($mode == 'shippedtobill') {
3657 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3658 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3659 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3660 }
3661
3662 $response = new WorkboardResponse();
3663
3664 $response->warning_delay = $delay_warning;
3665 $response->label = $label;
3666 $response->labelShort = $labelShort;
3667 $response->url = $url;
3668 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3669 $response->img = img_object('', "order");
3670
3671 $generic_commande = new Commande($this->db);
3672
3673 while ($obj = $this->db->fetch_object($resql)) {
3674 $response->nbtodo++;
3675 $response->total += $obj->total_ht;
3676
3677 $generic_commande->statut = $obj->fk_statut;
3678 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3679 $generic_commande->date = $this->db->jdate($obj->date_commande);
3680 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3681
3682 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3683 $response->nbtodolate++;
3684 }
3685 }
3686
3687 return $response;
3688 } else {
3689 $this->error = $this->db->error();
3690 return -1;
3691 }
3692 }
3693
3699 public function getLabelSource()
3700 {
3701 global $langs;
3702
3703 $label = $langs->trans('OrderSource'.$this->source);
3704
3705 if ($label == 'OrderSource') {
3706 return '';
3707 }
3708 return $label;
3709 }
3710
3717 public function getLibStatut($mode)
3718 {
3719 return $this->LibStatut($this->statut, $this->billed, $mode);
3720 }
3721
3722 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3732 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3733 {
3734 // phpcs:enable
3735 global $langs, $hookmanager;
3736
3737 $billedtext = '';
3738 if (empty($donotshowbilled)) {
3739 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3740 }
3741
3742 $labelTooltip = '';
3743
3744 if ($status == self::STATUS_CANCELED) {
3745 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3746 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3747 $statusType = 'status9';
3748 } elseif ($status == self::STATUS_DRAFT) {
3749 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3750 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3751 $statusType = 'status0';
3752 } elseif ($status == self::STATUS_VALIDATED) {
3753 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3754 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3755 $statusType = 'status1';
3756 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3757 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3758 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3759 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3760 if (!empty($this->delivery_date)) {
3761 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3762 }
3763 $statusType = 'status4';
3764 } elseif ($status == self::STATUS_CLOSED) {
3765 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtext;
3766 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3767 $statusType = 'status6';
3768 } else {
3769 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3770 $labelStatusShort = '';
3771 $statusType = '';
3772 $mode = 0;
3773 }
3774
3775 $parameters = array(
3776 'status' => $status,
3777 'mode' => $mode,
3778 'billed' => $billed,
3779 'donotshowbilled' => $donotshowbilled
3780 );
3781
3782 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3783
3784 if ($reshook > 0) {
3785 return $hookmanager->resPrint;
3786 }
3787
3788 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3789 }
3790
3798 public function getTooltipContentArray($params)
3799 {
3800 global $conf, $langs, $user;
3801
3802 $langs->load('orders');
3803 $datas = [];
3804 $nofetch = !empty($params['nofetch']);
3805
3806 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3807 return ['optimize' => $langs->trans("Order")];
3808 }
3809
3810 if ($user->hasRight('commande', 'lire')) {
3811 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').'<u>'.$langs->trans("Order").'</u>';
3812 if (isset($this->statut)) {
3813 $datas['status'] = ' '.$this->getLibStatut(5);
3814 }
3815 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3816 if (!$nofetch) {
3817 $langs->load('companies');
3818 if (empty($this->thirdparty)) {
3819 $this->fetch_thirdparty();
3820 }
3821 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3822 }
3823 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3824 if (!$nofetch) {
3825 $langs->load('project');
3826 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3827 $res = $this->fetchProject();
3828 if ($res > 0 && $this->project instanceof Project) {
3829 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3830 }
3831 }
3832 }
3833 if (!empty($this->total_ht)) {
3834 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3835 }
3836 if (!empty($this->total_tva)) {
3837 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3838 }
3839 if (!empty($this->total_ttc)) {
3840 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3841 }
3842 if (!empty($this->date)) {
3843 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3844 }
3845 if (!empty($this->delivery_date)) {
3846 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3847 }
3848 }
3849
3850 return $datas;
3851 }
3852
3866 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3867 {
3868 global $conf, $langs, $user, $hookmanager;
3869
3870 if (!empty($conf->dol_no_mouse_hover)) {
3871 $notooltip = 1; // Force disable tooltips
3872 }
3873
3874 $result = '';
3875
3876 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3877 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3878 } else {
3879 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3880 }
3881
3882 if (!$user->hasRight('commande', 'lire')) {
3883 $option = 'nolink';
3884 }
3885
3886 if ($option !== 'nolink') {
3887 // Add param to save lastsearch_values or not
3888 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3889 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3890 $add_save_lastsearch_values = 1;
3891 }
3892 if ($add_save_lastsearch_values) {
3893 $url .= '&save_lastsearch_values=1';
3894 }
3895 }
3896
3897 if ($short) {
3898 return $url;
3899 }
3900 $params = [
3901 'id' => $this->id,
3902 'objecttype' => $this->element,
3903 'option' => $option,
3904 'nofetch' => 1,
3905 ];
3906 $classfortooltip = 'classfortooltip';
3907 $dataparams = '';
3908 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3909 $classfortooltip = 'classforajaxtooltip';
3910 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3911 $label = '';
3912 } else {
3913 $label = implode($this->getTooltipContentArray($params));
3914 }
3915
3916 $linkclose = '';
3917 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3918 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3919 $label = $langs->trans("Order");
3920 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
3921 }
3922 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
3923 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3924
3925 $target_value = array('_self', '_blank', '_parent', '_top');
3926 if (in_array($target, $target_value)) {
3927 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3928 }
3929 }
3930
3931 $linkstart = '<a href="'.$url.'"';
3932 $linkstart .= $linkclose.'>';
3933 $linkend = '</a>';
3934
3935 if ($option === 'nolink') {
3936 $linkstart = '';
3937 $linkend = '';
3938 }
3939
3940 $result .= $linkstart;
3941 if ($withpicto) {
3942 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3943 }
3944 if ($withpicto != 2) {
3945 $result .= $this->ref;
3946 }
3947 $result .= $linkend;
3948
3949 if ($addlinktonotes) {
3950 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3951 if ($txttoshow) {
3952 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3953 $result .= ' <span class="note inline-block">';
3954 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3955 $result .= img_picto('', 'note');
3956 $result .= '</a>';
3957 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3958 //$result.='</a>';
3959 $result .= '</span>';
3960 }
3961 }
3962
3963 global $action;
3964 $hookmanager->initHooks(array($this->element . 'dao'));
3965 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3966 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3967 if ($reshook > 0) {
3968 $result = $hookmanager->resPrint;
3969 } else {
3970 $result .= $hookmanager->resPrint;
3971 }
3972 return $result;
3973 }
3974
3975
3982 public function info($id)
3983 {
3984 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3985 $sql .= ' date_valid as datev,';
3986 $sql .= ' date_cloture as datecloture,';
3987 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3988 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
3989 $sql .= ' WHERE c.rowid = '.((int) $id);
3990 $result = $this->db->query($sql);
3991 if ($result) {
3992 if ($this->db->num_rows($result)) {
3993 $obj = $this->db->fetch_object($result);
3994 $this->id = $obj->rowid;
3995 if ($obj->fk_user_author) {
3996 $this->user_creation_id = $obj->fk_user_author;
3997 }
3998 if ($obj->fk_user_valid) {
3999 $this->user_validation_id = $obj->fk_user_valid;
4000 }
4001 if ($obj->fk_user_cloture) {
4002 $this->user_closing_id = $obj->fk_user_cloture;
4003 }
4004
4005 $this->date_creation = $this->db->jdate($obj->datec);
4006 $this->date_modification = $this->db->jdate($obj->datem);
4007 $this->date_validation = $this->db->jdate($obj->datev);
4008 $this->date_cloture = $this->db->jdate($obj->datecloture);
4009 }
4010
4011 $this->db->free($result);
4012 } else {
4013 dol_print_error($this->db);
4014 }
4015 }
4016
4017
4025 public function initAsSpecimen()
4026 {
4027 global $conf, $langs;
4028
4029 dol_syslog(get_class($this)."::initAsSpecimen");
4030
4031 // Load array of products prodids
4032 $num_prods = 0;
4033 $prodids = array();
4034 $sql = "SELECT rowid";
4035 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4036 $sql .= " WHERE entity IN (".getEntity('product').")";
4037 $sql .= $this->db->plimit(100);
4038
4039 $resql = $this->db->query($sql);
4040 if ($resql) {
4041 $num_prods = $this->db->num_rows($resql);
4042 $i = 0;
4043 while ($i < $num_prods) {
4044 $i++;
4045 $row = $this->db->fetch_row($resql);
4046 $prodids[$i] = $row[0];
4047 }
4048 }
4049
4050 // Initialise parameters
4051 $this->id = 0;
4052 $this->ref = 'SPECIMEN';
4053 $this->specimen = 1;
4054 $this->entity = $conf->entity;
4055 $this->socid = 1;
4056 $this->date = time();
4057 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4058 $this->cond_reglement_code = 'RECEP';
4059 $this->mode_reglement_code = 'CHQ';
4060 $this->availability_code = 'DSP';
4061 $this->demand_reason_code = 'SRC_00';
4062
4063 $this->note_public = 'This is a comment (public)';
4064 $this->note_private = 'This is a comment (private)';
4065
4066 $this->multicurrency_tx = 1;
4067 $this->multicurrency_code = $conf->currency;
4068
4069 $this->status = $this::STATUS_DRAFT;
4070
4071 // Lines
4072 $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)
4073 $xnbp = 0;
4074
4075 while ($xnbp < $nbp) {
4076 $line = new OrderLine($this->db);
4077
4078 $line->desc = $langs->trans("Description")." ".$xnbp;
4079 $line->qty = 1;
4080 $line->subprice = 100;
4081 $line->price = 100;
4082 $line->tva_tx = 20;
4083 if ($xnbp == 2) {
4084 $line->total_ht = 50;
4085 $line->total_ttc = 60;
4086 $line->total_tva = 10;
4087 $line->remise_percent = 50;
4088 } else {
4089 $line->total_ht = 100;
4090 $line->total_ttc = 120;
4091 $line->total_tva = 20;
4092 $line->remise_percent = 0;
4093 }
4094 if ($num_prods > 0) {
4095 $prodid = mt_rand(1, $num_prods);
4096 $line->fk_product = $prodids[$prodid];
4097 $line->product_ref = 'SPECIMEN';
4098 }
4099
4100 $this->lines[$xnbp] = $line;
4101
4102 $this->total_ht += $line->total_ht;
4103 $this->total_tva += $line->total_tva;
4104 $this->total_ttc += $line->total_ttc;
4105
4106 $xnbp++;
4107 }
4108
4109 return 1;
4110 }
4111
4112
4118 public function loadStateBoard()
4119 {
4120 global $user;
4121
4122 $this->nb = array();
4123 $clause = "WHERE";
4124
4125 $sql = "SELECT count(co.rowid) as nb";
4126 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as co";
4127 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4128 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
4129 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4130 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4131 $clause = "AND";
4132 }
4133 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4134
4135 $resql = $this->db->query($sql);
4136 if ($resql) {
4137 while ($obj = $this->db->fetch_object($resql)) {
4138 $this->nb["orders"] = $obj->nb;
4139 }
4140 $this->db->free($resql);
4141 return 1;
4142 } else {
4143 dol_print_error($this->db);
4144 $this->error = $this->db->error();
4145 return -1;
4146 }
4147 }
4148
4154 public function getLinesArray()
4155 {
4156 return $this->fetch_lines();
4157 }
4158
4170 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4171 {
4172 global $conf, $langs;
4173
4174 $langs->load("orders");
4175 $outputlangs->load("products");
4176
4177 if (!dol_strlen($modele)) {
4178 $modele = 'einstein';
4179
4180 if (!empty($this->model_pdf)) {
4181 $modele = $this->model_pdf;
4182 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4183 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4184 }
4185 }
4186
4187 $modelpath = "core/modules/commande/doc/";
4188
4189 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4190 }
4191
4192
4201 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4202 {
4203 $tables = array(
4204 'commande'
4205 );
4206
4207 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4208 }
4209
4218 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4219 {
4220 $tables = array(
4221 'commandedet',
4222 );
4223
4224 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4225 }
4226
4232 public function hasDelay()
4233 {
4234 global $conf;
4235
4236 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4237 return false; // Never late if not inside this status range
4238 }
4239
4240 $now = dol_now();
4241
4242 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4243 }
4244
4250 public function showDelay()
4251 {
4252 global $conf, $langs;
4253
4254 if (empty($this->delivery_date)) {
4255 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4256 } else {
4257 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4258 }
4259 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4260
4261 return $text;
4262 }
4263
4273 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4274 {
4275 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4276 }
4277}
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