dolibarr 21.0.0-beta
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
117 public $statut;
118
122 public $billed;
123
127 public $date_lim_reglement;
131 public $cond_reglement_code;
132
136 public $cond_reglement_doc;
137
143 public $deposit_percent;
144
148 public $fk_account;
149
153 public $mode_reglement;
154
158 public $mode_reglement_id;
159
163 public $mode_reglement_code;
164
169 public $availability_id;
170
175 public $availability_code;
176
181 public $availability;
182
186 public $demand_reason_id;
187
191 public $demand_reason_code;
192
196 public $date;
197
203 public $date_commande;
204
208 public $delivery_date;
209
213 public $fk_remise_except;
214
219 public $remise_percent;
220
224 public $source; // Order mode. How we received order (by phone, by email, ...)
225
230 public $signed_status = 0;
231
235 public $warehouse_id;
236
240 public $extraparams = array();
241
242 public $linked_objects = array();
243
247 public $user_author_id;
248
252 public $line;
253
257 public $lines = array();
258
259
263 public $module_source;
267 public $pos_source;
268
272 public $expeditions;
273
277 public $online_payment_url;
278
279
280
305 // BEGIN MODULEBUILDER PROPERTIES
309 public $fields = array(
310 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
311 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
312 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 25),
313 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 26),
314 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 28),
315 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 20),
316 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 25),
317 'date_commande' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'csslist' => 'nowraponall'),
318 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 62, 'csslist' => 'nowraponall'),
319 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'csslist' => 'nowraponall'),
320 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 85),
321 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => -1, 'position' => 90),
322 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => -1, 'position' => 95),
323 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
324 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
325 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
326 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
327 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
328 'signed_status' => array('type' => 'smallint(6)', 'label' => 'SignedStatus', 'enabled' => 1, 'visible' => -1, 'position' => 146, 'arrayofkeyval' => array(0 => 'NoSignature', 1 => 'SignedSender', 2 => 'SignedReceiver', 9 => 'SignedAll')),
329 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 150),
330 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 155),
331 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 160),
332 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 170),
333 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 175),
334 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 180),
335 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 181),
336 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 185),
337 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'csslist' => 'nowraponall'),
338 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 195),
339 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 200),
340 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 205),
341 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 210),
342 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
343 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 225),
344 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 230),
345 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 235),
346 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
347 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245),
348 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
349 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
350 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 260, 'isameasure' => 1),
351 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 265, 'isameasure' => 1),
352 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 270),
353 'module_source' => array('type' => 'varchar(32)', 'label' => 'POSModule', 'enabled' => 1, 'visible' => -1, 'position' => 275),
354 'pos_source' => array('type' => 'varchar(32)', 'label' => 'POSTerminal', 'enabled' => 1, 'visible' => -1, 'position' => 280),
355 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 300),
356 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 302),
357 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 304, 'csslist' => 'nowraponall'),
358 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 306),
359 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 400),
360 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'position' => 500),
361 );
362 // END MODULEBUILDER PROPERTIES
363
368
372 const STATUS_CANCELED = -1;
376 const STATUS_DRAFT = 0;
384 const STATUS_SHIPMENTONPROCESS = 2; // We set this status when a shipment is validated
385
391
395 const STATUS_CLOSED = 3;
396
397 /*
398 * No signature
399 */
400 const STATUS_NO_SIGNATURE = 0;
401
402 /*
403 * Signed by sender
404 */
405 const STATUS_SIGNED_SENDER = 1;
406
407 /*
408 * Signed by receiver
409 */
410 const STATUS_SIGNED_RECEIVER = 2;
411
412 /*
413 * Signed by all
414 */
415 const STATUS_SIGNED_ALL = 9; // To handle future kind of signature (ex: tripartite contract)
416
417
423 public function __construct($db)
424 {
425 $this->db = $db;
426
427 $this->ismultientitymanaged = 1;
428 $this->isextrafieldmanaged = 1;
429 }
430
438 public function getNextNumRef($soc)
439 {
440 global $langs, $conf;
441 $langs->load("order");
442
443 if (getDolGlobalString('COMMANDE_ADDON')) {
444 $mybool = false;
445
446 $file = getDolGlobalString('COMMANDE_ADDON') . ".php";
447 $classname = getDolGlobalString('COMMANDE_ADDON');
448
449 // Include file with class
450 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
451 foreach ($dirmodels as $reldir) {
452 $dir = dol_buildpath($reldir."core/modules/commande/");
453
454 // Load file with numbering class (if found)
455 $mybool = ((bool) @include_once $dir.$file) || $mybool;
456 }
457
458 if (!$mybool) {
459 dol_print_error(null, "Failed to include file ".$file);
460 return '';
461 }
462
463 $obj = new $classname();
464 '@phan-var-force ModeleNumRefCommandes $obj';
465
466 $numref = $obj->getNextValue($soc, $this);
467
468 if ($numref != "") {
469 return $numref;
470 } else {
471 $this->error = $obj->error;
472 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
473 return "";
474 }
475 } else {
476 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
477 return "";
478 }
479 }
480
481
490 public function valid($user, $idwarehouse = 0, $notrigger = 0)
491 {
492 global $conf, $langs;
493
494 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
495
496 $error = 0;
497
498 // Protection
499 if ($this->statut == self::STATUS_VALIDATED) {
500 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
501 return 0;
502 }
503
504 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
505 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
506 $this->error = 'NotEnoughPermissions';
507 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
508 return -1;
509 }
510
511 $now = dol_now();
512
513 $this->db->begin();
514
515 // Definition du nom de module de numerotation de commande
516 $soc = new Societe($this->db);
517 $soc->fetch($this->socid);
518
519 // Class of company linked to order
520 $result = $soc->setAsCustomer();
521
522 // Define new ref
523 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
524 $num = $this->getNextNumRef($soc);
525 } else {
526 $num = $this->ref;
527 }
528 $this->newref = dol_sanitizeFileName($num);
529
530 // Validate
531 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
532 $sql .= " SET ref = '".$this->db->escape($num)."',";
533 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
534 $sql .= " date_valid='".$this->db->idate($now)."',";
535 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
536 $sql .= " fk_user_modif = ".((int) $user->id);
537 $sql .= " WHERE rowid = ".((int) $this->id);
538
539 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
540 $resql = $this->db->query($sql);
541 if (!$resql) {
542 dol_print_error($this->db);
543 $this->error = $this->db->lasterror();
544 $error++;
545 }
546
547 if (!$error) {
548 // If stock is incremented on validate order, we must increment it
549 if ($result >= 0 && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
550 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
551 $langs->load("agenda");
552
553 // Loop on each line
554 $cpt = count($this->lines);
555 for ($i = 0; $i < $cpt; $i++) {
556 if ($this->lines[$i]->fk_product > 0) {
557 $mouvP = new MouvementStock($this->db);
558 $mouvP->origin = &$this;
559 $mouvP->setOrigin($this->element, $this->id);
560 // We decrement stock of product (and sub-products)
561 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
562 if ($result < 0) {
563 $error++;
564 $this->error = $mouvP->error;
565 }
566 }
567 if ($error) {
568 break;
569 }
570 }
571 }
572 }
573
574 if (!$error && !$notrigger) {
575 // Call trigger
576 $result = $this->call_trigger('ORDER_VALIDATE', $user);
577 if ($result < 0) {
578 $error++;
579 }
580 // End call triggers
581 }
582
583 if (!$error) {
584 $this->oldref = $this->ref;
585
586 // Rename directory if dir was a temporary ref
587 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
588 // Now we rename also files into index
589 $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)."'";
590 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
591 $resql = $this->db->query($sql);
592 if (!$resql) {
593 $error++;
594 $this->error = $this->db->lasterror();
595 }
596 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
597 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
598 $resql = $this->db->query($sql);
599 if (!$resql) {
600 $error++;
601 $this->error = $this->db->lasterror();
602 }
603
604 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
605 $oldref = dol_sanitizeFileName($this->ref);
606 $newref = dol_sanitizeFileName($num);
607 $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
608 $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
609 if (!$error && file_exists($dirsource)) {
610 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
611
612 if (@rename($dirsource, $dirdest)) {
613 dol_syslog("Rename ok");
614 // Rename docs starting with $oldref with $newref
615 $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
616 foreach ($listoffiles as $fileentry) {
617 $dirsource = $fileentry['name'];
618 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
619 $dirsource = $fileentry['path'].'/'.$dirsource;
620 $dirdest = $fileentry['path'].'/'.$dirdest;
621 @rename($dirsource, $dirdest);
622 }
623 }
624 }
625 }
626 }
627
628 // Set new ref and current status
629 if (!$error) {
630 $this->ref = $num;
631 $this->statut = self::STATUS_VALIDATED; // deprecated
633 }
634
635 if (!$error) {
636 $this->db->commit();
637 return 1;
638 } else {
639 $this->db->rollback();
640 return -1;
641 }
642 }
643
644 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
652 public function setDraft($user, $idwarehouse = -1)
653 {
654 //phpcs:enable
655 global $conf, $langs;
656
657 $error = 0;
658
659 // Protection
660 if ($this->statut <= self::STATUS_DRAFT && !getDolGlobalInt('ORDER_REOPEN_TO_DRAFT')) {
661 return 0;
662 }
663
664 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
665 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
666 $this->error = 'Permission denied';
667 return -1;
668 }
669
670 dol_syslog(__METHOD__, LOG_DEBUG);
671
672 $this->db->begin();
673
674 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
675 $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
676 $sql .= " fk_user_modif = ".((int) $user->id);
677 $sql .= " WHERE rowid = ".((int) $this->id);
678
679 if ($this->db->query($sql)) {
680 if (!$error) {
681 $this->oldcopy = clone $this;
682 }
683
684 // If stock is decremented on validate order, we must reincrement it
685 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
686 $result = 0;
687
688 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
689 $langs->load("agenda");
690
691 $num = count($this->lines);
692 for ($i = 0; $i < $num; $i++) {
693 if ($this->lines[$i]->fk_product > 0) {
694 $mouvP = new MouvementStock($this->db);
695 $mouvP->origin = &$this;
696 $mouvP->setOrigin($this->element, $this->id);
697 // We increment stock of product (and sub-products)
698 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
699 if ($result < 0) {
700 $error++;
701 $this->error = $mouvP->error;
702 break;
703 }
704 }
705 }
706 }
707
708 if (!$error) {
709 // Call trigger
710 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
711 if ($result < 0) {
712 $error++;
713 }
714 }
715
716 if (!$error) {
717 $this->statut = self::STATUS_DRAFT;
718 $this->db->commit();
719 return 1;
720 } else {
721 $this->db->rollback();
722 return -1;
723 }
724 } else {
725 $this->error = $this->db->error();
726 $this->db->rollback();
727 return -1;
728 }
729 }
730
731
732 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
740 public function set_reopen($user)
741 {
742 // phpcs:enable
743 $error = 0;
744
745 if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
746 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
747 return 0;
748 }
749
750 $this->db->begin();
751
752 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
753 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
754 $sql .= " fk_user_modif = ".((int) $user->id);
755 $sql .= " WHERE rowid = ".((int) $this->id);
756
757 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
758 $resql = $this->db->query($sql);
759 if ($resql) {
760 // Call trigger
761 $result = $this->call_trigger('ORDER_REOPEN', $user);
762 if ($result < 0) {
763 $error++;
764 }
765 // End call triggers
766 } else {
767 $error++;
768 $this->error = $this->db->lasterror();
769 dol_print_error($this->db);
770 }
771
772 if (!$error) {
773 $this->statut = self::STATUS_VALIDATED;
774 $this->billed = 0;
775
776 $this->db->commit();
777 return 1;
778 } else {
779 foreach ($this->errors as $errmsg) {
780 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
781 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
782 }
783 $this->db->rollback();
784 return -1 * $error;
785 }
786 }
787
795 public function cloture($user, $notrigger = 0)
796 {
797 global $conf;
798
799 $error = 0;
800
801 $usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
802 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'close')));
803
804 if ($usercanclose) {
805 if ($this->statut == self::STATUS_CLOSED) {
806 return 0;
807 }
808 $this->db->begin();
809
810 $now = dol_now();
811
812 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
813 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
814 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
815 $sql .= " date_cloture = '".$this->db->idate($now)."',";
816 $sql .= " fk_user_modif = ".((int) $user->id);
817 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
818
819 if ($this->db->query($sql)) {
820 if (!$notrigger) {
821 // Call trigger
822 $result = $this->call_trigger('ORDER_CLOSE', $user);
823 if ($result < 0) {
824 $error++;
825 }
826 // End call triggers
827 }
828
829 if (!$error) {
830 $this->statut = self::STATUS_CLOSED;
831
832 $this->db->commit();
833 return 1;
834 } else {
835 $this->db->rollback();
836 return -1;
837 }
838 } else {
839 $this->error = $this->db->lasterror();
840
841 $this->db->rollback();
842 return -1;
843 }
844 }
845 return 0;
846 }
847
855 public function cancel($idwarehouse = -1)
856 {
857 global $conf, $user, $langs;
858
859 $error = 0;
860
861 $this->db->begin();
862
863 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
864 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
865 $sql .= " fk_user_modif = ".((int) $user->id);
866 $sql .= " WHERE rowid = ".((int) $this->id);
867 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
868
869 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
870 if ($this->db->query($sql)) {
871 // If stock is decremented on validate order, we must reincrement it
872 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
873 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
874 $langs->load("agenda");
875
876 $num = count($this->lines);
877 for ($i = 0; $i < $num; $i++) {
878 if ($this->lines[$i]->fk_product > 0) {
879 $mouvP = new MouvementStock($this->db);
880 $mouvP->setOrigin($this->element, $this->id);
881 // We increment stock of product (and sub-products)
882 $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
883 if ($result < 0) {
884 $error++;
885 $this->error = $mouvP->error;
886 break;
887 }
888 }
889 }
890 }
891
892 if (!$error) {
893 // Call trigger
894 $result = $this->call_trigger('ORDER_CANCEL', $user);
895 if ($result < 0) {
896 $error++;
897 }
898 // End call triggers
899 }
900
901 if (!$error) {
902 $this->statut = self::STATUS_CANCELED;
903 $this->db->commit();
904 return 1;
905 } else {
906 foreach ($this->errors as $errmsg) {
907 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
908 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
909 }
910 $this->db->rollback();
911 return -1 * $error;
912 }
913 } else {
914 $this->error = $this->db->error();
915 $this->db->rollback();
916 return -1;
917 }
918 }
919
928 public function create($user, $notrigger = 0)
929 {
930 global $conf, $langs, $mysoc;
931 $error = 0;
932
933 // Clean parameters
934
935 // Set tmp vars
936 $date = ($this->date_commande ? $this->date_commande : $this->date);
937 $delivery_date = $this->delivery_date;
938
939 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
940 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
941 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
942 } else {
943 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
944 }
945 if (empty($this->fk_multicurrency)) {
946 $this->multicurrency_code = $conf->currency;
947 $this->fk_multicurrency = 0;
948 $this->multicurrency_tx = 1;
949 }
950 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
951 $this->entity = setEntity($this);
952
953 dol_syslog(get_class($this)."::create user=".$user->id);
954
955 // Check parameters
956 if (!empty($this->ref)) { // We check that ref is not already used
957 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
958 if ($result > 0) {
959 $this->error = 'ErrorRefAlreadyExists';
960 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
961 $this->db->rollback();
962 return -1;
963 }
964 }
965
966 $soc = new Societe($this->db);
967 $result = $soc->fetch($this->socid);
968 if ($result < 0) {
969 $this->error = "Failed to fetch company";
970 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
971 return -2;
972 }
973 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
974 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
975 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
976 return -1;
977 }
978
979 $now = dol_now();
980
981 $this->db->begin();
982
983 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
984 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
985 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
986 $sql .= ", fk_shipping_method";
987 $sql .= ", fk_warehouse";
988 $sql .= ", fk_incoterms, location_incoterms";
989 $sql .= ", entity, module_source, pos_source";
990 $sql .= ", fk_multicurrency";
991 $sql .= ", multicurrency_code";
992 $sql .= ", multicurrency_tx";
993 $sql .= ")";
994 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
995 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
996 $sql .= ", '".$this->db->idate($date)."'";
997 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
998 $sql .= ", '".$this->db->escape($this->note_private)."'";
999 $sql .= ", '".$this->db->escape($this->note_public)."'";
1000 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
1001 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1002 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1003 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1004 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1005 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1006 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1007 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1008 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1009 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1010 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1011 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1012 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1013 $sql .= ", ".(int) $this->fk_incoterms;
1014 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1015 $sql .= ", ".(int) $this->entity;
1016 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1017 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1018 $sql .= ", ".(int) $this->fk_multicurrency;
1019 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1020 $sql .= ", ".(float) $this->multicurrency_tx;
1021 $sql .= ")";
1022
1023 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1024 $resql = $this->db->query($sql);
1025 if ($resql) {
1026 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1027
1028 if ($this->id) {
1029 $fk_parent_line = 0;
1030 $num = count($this->lines);
1031
1032 /*
1033 * Insert products details into db
1034 */
1035 for ($i = 0; $i < $num; $i++) {
1036 $line = $this->lines[$i];
1037
1038 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1039 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1040 if (!is_object($line)) {
1041 $line = (object) $line;
1042 }
1043
1044 // Reset fk_parent_line for no child products and special product
1045 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1046 $fk_parent_line = 0;
1047 }
1048
1049 // Complete vat rate with code
1050 $vatrate = $line->tva_tx;
1051 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1052 $vatrate .= ' ('.$line->vat_src_code.')';
1053 }
1054
1055 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1056 $originid = $line->origin_id;
1057 $origintype = $line->origin;
1058 } else {
1059 $originid = $line->id;
1060 $origintype = $this->element;
1061 }
1062
1063 // ref_ext
1064 if (empty($line->ref_ext)) {
1065 $line->ref_ext = '';
1066 }
1067
1068 $result = $this->addline(
1069 $line->desc,
1070 $line->subprice,
1071 $line->qty,
1072 $vatrate,
1073 $line->localtax1_tx,
1074 $line->localtax2_tx,
1075 $line->fk_product,
1076 $line->remise_percent,
1077 $line->info_bits,
1078 $line->fk_remise_except,
1079 'HT',
1080 0,
1081 $line->date_start,
1082 $line->date_end,
1083 $line->product_type,
1084 $line->rang,
1085 $line->special_code,
1086 $fk_parent_line,
1087 $line->fk_fournprice,
1088 $line->pa_ht,
1089 $line->label,
1090 $line->array_options,
1091 $line->fk_unit,
1092 $origintype,
1093 $originid,
1094 0,
1095 $line->ref_ext,
1096 1
1097 );
1098 if ($result < 0) {
1099 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1100 $this->error = $this->db->lasterror();
1101 $this->errors[] = $this->error;
1102 dol_print_error($this->db);
1103 }
1104 $this->db->rollback();
1105 return -1;
1106 }
1107 // Defined the new fk_parent_line
1108 if ($result > 0 && $line->product_type == 9) {
1109 $fk_parent_line = $result;
1110 }
1111 }
1112
1113 $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.
1114
1115 // update ref
1116 $initialref = '(PROV'.$this->id.')';
1117 if (!empty($this->ref)) {
1118 $initialref = $this->ref;
1119 }
1120
1121 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1122 if ($this->db->query($sql)) {
1123 $this->ref = $initialref;
1124
1125 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1126 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1127 }
1128
1129 // Add object linked
1130 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1131 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1132 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, ...))
1133 foreach ($tmp_origin_id as $origin_id) {
1134 $ret = $this->add_object_linked($origin, $origin_id);
1135 if (!$ret) {
1136 $this->error = $this->db->lasterror();
1137 $error++;
1138 }
1139 }
1140 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1141 $origin_id = $tmp_origin_id;
1142 $ret = $this->add_object_linked($origin, $origin_id);
1143 if (!$ret) {
1144 $this->error = $this->db->lasterror();
1145 $error++;
1146 }
1147 }
1148 }
1149 }
1150
1151 if (!$error && $this->id && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1152 $originforcontact = $this->origin;
1153 $originidforcontact = $this->origin_id;
1154 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1155 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1156 $exp = new Expedition($this->db);
1157 $exp->fetch($this->origin_id);
1158 $exp->fetchObjectLinked();
1159 if (count($exp->linkedObjectsIds['commande']) > 0) {
1160 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1161 $originforcontact = 'commande';
1162 if (is_object($value)) {
1163 $originidforcontact = $value->id;
1164 } else {
1165 $originidforcontact = $value;
1166 }
1167 break; // We take first one
1168 }
1169 }
1170 }
1171
1172 $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";
1173 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1174
1175 $resqlcontact = $this->db->query($sqlcontact);
1176 if ($resqlcontact) {
1177 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1178 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1179 $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
1180 }
1181 } else {
1182 dol_print_error($this->db);
1183 }
1184 }
1185
1186 if (!$error) {
1187 $result = $this->insertExtraFields();
1188 if ($result < 0) {
1189 $error++;
1190 }
1191 }
1192
1193 if (!$error && !$notrigger) {
1194 // Call trigger
1195 $result = $this->call_trigger('ORDER_CREATE', $user);
1196 if ($result < 0) {
1197 $error++;
1198 }
1199 // End call triggers
1200 }
1201
1202 if (!$error) {
1203 $this->db->commit();
1204 return $this->id;
1205 } else {
1206 $this->db->rollback();
1207 return -1 * $error;
1208 }
1209 } else {
1210 $this->error = $this->db->lasterror();
1211 $this->db->rollback();
1212 return -1;
1213 }
1214 }
1215
1216 return 0;
1217 } else {
1218 $this->error = $this->db->lasterror();
1219 $this->db->rollback();
1220 return -1;
1221 }
1222 }
1223
1224
1232 public function createFromClone(User $user, $socid = 0)
1233 {
1234 global $conf, $user, $hookmanager;
1235
1236 $error = 0;
1237
1238 $this->db->begin();
1239
1240 // get lines so they will be clone
1241 foreach ($this->lines as $line) {
1242 $line->fetch_optionals();
1243 }
1244
1245 // Load source object
1246 $objFrom = clone $this;
1247
1248 // Change socid if needed
1249 if (!empty($socid) && $socid != $this->socid) {
1250 $objsoc = new Societe($this->db);
1251
1252 if ($objsoc->fetch($socid) > 0) {
1253 $this->socid = $objsoc->id;
1254 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1255 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1256 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1257 $this->fk_project = 0;
1258 $this->fk_delivery_address = 0;
1259 }
1260
1261 // TODO Change product price if multi-prices
1262 }
1263
1264 $this->id = 0;
1265 $this->ref = '';
1266 $this->statut = self::STATUS_DRAFT;
1267
1268 // Clear fields
1269 $this->user_author_id = $user->id;
1270 $this->user_validation_id = 0;
1271 $this->date = dol_now();
1272 $this->date_commande = dol_now();
1273 $this->date_creation = '';
1274 $this->date_validation = '';
1275 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1276 $this->ref_client = '';
1277 $this->ref_customer = '';
1278 }
1279
1280 // Do not clone ref_ext
1281 $num = count($this->lines);
1282 for ($i = 0; $i < $num; $i++) {
1283 $this->lines[$i]->ref_ext = '';
1284 }
1285
1286 // Create clone
1287 $this->context['createfromclone'] = 'createfromclone';
1288 $result = $this->create($user);
1289 if ($result < 0) {
1290 $error++;
1291 }
1292
1293 if (!$error) {
1294 // copy internal contacts
1295 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1296 $error++;
1297 }
1298 }
1299
1300 if (!$error) {
1301 // copy external contacts if same company
1302 if ($this->socid == $objFrom->socid) {
1303 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1304 $error++;
1305 }
1306 }
1307 }
1308
1309 if (!$error) {
1310 // Hook of thirdparty module
1311 if (is_object($hookmanager)) {
1312 $parameters = array('objFrom' => $objFrom);
1313 $action = '';
1314 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1315 if ($reshook < 0) {
1316 $this->setErrorsFromObject($hookmanager);
1317 $error++;
1318 }
1319 }
1320 }
1321
1322 unset($this->context['createfromclone']);
1323
1324 // End
1325 if (!$error) {
1326 $this->db->commit();
1327 return $this->id;
1328 } else {
1329 $this->db->rollback();
1330 return -1;
1331 }
1332 }
1333
1334
1342 public function createFromProposal($object, User $user)
1343 {
1344 global $conf, $hookmanager;
1345
1346 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1347 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1348
1349 $error = 0;
1350
1351 $this->date_commande = dol_now();
1352 $this->date = dol_now();
1353 $this->source = 0;
1354
1355 $num = count($object->lines);
1356 for ($i = 0; $i < $num; $i++) {
1357 $line = new OrderLine($this->db);
1358
1359 $line->libelle = $object->lines[$i]->libelle;
1360 $line->label = $object->lines[$i]->label;
1361 $line->desc = $object->lines[$i]->desc;
1362 $line->price = $object->lines[$i]->price;
1363 $line->subprice = $object->lines[$i]->subprice;
1364 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1365 $line->tva_tx = $object->lines[$i]->tva_tx;
1366 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1367 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1368 $line->qty = $object->lines[$i]->qty;
1369 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1370 $line->remise_percent = $object->lines[$i]->remise_percent;
1371 $line->fk_product = $object->lines[$i]->fk_product;
1372 $line->info_bits = $object->lines[$i]->info_bits;
1373 $line->product_type = $object->lines[$i]->product_type;
1374 $line->rang = $object->lines[$i]->rang;
1375 $line->special_code = $object->lines[$i]->special_code;
1376 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1377 $line->fk_unit = $object->lines[$i]->fk_unit;
1378
1379 $line->date_start = $object->lines[$i]->date_start;
1380 $line->date_end = $object->lines[$i]->date_end;
1381
1382 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1383 $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);
1384 $line->pa_ht = $marginInfos[0];
1385 $line->marge_tx = $marginInfos[1];
1386 $line->marque_tx = $marginInfos[2];
1387
1388 $line->origin = $object->element;
1389 $line->origin_id = $object->lines[$i]->id;
1390
1391 // get extrafields from original line
1392 $object->lines[$i]->fetch_optionals();
1393 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1394 $line->array_options[$options_key] = $value;
1395 }
1396
1397 $this->lines[$i] = $line;
1398 }
1399
1400 $this->entity = $object->entity;
1401 $this->socid = $object->socid;
1402 $this->fk_project = $object->fk_project;
1403 $this->cond_reglement_id = $object->cond_reglement_id;
1404 $this->deposit_percent = $object->deposit_percent;
1405 $this->mode_reglement_id = $object->mode_reglement_id;
1406 $this->fk_account = $object->fk_account;
1407 $this->availability_id = $object->availability_id;
1408 $this->demand_reason_id = $object->demand_reason_id;
1409 $this->delivery_date = $object->delivery_date;
1410 $this->shipping_method_id = $object->shipping_method_id;
1411 $this->warehouse_id = $object->warehouse_id;
1412 $this->fk_delivery_address = $object->fk_delivery_address;
1413 $this->contact_id = $object->contact_id;
1414 $this->ref_client = $object->ref_client;
1415 $this->ref_customer = $object->ref_client;
1416
1417 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1418 $this->note_private = $object->note_private;
1419 $this->note_public = $object->note_public;
1420 }
1421
1422 $this->origin = $object->element;
1423 $this->origin_id = $object->id;
1424
1425 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1426 if (isModEnabled('multicurrency')) {
1427 if (!empty($object->multicurrency_code)) {
1428 $this->multicurrency_code = $object->multicurrency_code;
1429 }
1430 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1431 $this->multicurrency_tx = $object->multicurrency_tx;
1432 }
1433
1434 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1435 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1436 $this->fk_multicurrency = $tmparray[0];
1437 $this->multicurrency_tx = $tmparray[1];
1438 } else {
1439 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1440 }
1441 if (empty($this->fk_multicurrency)) {
1442 $this->multicurrency_code = $conf->currency;
1443 $this->fk_multicurrency = 0;
1444 $this->multicurrency_tx = 1;
1445 }
1446 }
1447
1448 // get extrafields from original line
1449 $object->fetch_optionals();
1450
1451 $e = new ExtraFields($this->db);
1452 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1453
1454 foreach ($object->array_options as $options_key => $value) {
1455 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1456 $this->array_options[$options_key] = $value;
1457 }
1458 }
1459 // Possibility to add external linked objects with hooks
1460 $this->linked_objects[$this->origin] = $this->origin_id;
1461 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1462 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1463 }
1464
1465 $ret = $this->create($user);
1466
1467 if ($ret > 0) {
1468 // Actions hooked (by external module)
1469 $hookmanager->initHooks(array('orderdao'));
1470
1471 $parameters = array('objFrom' => $object);
1472 $action = '';
1473 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1474 if ($reshook < 0) {
1475 $this->setErrorsFromObject($hookmanager);
1476 $error++;
1477 }
1478
1479 if (!$error) {
1480 // Validate immediately the order
1481 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1482 $this->fetch($ret);
1483 $this->valid($user);
1484 }
1485 return $ret;
1486 } else {
1487 return -1;
1488 }
1489 } else {
1490 return -1;
1491 }
1492 }
1493
1494
1535 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)
1536 {
1537 global $mysoc, $conf, $langs, $user;
1538
1539 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1540 $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";
1541 $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";
1542 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1543
1544 if ($this->statut == self::STATUS_DRAFT) {
1545 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1546
1547 // Clean parameters
1548
1549 if (empty($remise_percent)) {
1550 $remise_percent = 0;
1551 }
1552 if (empty($qty)) {
1553 $qty = 0;
1554 }
1555 if (empty($info_bits)) {
1556 $info_bits = 0;
1557 }
1558 if (empty($rang)) {
1559 $rang = 0;
1560 }
1561 if (empty($txtva)) {
1562 $txtva = 0;
1563 }
1564 if (empty($txlocaltax1)) {
1565 $txlocaltax1 = 0;
1566 }
1567 if (empty($txlocaltax2)) {
1568 $txlocaltax2 = 0;
1569 }
1570 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1571 $fk_parent_line = 0;
1572 }
1573 if (empty($this->fk_multicurrency)) {
1574 $this->fk_multicurrency = 0;
1575 }
1576 if (empty($ref_ext)) {
1577 $ref_ext = '';
1578 }
1579
1580 $remise_percent = (float) price2num($remise_percent);
1581 $qty = (float) price2num($qty);
1582 $pu_ht = price2num($pu_ht);
1583 $pu_ht_devise = price2num($pu_ht_devise);
1584 $pu_ttc = price2num($pu_ttc);
1585 $pa_ht = (float) price2num($pa_ht);
1586 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1587 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1588 }
1589 $txlocaltax1 = price2num($txlocaltax1);
1590 $txlocaltax2 = price2num($txlocaltax2);
1591 if ($price_base_type == 'HT') {
1592 $pu = $pu_ht;
1593 } else {
1594 $pu = $pu_ttc;
1595 }
1596 $label = trim($label);
1597 $desc = trim($desc);
1598
1599 // Check parameters
1600 if ($type < 0) {
1601 return -1;
1602 }
1603
1604 if ($date_start && $date_end && $date_start > $date_end) {
1605 $langs->load("errors");
1606 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1607 return -1;
1608 }
1609
1610 $this->db->begin();
1611
1612 $product_type = $type;
1613 if (!empty($fk_product) && $fk_product > 0) {
1614 $product = new Product($this->db);
1615 $result = $product->fetch($fk_product);
1616 $product_type = $product->type;
1617
1618 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
1619 $langs->load("errors");
1620 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1621 $this->errors[] = $this->error;
1622 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1623 $this->db->rollback();
1625 }
1626 }
1627 // Calcul du total TTC et de la TVA pour la ligne a partir de
1628 // qty, pu, remise_percent et txtva
1629 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1630 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1631
1632 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1633
1634 // Clean vat code
1635 $reg = array();
1636 $vat_src_code = '';
1637 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1638 $vat_src_code = $reg[1];
1639 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1640 }
1641
1642 $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);
1643
1644 /*var_dump($txlocaltax1);
1645 var_dump($txlocaltax2);
1646 var_dump($localtaxes_type);
1647 var_dump($tabprice);
1648 var_dump($tabprice[9]);
1649 var_dump($tabprice[10]);
1650 exit;*/
1651
1652 $total_ht = $tabprice[0];
1653 $total_tva = $tabprice[1];
1654 $total_ttc = $tabprice[2];
1655 $total_localtax1 = $tabprice[9];
1656 $total_localtax2 = $tabprice[10];
1657 $pu_ht = $tabprice[3];
1658
1659 // MultiCurrency
1660 $multicurrency_total_ht = $tabprice[16];
1661 $multicurrency_total_tva = $tabprice[17];
1662 $multicurrency_total_ttc = $tabprice[18];
1663 $pu_ht_devise = $tabprice[19];
1664
1665 // Rang to use
1666 $ranktouse = $rang;
1667 if ($ranktouse == -1) {
1668 $rangmax = $this->line_max($fk_parent_line);
1669 $ranktouse = $rangmax + 1;
1670 }
1671
1672 // TODO A virer
1673 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1674 $price = $pu;
1675 $remise = 0;
1676 if ($remise_percent > 0) {
1677 $remise = round(((float) $pu * $remise_percent / 100), 2);
1678 $price = (float) $pu - $remise;
1679 }
1680
1681 // Insert line
1682 $this->line = new OrderLine($this->db);
1683
1684 $this->line->context = $this->context;
1685
1686 $this->line->fk_commande = $this->id;
1687 $this->line->label = $label;
1688 $this->line->desc = $desc;
1689 $this->line->qty = $qty;
1690 $this->line->ref_ext = $ref_ext;
1691
1692 $this->line->vat_src_code = $vat_src_code;
1693 $this->line->tva_tx = $txtva;
1694 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1695 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1696 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1697 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1698 $this->line->fk_product = $fk_product;
1699 $this->line->product_type = $product_type;
1700 $this->line->fk_remise_except = $fk_remise_except;
1701 $this->line->remise_percent = $remise_percent;
1702 $this->line->subprice = (float) $pu_ht;
1703 $this->line->rang = $ranktouse;
1704 $this->line->info_bits = $info_bits;
1705 $this->line->total_ht = (float) $total_ht;
1706 $this->line->total_tva = (float) $total_tva;
1707 $this->line->total_localtax1 = (float) $total_localtax1;
1708 $this->line->total_localtax2 = (float) $total_localtax2;
1709 $this->line->total_ttc = (float) $total_ttc;
1710 $this->line->special_code = $special_code;
1711 $this->line->origin = $origin;
1712 $this->line->origin_id = $origin_id;
1713 $this->line->fk_parent_line = $fk_parent_line;
1714 $this->line->fk_unit = $fk_unit;
1715
1716 $this->line->date_start = $date_start;
1717 $this->line->date_end = $date_end;
1718
1719 $this->line->fk_fournprice = $fk_fournprice;
1720 $this->line->pa_ht = $pa_ht;
1721
1722 // Multicurrency
1723 $this->line->fk_multicurrency = $this->fk_multicurrency;
1724 $this->line->multicurrency_code = $this->multicurrency_code;
1725 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
1726 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
1727 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
1728 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
1729
1730 // TODO Ne plus utiliser
1731 $this->line->price = $price;
1732
1733 if (is_array($array_options) && count($array_options) > 0) {
1734 $this->line->array_options = $array_options;
1735 }
1736
1737 $result = $this->line->insert($user);
1738 if ($result > 0) {
1739 // Reorder if child line
1740 if (!empty($fk_parent_line)) {
1741 $this->line_order(true, 'DESC');
1742 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1743 $linecount = count($this->lines);
1744 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1745 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1746 }
1747 }
1748
1749 // Mise a jour information denormalisees au niveau de la commande meme
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 $this->db->commit();
1756 $this->lines[] = $this->line;
1757 return $this->line->id;
1758 } else {
1759 $this->db->rollback();
1760 return -1;
1761 }
1762 } else {
1763 $this->error = $this->line->error;
1764 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1765 $this->db->rollback();
1766 return -2;
1767 }
1768 } else {
1769 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1770 return -3;
1771 }
1772 }
1773
1774
1775 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1789 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1790 {
1791 // phpcs:enable
1792 global $conf, $mysoc;
1793
1794 if (!$qty) {
1795 $qty = 1;
1796 }
1797
1798 if ($idproduct > 0) {
1799 $prod = new Product($this->db);
1800 $prod->fetch($idproduct);
1801
1802 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1803 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1804 if (empty($tva_tx)) {
1805 $tva_npr = 0;
1806 }
1807 $vat_src_code = ''; // May be defined into tva_tx
1808
1809 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1810 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1811
1812 // multiprix
1813 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1814 $price = $prod->multiprices[$this->thirdparty->price_level];
1815 } else {
1816 $price = $prod->price;
1817 }
1818
1819 $line = new OrderLine($this->db);
1820
1821 $line->context = $this->context;
1822
1823 $line->fk_product = $idproduct;
1824 $line->desc = $prod->description;
1825 $line->qty = $qty;
1826 $line->subprice = $price;
1827 $line->remise_percent = $remise_percent;
1828 $line->vat_src_code = $vat_src_code;
1829 $line->tva_tx = $tva_tx;
1830 $line->localtax1_tx = $localtax1_tx;
1831 $line->localtax2_tx = $localtax2_tx;
1832
1833 $line->product_ref = $prod->ref;
1834 $line->product_label = $prod->label;
1835 $line->product_desc = $prod->description;
1836 $line->fk_unit = $prod->fk_unit;
1837
1838 // Save the start and end date of the line in the object
1839 if ($date_start) {
1840 $line->date_start = $date_start;
1841 }
1842 if ($date_end) {
1843 $line->date_end = $date_end;
1844 }
1845
1846 $this->lines[] = $line;
1847
1866 }
1867 }
1868
1869
1879 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1880 {
1881 // Check parameters
1882 if (empty($id) && empty($ref) && empty($ref_ext)) {
1883 return -1;
1884 }
1885
1886 $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';
1887 $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';
1888 $sql .= ', c.fk_account';
1889 $sql .= ', c.date_commande, c.date_valid, c.tms';
1890 $sql .= ', c.date_livraison as delivery_date';
1891 $sql .= ', c.fk_shipping_method';
1892 $sql .= ', c.fk_warehouse';
1893 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1894 $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';
1895 $sql .= ', c.fk_incoterms, c.location_incoterms';
1896 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1897 $sql .= ", c.module_source, c.pos_source";
1898 $sql .= ", i.libelle as label_incoterms";
1899 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1900 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1901 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1902 $sql .= ', dr.code as demand_reason_code';
1903 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
1904 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1905 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1906 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1907 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1908 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1909
1910 if ($id) {
1911 $sql .= " WHERE c.rowid=".((int) $id);
1912 } else {
1913 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
1914 }
1915
1916 if ($ref) {
1917 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1918 }
1919 if ($ref_ext) {
1920 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1921 }
1922
1923 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1924 $result = $this->db->query($sql);
1925 if ($result) {
1926 $obj = $this->db->fetch_object($result);
1927 if ($obj) {
1928 $this->id = $obj->rowid;
1929 $this->entity = $obj->entity;
1930
1931 $this->ref = $obj->ref;
1932 $this->ref_client = $obj->ref_client;
1933 $this->ref_customer = $obj->ref_client;
1934 $this->ref_ext = $obj->ref_ext;
1935
1936 $this->socid = $obj->fk_soc;
1937 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1938
1939 $this->fk_project = $obj->fk_project;
1940 $this->project = null; // Clear if another value was already set by fetch_projet
1941
1942 $this->statut = $obj->fk_statut;
1943 $this->status = $obj->fk_statut;
1944
1945 $this->user_author_id = $obj->fk_user_author;
1946 $this->user_creation_id = $obj->fk_user_author;
1947 $this->user_validation_id = $obj->fk_user_valid;
1948 $this->user_modification_id = $obj->fk_user_modif;
1949 $this->total_ht = $obj->total_ht;
1950 $this->total_tva = $obj->total_tva;
1951 $this->total_localtax1 = $obj->total_localtax1;
1952 $this->total_localtax2 = $obj->total_localtax2;
1953 $this->total_ttc = $obj->total_ttc;
1954 $this->date = $this->db->jdate($obj->date_commande);
1955 $this->date_commande = $this->db->jdate($obj->date_commande);
1956 $this->date_creation = $this->db->jdate($obj->date_creation);
1957 $this->date_validation = $this->db->jdate($obj->date_valid);
1958 $this->date_modification = $this->db->jdate($obj->tms);
1959 $this->source = $obj->source;
1960 $this->billed = $obj->billed;
1961 $this->note = $obj->note_private; // deprecated
1962 $this->note_private = $obj->note_private;
1963 $this->note_public = $obj->note_public;
1964 $this->model_pdf = $obj->model_pdf;
1965 $this->last_main_doc = $obj->last_main_doc;
1966 $this->mode_reglement_id = $obj->fk_mode_reglement;
1967 $this->mode_reglement_code = $obj->mode_reglement_code;
1968 $this->mode_reglement = $obj->mode_reglement_libelle;
1969 $this->cond_reglement_id = $obj->fk_cond_reglement;
1970 $this->cond_reglement_code = $obj->cond_reglement_code;
1971 $this->cond_reglement = $obj->cond_reglement_libelle;
1972 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1973 $this->deposit_percent = $obj->deposit_percent;
1974 $this->fk_account = $obj->fk_account;
1975 $this->availability_id = $obj->fk_availability;
1976 $this->availability_code = $obj->availability_code;
1977 $this->availability = $obj->availability_label;
1978 $this->demand_reason_id = $obj->fk_input_reason;
1979 $this->demand_reason_code = $obj->demand_reason_code;
1980 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1981 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1982 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1983 $this->fk_delivery_address = $obj->fk_delivery_address;
1984 $this->module_source = $obj->module_source;
1985 $this->pos_source = $obj->pos_source;
1986
1987 //Incoterms
1988 $this->fk_incoterms = $obj->fk_incoterms;
1989 $this->location_incoterms = $obj->location_incoterms;
1990 $this->label_incoterms = $obj->label_incoterms;
1991
1992 // Multicurrency
1993 $this->fk_multicurrency = $obj->fk_multicurrency;
1994 $this->multicurrency_code = $obj->multicurrency_code;
1995 $this->multicurrency_tx = $obj->multicurrency_tx;
1996 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1997 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1998 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1999
2000 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2001
2002 $this->lines = array();
2003
2004 // Retrieve all extrafield
2005 // fetch optionals attributes and labels
2006 $this->fetch_optionals();
2007
2008 $this->db->free($result);
2009
2010 // Lines
2011 $result = $this->fetch_lines();
2012 if ($result < 0) {
2013 return -3;
2014 }
2015 return 1;
2016 } else {
2017 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2018 return 0;
2019 }
2020 } else {
2021 $this->error = $this->db->error();
2022 return -1;
2023 }
2024 }
2025
2026
2027 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2034 public function insert_discount($idremise)
2035 {
2036 // phpcs:enable
2037 global $langs;
2038
2039 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2040 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2041
2042 $this->db->begin();
2043
2044 $remise = new DiscountAbsolute($this->db);
2045 $result = $remise->fetch($idremise);
2046
2047 if ($result > 0) {
2048 if ($remise->fk_facture) { // Protection against multiple submission
2049 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2050 $this->db->rollback();
2051 return -5;
2052 }
2053
2054 $line = new OrderLine($this->db);
2055
2056 $line->fk_commande = $this->id;
2057 $line->fk_remise_except = $remise->id;
2058 $line->desc = $remise->description; // Description ligne
2059 $line->vat_src_code = $remise->vat_src_code;
2060 $line->tva_tx = $remise->tva_tx;
2061 $line->subprice = -(float) $remise->amount_ht;
2062 $line->price = -(float) $remise->amount_ht;
2063 $line->fk_product = 0; // Id produit predefini
2064 $line->qty = 1;
2065 $line->remise_percent = 0;
2066 $line->rang = -1;
2067 $line->info_bits = 2;
2068
2069 $line->total_ht = -(float) $remise->amount_ht;
2070 $line->total_tva = -(float) $remise->amount_tva;
2071 $line->total_ttc = -(float) $remise->amount_ttc;
2072
2073 $result = $line->insert();
2074 if ($result > 0) {
2075 $result = $this->update_price(1);
2076 if ($result > 0) {
2077 $this->db->commit();
2078 return 1;
2079 } else {
2080 $this->db->rollback();
2081 return -1;
2082 }
2083 } else {
2084 $this->error = $line->error;
2085 $this->errors = $line->errors;
2086 $this->db->rollback();
2087 return -2;
2088 }
2089 } else {
2090 $this->db->rollback();
2091 return -2;
2092 }
2093 }
2094
2095
2096 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2104 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2105 {
2106 // phpcs:enable
2107 global $langs, $conf;
2108
2109 $this->lines = array();
2110
2111 $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,';
2112 $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,';
2113 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2114 $sql .= ' l.fk_unit,';
2115 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2116 $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,';
2117 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2118 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2119 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2120 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2121 if ($only_product) {
2122 $sql .= ' AND p.fk_product_type = 0';
2123 }
2124 $sql .= ' ORDER BY l.rang, l.rowid';
2125
2126 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2127 $result = $this->db->query($sql);
2128 if ($result) {
2129 $num = $this->db->num_rows($result);
2130
2131 $i = 0;
2132 while ($i < $num) {
2133 $objp = $this->db->fetch_object($result);
2134
2135 $line = new OrderLine($this->db);
2136
2137 $line->rowid = $objp->rowid;
2138 $line->id = $objp->rowid;
2139 $line->fk_commande = $objp->fk_commande;
2140 $line->commande_id = $objp->fk_commande;
2141 $line->label = $objp->custom_label;
2142 $line->desc = $objp->description;
2143 $line->description = $objp->description; // Description line
2144 $line->product_type = $objp->product_type;
2145 $line->qty = $objp->qty;
2146 $line->ref_ext = $objp->ref_ext;
2147
2148 $line->vat_src_code = $objp->vat_src_code;
2149 $line->tva_tx = $objp->tva_tx;
2150 $line->localtax1_tx = $objp->localtax1_tx;
2151 $line->localtax2_tx = $objp->localtax2_tx;
2152 $line->localtax1_type = $objp->localtax1_type;
2153 $line->localtax2_type = $objp->localtax2_type;
2154 $line->total_ht = $objp->total_ht;
2155 $line->total_ttc = $objp->total_ttc;
2156 $line->total_tva = $objp->total_tva;
2157 $line->total_localtax1 = $objp->total_localtax1;
2158 $line->total_localtax2 = $objp->total_localtax2;
2159 $line->subprice = $objp->subprice;
2160 $line->fk_remise_except = $objp->fk_remise_except;
2161 $line->remise_percent = $objp->remise_percent;
2162 $line->price = $objp->price;
2163 $line->fk_product = $objp->fk_product;
2164 $line->fk_fournprice = $objp->fk_fournprice;
2165 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2166 $line->pa_ht = $marginInfos[0];
2167 $line->marge_tx = $marginInfos[1];
2168 $line->marque_tx = $marginInfos[2];
2169 $line->rang = $objp->rang;
2170 $line->info_bits = $objp->info_bits;
2171 $line->special_code = $objp->special_code;
2172 $line->fk_parent_line = $objp->fk_parent_line;
2173
2174 $line->ref = $objp->product_ref;
2175 $line->libelle = $objp->product_label;
2176
2177 $line->product_ref = $objp->product_ref;
2178 $line->product_label = $objp->product_label;
2179 $line->product_tosell = $objp->product_tosell;
2180 $line->product_tobuy = $objp->product_tobuy;
2181 $line->product_desc = $objp->product_desc;
2182 $line->product_tobatch = $objp->product_tobatch;
2183 $line->product_barcode = $objp->product_barcode;
2184
2185 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2186 $line->fk_unit = $objp->fk_unit;
2187
2188 $line->weight = $objp->weight;
2189 $line->weight_units = $objp->weight_units;
2190 $line->volume = $objp->volume;
2191 $line->volume_units = $objp->volume_units;
2192
2193 $line->date_start = $this->db->jdate($objp->date_start);
2194 $line->date_end = $this->db->jdate($objp->date_end);
2195
2196 // Multicurrency
2197 $line->fk_multicurrency = $objp->fk_multicurrency;
2198 $line->multicurrency_code = $objp->multicurrency_code;
2199 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2200 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2201 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2202 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2203
2204 $line->fetch_optionals();
2205
2206 // multilangs
2207 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2208 $tmpproduct = new Product($this->db);
2209 $tmpproduct->fetch($objp->fk_product);
2210 $tmpproduct->getMultiLangs();
2211
2212 $line->multilangs = $tmpproduct->multilangs;
2213 }
2214
2215 $this->lines[$i] = $line;
2216
2217 $i++;
2218 }
2219
2220 $this->db->free($result);
2221
2222 return 1;
2223 } else {
2224 $this->error = $this->db->error();
2225 return -3;
2226 }
2227 }
2228
2229
2235 public function getNbOfProductsLines()
2236 {
2237 $nb = 0;
2238 foreach ($this->lines as $line) {
2239 if ($line->product_type == 0) {
2240 $nb++;
2241 }
2242 }
2243 return $nb;
2244 }
2245
2251 public function getNbOfServicesLines()
2252 {
2253 $nb = 0;
2254 foreach ($this->lines as $line) {
2255 if ($line->product_type == 1) {
2256 $nb++;
2257 }
2258 }
2259 return $nb;
2260 }
2261
2267 public function getNbOfShipments()
2268 {
2269 $nb = 0;
2270
2271 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2272 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2273 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2274 $sql .= ' WHERE';
2275 $sql .= ' ed.fk_elementdet = cd.rowid';
2276 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2277 //print $sql;
2278
2279 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2280 $resql = $this->db->query($sql);
2281 if ($resql) {
2282 $obj = $this->db->fetch_object($resql);
2283 if ($obj) {
2284 $nb = $obj->nb;
2285 }
2286
2287 $this->db->free($resql);
2288 return $nb;
2289 } else {
2290 $this->error = $this->db->lasterror();
2291 return -1;
2292 }
2293 }
2294
2303 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2304 {
2305 $this->expeditions = array();
2306
2307 $sql = 'SELECT cd.rowid, cd.fk_product,';
2308 $sql .= ' sum(ed.qty) as qty';
2309 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2310 if ($filtre_statut >= 0) {
2311 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2312 }
2313 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2314 $sql .= ' WHERE';
2315 if ($filtre_statut >= 0) {
2316 $sql .= ' ed.fk_expedition = e.rowid AND';
2317 }
2318 $sql .= ' ed.fk_elementdet = cd.rowid';
2319 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2320 if ($fk_product > 0) {
2321 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2322 }
2323 if ($filtre_statut >= 0) {
2324 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2325 }
2326 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2327 //print $sql;
2328
2329 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2330 $resql = $this->db->query($sql);
2331 if ($resql) {
2332 $num = $this->db->num_rows($resql);
2333 $i = 0;
2334 while ($i < $num) {
2335 $obj = $this->db->fetch_object($resql);
2336 $this->expeditions[$obj->rowid] = $obj->qty;
2337 $i++;
2338 }
2339 $this->db->free($resql);
2340 return $num;
2341 } else {
2342 $this->error = $this->db->lasterror();
2343 return -1;
2344 }
2345 }
2346
2352 public function countNbOfShipments()
2353 {
2354 $sql = 'SELECT count(*)';
2355 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2356 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2357 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2358 $sql .= " AND el.sourcetype = 'commande'";
2359 $sql .= " AND el.fk_target = e.rowid";
2360 $sql .= " AND el.targettype = 'shipping'";
2361
2362 $resql = $this->db->query($sql);
2363 if ($resql) {
2364 $row = $this->db->fetch_row($resql);
2365 return $row[0];
2366 } else {
2367 dol_print_error($this->db);
2368 }
2369
2370 return 0;
2371 }
2372
2373 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2382 /*public function stock_array($filtre_statut = self::STATUS_CANCELED)
2383 {
2384 // phpcs:enable
2385 $this->stocks = array();
2386
2387 // Tableau des id de produit de la commande
2388 $array_of_product = array();
2389
2390 // Recherche total en stock pour chaque produit
2391 // TODO $array_of_product est défini vide juste au dessus !!
2392 if (count($array_of_product)) {
2393 $sql = "SELECT fk_product, sum(ps.reel) as total";
2394 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2395 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2396 $sql .= ' GROUP BY fk_product';
2397 $resql = $this->db->query($sql);
2398 if ($resql) {
2399 $num = $this->db->num_rows($resql);
2400 $i = 0;
2401 while ($i < $num) {
2402 $obj = $this->db->fetch_object($resql);
2403 $this->stocks[$obj->fk_product] = $obj->total;
2404 $i++;
2405 }
2406 $this->db->free($resql);
2407 }
2408 }
2409 return 0;
2410 }*/
2411
2420 public function deleteLine($user = null, $lineid = 0, $id = 0)
2421 {
2422 if ($this->statut == self::STATUS_DRAFT) {
2423 $this->db->begin();
2424
2425 // Delete line
2426 $line = new OrderLine($this->db);
2427
2428 $line->context = $this->context;
2429
2430 // Load data
2431 $line->fetch($lineid);
2432
2433 if ($id > 0 && $line->fk_commande != $id) {
2434 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2435 return -1;
2436 }
2437
2438 // Memorize previous line for triggers
2439 $staticline = clone $line;
2440 $line->oldline = $staticline;
2441
2442 if ($line->delete($user) > 0) {
2443 $result = $this->update_price(1);
2444
2445 if ($result > 0) {
2446 $this->db->commit();
2447 return 1;
2448 } else {
2449 $this->db->rollback();
2450 $this->error = $this->db->lasterror();
2451 return -1;
2452 }
2453 } else {
2454 $this->db->rollback();
2455 $this->error = $line->error;
2456 return -1;
2457 }
2458 } else {
2459 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2460 return -1;
2461 }
2462 }
2463
2464 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2475 public function set_remise($user, $remise, $notrigger = 0)
2476 {
2477 // phpcs:enable
2478 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2479 // @phan-suppress-next-line PhanDeprecatedFunction
2480 return $this->setDiscount($user, $remise, $notrigger);
2481 }
2482
2491 public function setDiscount($user, $remise, $notrigger = 0)
2492 {
2493 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2494
2495 if ($user->hasRight('commande', 'creer')) {
2496 $error = 0;
2497
2498 $this->db->begin();
2499
2500 $remise = price2num($remise, 2);
2501
2502 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2503 $sql .= ' SET remise_percent = '.((float) $remise);
2504 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2505
2506 dol_syslog(__METHOD__, LOG_DEBUG);
2507 $resql = $this->db->query($sql);
2508 if (!$resql) {
2509 $this->errors[] = $this->db->error();
2510 $error++;
2511 }
2512
2513 if (!$error) {
2514 $this->oldcopy = clone $this;
2515 $this->remise_percent = $remise;
2516 $this->update_price(1);
2517 }
2518
2519 if (!$notrigger && empty($error)) {
2520 // Call trigger
2521 $result = $this->call_trigger('ORDER_MODIFY', $user);
2522 if ($result < 0) {
2523 $error++;
2524 }
2525 // End call triggers
2526 }
2527
2528 if (!$error) {
2529 $this->db->commit();
2530 return 1;
2531 } else {
2532 foreach ($this->errors as $errmsg) {
2533 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2534 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2535 }
2536 $this->db->rollback();
2537 return -1 * $error;
2538 }
2539 }
2540
2541 return 0;
2542 }
2543
2544
2545 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2554 /*
2555 public function set_remise_absolue($user, $remise, $notrigger = 0)
2556 {
2557 // phpcs:enable
2558 if (empty($remise)) {
2559 $remise = 0;
2560 }
2561
2562 $remise = price2num($remise);
2563
2564 if ($user->hasRight('commande', 'creer')) {
2565 $error = 0;
2566
2567 $this->db->begin();
2568
2569 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2570 $sql .= ' SET remise_absolue = '.((float) $remise);
2571 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2572
2573 dol_syslog(__METHOD__, LOG_DEBUG);
2574 $resql = $this->db->query($sql);
2575 if (!$resql) {
2576 $this->errors[] = $this->db->error();
2577 $error++;
2578 }
2579
2580 if (!$error) {
2581 $this->oldcopy = clone $this;
2582 $this->remise_absolue = $remise;
2583 $this->update_price(1);
2584 }
2585
2586 if (!$notrigger && empty($error)) {
2587 // Call trigger
2588 $result = $this->call_trigger('ORDER_MODIFY', $user);
2589 if ($result < 0) {
2590 $error++;
2591 }
2592 // End call triggers
2593 }
2594
2595 if (!$error) {
2596 $this->db->commit();
2597 return 1;
2598 } else {
2599 foreach ($this->errors as $errmsg) {
2600 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2601 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2602 }
2603 $this->db->rollback();
2604 return -1 * $error;
2605 }
2606 }
2607
2608 return 0;
2609 }
2610 */
2611
2612 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2621 public function set_date($user, $date, $notrigger = 0)
2622 {
2623 // phpcs:enable
2624 if ($user->hasRight('commande', 'creer')) {
2625 $error = 0;
2626
2627 $this->db->begin();
2628
2629 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2630 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2631 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2632
2633 dol_syslog(__METHOD__, LOG_DEBUG);
2634 $resql = $this->db->query($sql);
2635 if (!$resql) {
2636 $this->errors[] = $this->db->error();
2637 $error++;
2638 }
2639
2640 if (!$error) {
2641 $this->oldcopy = clone $this;
2642 $this->date = $date;
2643 }
2644
2645 if (!$notrigger && empty($error)) {
2646 // Call trigger
2647 $result = $this->call_trigger('ORDER_MODIFY', $user);
2648 if ($result < 0) {
2649 $error++;
2650 }
2651 // End call triggers
2652 }
2653
2654 if (!$error) {
2655 $this->db->commit();
2656 return 1;
2657 } else {
2658 foreach ($this->errors as $errmsg) {
2659 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2660 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2661 }
2662 $this->db->rollback();
2663 return -1 * $error;
2664 }
2665 } else {
2666 return -2;
2667 }
2668 }
2669
2670 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2680 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2681 {
2682 // phpcs:enable
2683 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2684 }
2685
2694 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2695 {
2696 if ($user->hasRight('commande', 'creer')) {
2697 $error = 0;
2698
2699 $this->db->begin();
2700
2701 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2702 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2703 $sql .= " WHERE rowid = ".((int) $this->id);
2704
2705 dol_syslog(__METHOD__, LOG_DEBUG);
2706 $resql = $this->db->query($sql);
2707 if (!$resql) {
2708 $this->errors[] = $this->db->error();
2709 $error++;
2710 }
2711
2712 if (!$error) {
2713 $this->oldcopy = clone $this;
2714 $this->delivery_date = $delivery_date;
2715 }
2716
2717 if (!$notrigger && empty($error)) {
2718 // Call trigger
2719 $result = $this->call_trigger('ORDER_MODIFY', $user);
2720 if ($result < 0) {
2721 $error++;
2722 }
2723 // End call triggers
2724 }
2725
2726 if (!$error) {
2727 $this->db->commit();
2728 return 1;
2729 } else {
2730 foreach ($this->errors as $errmsg) {
2731 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2732 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2733 }
2734 $this->db->rollback();
2735 return -1 * $error;
2736 }
2737 } else {
2738 return -2;
2739 }
2740 }
2741
2742 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2756 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2757 {
2758 // phpcs:enable
2759 global $user;
2760
2761 $ga = array();
2762
2763 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2764 $sql .= " c.rowid as cid, c.ref";
2765 if (!$user->hasRight('societe', 'client', 'voir')) {
2766 $sql .= ", sc.fk_soc, sc.fk_user";
2767 }
2768 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2769 if (!$user->hasRight('societe', 'client', 'voir')) {
2770 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2771 }
2772 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2773 $sql .= " AND c.fk_soc = s.rowid";
2774 if (!$user->hasRight('societe', 'client', 'voir')) {
2775 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2776 }
2777 if ($socid) {
2778 $sql .= " AND s.rowid = ".((int) $socid);
2779 }
2780 if ($draft) {
2781 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2782 }
2783 if (is_object($excluser)) {
2784 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2785 }
2786 $sql .= $this->db->order($sortfield, $sortorder);
2787 $sql .= $this->db->plimit($limit, $offset);
2788
2789 $result = $this->db->query($sql);
2790 if ($result) {
2791 $numc = $this->db->num_rows($result);
2792 if ($numc) {
2793 $i = 0;
2794 while ($i < $numc) {
2795 $obj = $this->db->fetch_object($result);
2796
2797 if ($shortlist == 1) {
2798 $ga[$obj->cid] = $obj->ref;
2799 } elseif ($shortlist == 2) {
2800 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2801 } else {
2802 $ga[$i]['id'] = $obj->cid;
2803 $ga[$i]['ref'] = $obj->ref;
2804 $ga[$i]['name'] = $obj->name;
2805 }
2806 $i++;
2807 }
2808 }
2809 return $ga;
2810 } else {
2811 dol_print_error($this->db);
2812 return -1;
2813 }
2814 }
2815
2823 public function availability($availability_id, $notrigger = 0)
2824 {
2825 global $user;
2826
2827 dol_syslog('Commande::availability('.$availability_id.')');
2828 if ($this->statut >= self::STATUS_DRAFT) {
2829 $error = 0;
2830
2831 $this->db->begin();
2832
2833 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2834 $sql .= ' SET fk_availability = '.((int) $availability_id);
2835 $sql .= ' WHERE rowid='.((int) $this->id);
2836
2837 dol_syslog(__METHOD__, LOG_DEBUG);
2838 $resql = $this->db->query($sql);
2839 if (!$resql) {
2840 $this->errors[] = $this->db->error();
2841 $error++;
2842 }
2843
2844 if (!$error) {
2845 $this->oldcopy = clone $this;
2846 $this->availability_id = $availability_id;
2847 }
2848
2849 if (!$notrigger && empty($error)) {
2850 // Call trigger
2851 $result = $this->call_trigger('ORDER_MODIFY', $user);
2852 if ($result < 0) {
2853 $error++;
2854 }
2855 // End call triggers
2856 }
2857
2858 if (!$error) {
2859 $this->db->commit();
2860 return 1;
2861 } else {
2862 foreach ($this->errors as $errmsg) {
2863 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2864 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2865 }
2866 $this->db->rollback();
2867 return -1 * $error;
2868 }
2869 } else {
2870 $error_str = 'Command status do not meet requirement '.$this->statut;
2871 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2872 $this->error = $error_str;
2873 $this->errors[] = $this->error;
2874 return -2;
2875 }
2876 }
2877
2878 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2886 public function demand_reason($demand_reason_id, $notrigger = 0)
2887 {
2888 // phpcs:enable
2889 global $user;
2890
2891 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2892 if ($this->statut >= self::STATUS_DRAFT) {
2893 $error = 0;
2894
2895 $this->db->begin();
2896
2897 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2898 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2899 $sql .= ' WHERE rowid='.((int) $this->id);
2900
2901 dol_syslog(__METHOD__, LOG_DEBUG);
2902 $resql = $this->db->query($sql);
2903 if (!$resql) {
2904 $this->errors[] = $this->db->error();
2905 $error++;
2906 }
2907
2908 if (!$error) {
2909 $this->oldcopy = clone $this;
2910 $this->demand_reason_id = $demand_reason_id;
2911 }
2912
2913 if (!$notrigger && empty($error)) {
2914 // Call trigger
2915 $result = $this->call_trigger('ORDER_MODIFY', $user);
2916 if ($result < 0) {
2917 $error++;
2918 }
2919 // End call triggers
2920 }
2921
2922 if (!$error) {
2923 $this->db->commit();
2924 return 1;
2925 } else {
2926 foreach ($this->errors as $errmsg) {
2927 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2928 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2929 }
2930 $this->db->rollback();
2931 return -1 * $error;
2932 }
2933 } else {
2934 $error_str = 'order status do not meet requirement '.$this->statut;
2935 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2936 $this->error = $error_str;
2937 $this->errors[] = $this->error;
2938 return -2;
2939 }
2940 }
2941
2942 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2951 public function set_ref_client($user, $ref_client, $notrigger = 0)
2952 {
2953 // phpcs:enable
2954 if ($user->hasRight('commande', 'creer')) {
2955 $error = 0;
2956
2957 $this->db->begin();
2958
2959 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2960 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2961 $sql .= ' WHERE rowid = '.((int) $this->id);
2962
2963 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2964 $resql = $this->db->query($sql);
2965 if (!$resql) {
2966 $this->errors[] = $this->db->error();
2967 $error++;
2968 }
2969
2970 if (!$error) {
2971 $this->oldcopy = clone $this;
2972 $this->ref_client = $ref_client;
2973 $this->ref_customer = $ref_client;
2974 }
2975
2976 if (!$notrigger && empty($error)) {
2977 // Call trigger
2978 $result = $this->call_trigger('ORDER_MODIFY', $user);
2979 if ($result < 0) {
2980 $error++;
2981 }
2982 // End call triggers
2983 }
2984 if (!$error) {
2985 $this->db->commit();
2986 return 1;
2987 } else {
2988 foreach ($this->errors as $errmsg) {
2989 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2990 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2991 }
2992 $this->db->rollback();
2993 return -1 * $error;
2994 }
2995 } else {
2996 return -1;
2997 }
2998 }
2999
3007 public function classifyBilled(User $user, $notrigger = 0)
3008 {
3009 $error = 0;
3010
3011 if ($this->billed) {
3012 return 0;
3013 }
3014
3015 $this->db->begin();
3016
3017 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3018 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3019
3020 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3021 if ($this->db->query($sql)) {
3022 if (!$error) {
3023 $this->oldcopy = clone $this;
3024 $this->billed = 1;
3025 }
3026
3027 if (!$notrigger && empty($error)) {
3028 // Call trigger
3029 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3030 if ($result < 0) {
3031 $error++;
3032 }
3033 // End call triggers
3034 }
3035
3036 if (!$error) {
3037 $this->db->commit();
3038 return 1;
3039 } else {
3040 foreach ($this->errors as $errmsg) {
3041 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3042 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3043 }
3044 $this->db->rollback();
3045 return -1 * $error;
3046 }
3047 } else {
3048 $this->error = $this->db->error();
3049 $this->db->rollback();
3050 return -1;
3051 }
3052 }
3053
3061 public function classifyUnBilled(User $user, $notrigger = 0)
3062 {
3063 $error = 0;
3064
3065 $this->db->begin();
3066
3067 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3068 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3069
3070 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3071 if ($this->db->query($sql)) {
3072 if (!$error) {
3073 $this->oldcopy = clone $this;
3074 $this->billed = 1;
3075 }
3076
3077 if (!$notrigger && empty($error)) {
3078 // Call trigger
3079 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3080 if ($result < 0) {
3081 $error++;
3082 }
3083 // End call triggers
3084 }
3085
3086 if (!$error) {
3087 $this->billed = 0;
3088
3089 $this->db->commit();
3090 return 1;
3091 } else {
3092 foreach ($this->errors as $errmsg) {
3093 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3094 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3095 }
3096 $this->db->rollback();
3097 return -1 * $error;
3098 }
3099 } else {
3100 $this->error = $this->db->error();
3101 $this->db->rollback();
3102 return -1;
3103 }
3104 }
3105
3106
3137 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)
3138 {
3139 global $conf, $mysoc, $langs, $user;
3140
3141 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");
3142 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3143
3144 if ($this->statut == Commande::STATUS_DRAFT) {
3145 // Clean parameters
3146 if (empty($qty)) {
3147 $qty = 0;
3148 }
3149 if (empty($info_bits)) {
3150 $info_bits = 0;
3151 }
3152 if (empty($txtva)) {
3153 $txtva = 0;
3154 }
3155 if (empty($txlocaltax1)) {
3156 $txlocaltax1 = 0;
3157 }
3158 if (empty($txlocaltax2)) {
3159 $txlocaltax2 = 0;
3160 }
3161 if (empty($remise_percent)) {
3162 $remise_percent = 0;
3163 }
3164 if (empty($special_code) || $special_code == 3) {
3165 $special_code = 0;
3166 }
3167 if (empty($ref_ext)) {
3168 $ref_ext = '';
3169 }
3170
3171 if ($date_start && $date_end && $date_start > $date_end) {
3172 $langs->load("errors");
3173 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3174 return -1;
3175 }
3176
3177 $remise_percent = (float) price2num($remise_percent);
3178 $qty = (float) price2num($qty);
3179 $pu = price2num($pu);
3180 $pa_ht = (float) price2num($pa_ht);
3181 $pu_ht_devise = price2num($pu_ht_devise);
3182 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3183 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3184 }
3185 $txlocaltax1 = (float) price2num($txlocaltax1);
3186 $txlocaltax2 = (float) price2num($txlocaltax2);
3187
3188 $this->db->begin();
3189
3190 // Calcul du total TTC et de la TVA pour la ligne a partir de
3191 // qty, pu, remise_percent et txtva
3192 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3193 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3194
3195 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3196
3197 // Clean vat code
3198 $vat_src_code = '';
3199 $reg = array();
3200 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3201 $vat_src_code = $reg[1];
3202 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3203 }
3204
3205 $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);
3206
3207 $total_ht = $tabprice[0];
3208 $total_tva = $tabprice[1];
3209 $total_ttc = $tabprice[2];
3210 $total_localtax1 = $tabprice[9];
3211 $total_localtax2 = $tabprice[10];
3212 $pu_ht = $tabprice[3];
3213 $pu_tva = $tabprice[4];
3214 $pu_ttc = $tabprice[5];
3215
3216 // MultiCurrency
3217 $multicurrency_total_ht = $tabprice[16];
3218 $multicurrency_total_tva = $tabprice[17];
3219 $multicurrency_total_ttc = $tabprice[18];
3220 $pu_ht_devise = $tabprice[19];
3221
3222 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3223 $price = $pu_ht;
3224 if ($price_base_type == 'TTC') {
3225 $subprice = $pu_ttc;
3226 } else {
3227 $subprice = $pu_ht;
3228 }
3229 $remise = 0;
3230 if ($remise_percent > 0) {
3231 $remise = round(((float) $pu * $remise_percent / 100), 2);
3232 $price = ((float) $pu - $remise);
3233 }
3234
3235 // Fetch current line from the database and then clone the object and set it in $oldline property
3236 $line = new OrderLine($this->db);
3237 $line->fetch($rowid);
3238 $line->fetch_optionals();
3239
3240 if (!empty($line->fk_product)) {
3241 $product = new Product($this->db);
3242 $result = $product->fetch($line->fk_product);
3243 $product_type = $product->type;
3244
3245 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
3246 $langs->load("errors");
3247 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3248 $this->errors[] = $this->error;
3249
3250 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3251
3252 $this->db->rollback();
3254 }
3255 }
3256
3257 $staticline = clone $line;
3258
3259 $line->oldline = $staticline;
3260 $this->line = $line;
3261 $this->line->context = $this->context;
3262 $this->line->rang = $rang;
3263
3264 // Reorder if fk_parent_line change
3265 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3266 $rangmax = $this->line_max($fk_parent_line);
3267 $this->line->rang = $rangmax + 1;
3268 }
3269
3270 $this->line->id = $rowid;
3271 $this->line->label = $label;
3272 $this->line->desc = $desc;
3273 $this->line->qty = $qty;
3274 $this->line->ref_ext = $ref_ext;
3275
3276 $this->line->vat_src_code = $vat_src_code;
3277 $this->line->tva_tx = $txtva;
3278 $this->line->localtax1_tx = $txlocaltax1;
3279 $this->line->localtax2_tx = $txlocaltax2;
3280 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3281 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3282 $this->line->remise_percent = $remise_percent;
3283 $this->line->subprice = (float) $pu_ht;
3284 $this->line->info_bits = $info_bits;
3285 $this->line->special_code = $special_code;
3286 $this->line->total_ht = (float) $total_ht;
3287 $this->line->total_tva = (float) $total_tva;
3288 $this->line->total_localtax1 = (float) $total_localtax1;
3289 $this->line->total_localtax2 = (float) $total_localtax2;
3290 $this->line->total_ttc = (float) $total_ttc;
3291 $this->line->date_start = $date_start;
3292 $this->line->date_end = $date_end;
3293 $this->line->product_type = $type;
3294 $this->line->fk_parent_line = $fk_parent_line;
3295 $this->line->skip_update_total = $skip_update_total;
3296 $this->line->fk_unit = $fk_unit;
3297
3298 $this->line->fk_fournprice = $fk_fournprice;
3299 $this->line->pa_ht = $pa_ht;
3300
3301 // Multicurrency
3302 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3303 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3304 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3305 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3306
3307 // TODO deprecated
3308 $this->line->price = $price;
3309
3310 if (is_array($array_options) && count($array_options) > 0) {
3311 // We replace values in this->line->array_options only for entries defined into $array_options
3312 foreach ($array_options as $key => $value) {
3313 $this->line->array_options[$key] = $array_options[$key];
3314 }
3315 }
3316
3317 $result = $this->line->update($user, $notrigger);
3318 if ($result > 0) {
3319 // Reorder if child line
3320 if (!empty($fk_parent_line)) {
3321 $this->line_order(true, 'DESC');
3322 }
3323
3324 // Mise a jour info denormalisees
3325 $this->update_price(1, 'auto');
3326
3327 $this->db->commit();
3328 return $result;
3329 } else {
3330 $this->error = $this->line->error;
3331
3332 $this->db->rollback();
3333 return -1;
3334 }
3335 } else {
3336 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3337 $this->errors = array('OrderStatusMakeOperationForbidden');
3338 return -2;
3339 }
3340 }
3341
3349 public function update(User $user, $notrigger = 0)
3350 {
3351 global $conf;
3352
3353 $error = 0;
3354
3355 // Clean parameters
3356 if (isset($this->ref)) {
3357 $this->ref = trim($this->ref);
3358 }
3359 if (isset($this->ref_client)) {
3360 $this->ref_client = trim($this->ref_client);
3361 }
3362 if (isset($this->ref_customer)) {
3363 $this->ref_customer = trim($this->ref_customer);
3364 }
3365 if (isset($this->note) || isset($this->note_private)) {
3366 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3367 }
3368 if (isset($this->note_public)) {
3369 $this->note_public = trim($this->note_public);
3370 }
3371 if (isset($this->model_pdf)) {
3372 $this->model_pdf = trim($this->model_pdf);
3373 }
3374 if (isset($this->import_key)) {
3375 $this->import_key = trim($this->import_key);
3376 }
3377 $delivery_date = $this->delivery_date;
3378
3379 // Check parameters
3380 // Put here code to add control on parameters values
3381
3382 // Update request
3383 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3384
3385 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3386 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3387 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3388 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3389 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3390 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3391 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3392 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3393 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3394 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3395 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3396 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3397 $sql .= " fk_user_modif=".(isset($user->id) ? $user->id : "null").",";
3398 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? $this->user_validation_id : "null").",";
3399 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3400 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3401 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3402 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3403 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3404 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3405 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3406 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3407 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3408 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3409 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3410 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3411
3412 $sql .= " WHERE rowid=".((int) $this->id);
3413
3414 $this->db->begin();
3415
3416 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3417 $resql = $this->db->query($sql);
3418 if (!$resql) {
3419 $error++;
3420 $this->errors[] = "Error ".$this->db->lasterror();
3421 }
3422
3423 if (!$error) {
3424 $result = $this->insertExtraFields();
3425 if ($result < 0) {
3426 $error++;
3427 }
3428 }
3429
3430 if (!$error && !$notrigger) {
3431 // Call trigger
3432 $result = $this->call_trigger('ORDER_MODIFY', $user);
3433 if ($result < 0) {
3434 $error++;
3435 }
3436 // End call triggers
3437 }
3438
3439 // Commit or rollback
3440 if ($error) {
3441 foreach ($this->errors as $errmsg) {
3442 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3443 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3444 }
3445 $this->db->rollback();
3446 return -1 * $error;
3447 } else {
3448 $this->db->commit();
3449 return 1;
3450 }
3451 }
3452
3460 public function delete($user, $notrigger = 0)
3461 {
3462 global $conf, $langs;
3463 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3464
3465 $error = 0;
3466
3467 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3468
3469 $this->db->begin();
3470
3471 if (!$notrigger) {
3472 // Call trigger
3473 $result = $this->call_trigger('ORDER_DELETE', $user);
3474 if ($result < 0) {
3475 $error++;
3476 }
3477 // End call triggers
3478 }
3479
3480 // Test we can delete
3481 if ($this->countNbOfShipments() != 0) {
3482 $this->errors[] = $langs->trans('SomeShipmentExists');
3483 $error++;
3484 }
3485
3486 // Delete extrafields of lines and lines
3487 if (!$error && !empty($this->table_element_line)) {
3488 $tabletodelete = $this->table_element_line;
3489 $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).")";
3490 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3491 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3492 $error++;
3493 $this->error = $this->db->lasterror();
3494 $this->errors[] = $this->error;
3495 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3496 }
3497 }
3498
3499 if (!$error) {
3500 // Delete linked object
3501 $res = $this->deleteObjectLinked();
3502 if ($res < 0) {
3503 $error++;
3504 }
3505 }
3506
3507 if (!$error) {
3508 // Delete linked contacts
3509 $res = $this->delete_linked_contact();
3510 if ($res < 0) {
3511 $error++;
3512 }
3513 }
3514
3515 // Removed extrafields of object
3516 if (!$error) {
3517 $result = $this->deleteExtraFields();
3518 if ($result < 0) {
3519 $error++;
3520 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3521 }
3522 }
3523
3524 // Delete main record
3525 if (!$error) {
3526 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3527 $res = $this->db->query($sql);
3528 if (!$res) {
3529 $error++;
3530 $this->error = $this->db->lasterror();
3531 $this->errors[] = $this->error;
3532 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3533 }
3534 }
3535
3536 // Delete record into ECM index and physically
3537 if (!$error) {
3538 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3539 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3540 if (!$res) {
3541 $error++;
3542 }
3543 }
3544
3545 if (!$error) {
3546 // We remove directory
3547 $ref = dol_sanitizeFileName($this->ref);
3548 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3549 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3550 $file = $dir."/".$ref.".pdf";
3551 if (file_exists($file)) {
3552 dol_delete_preview($this);
3553
3554 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3555 $this->error = 'ErrorFailToDeleteFile';
3556 $this->errors[] = $this->error;
3557 $this->db->rollback();
3558 return 0;
3559 }
3560 }
3561 if (file_exists($dir)) {
3562 $res = @dol_delete_dir_recursive($dir);
3563 if (!$res) {
3564 $this->error = 'ErrorFailToDeleteDir';
3565 $this->errors[] = $this->error;
3566 $this->db->rollback();
3567 return 0;
3568 }
3569 }
3570 }
3571 }
3572
3573 if (!$error) {
3574 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3575 $this->db->commit();
3576 return 1;
3577 } else {
3578 $this->db->rollback();
3579 return -1;
3580 }
3581 }
3582
3583
3584 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3592 public function load_board($user, $mode)
3593 {
3594 // phpcs:enable
3595 global $conf, $langs;
3596
3597 $clause = " WHERE";
3598
3599 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3600 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3601 if (!$user->hasRight('societe', 'client', 'voir')) {
3602 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3603 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3604 $clause = " AND";
3605 }
3606 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3607 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3608 if ($mode == 'toship') {
3609 // An order to ship is an open order (validated or in progress)
3610 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3611 }
3612 if ($mode == 'tobill') {
3613 // An order to bill is an order not already billed
3614 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3615 }
3616 if ($mode == 'shippedtobill') {
3617 // An order shipped and to bill is a delivered order not already billed
3618 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3619 }
3620 if ($user->socid) {
3621 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3622 }
3623
3624 $resql = $this->db->query($sql);
3625 if ($resql) {
3626 $delay_warning = 0;
3627 $label = $labelShort = $url = '';
3628 if ($mode == 'toship') {
3629 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3630 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3631 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3632 $labelShort = $langs->transnoentitiesnoconv("Opened");
3633 }
3634 if ($mode == 'tobill') {
3635 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3636 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3637 $labelShort = $langs->trans("ToBill");
3638 }
3639 if ($mode == 'shippedtobill') {
3640 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3641 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3642 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3643 }
3644
3645 $response = new WorkboardResponse();
3646
3647 $response->warning_delay = $delay_warning;
3648 $response->label = $label;
3649 $response->labelShort = $labelShort;
3650 $response->url = $url;
3651 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3652 $response->img = img_object('', "order");
3653
3654 $generic_commande = new Commande($this->db);
3655
3656 while ($obj = $this->db->fetch_object($resql)) {
3657 $response->nbtodo++;
3658 $response->total += $obj->total_ht;
3659
3660 $generic_commande->statut = $obj->fk_statut;
3661 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3662 $generic_commande->date = $this->db->jdate($obj->date_commande);
3663 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3664
3665 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3666 $response->nbtodolate++;
3667 }
3668 }
3669
3670 return $response;
3671 } else {
3672 $this->error = $this->db->error();
3673 return -1;
3674 }
3675 }
3676
3682 public function getLabelSource()
3683 {
3684 global $langs;
3685
3686 $label = $langs->trans('OrderSource'.$this->source);
3687
3688 if ($label == 'OrderSource') {
3689 return '';
3690 }
3691 return $label;
3692 }
3693
3700 public function getLibStatut($mode)
3701 {
3702 return $this->LibStatut($this->statut, $this->billed, $mode);
3703 }
3704
3705 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3715 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3716 {
3717 // phpcs:enable
3718 global $langs, $hookmanager;
3719
3720 $billedtext = '';
3721 if (empty($donotshowbilled)) {
3722 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3723 }
3724
3725 $labelTooltip = '';
3726
3727 if ($status == self::STATUS_CANCELED) {
3728 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3729 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3730 $statusType = 'status9';
3731 } elseif ($status == self::STATUS_DRAFT) {
3732 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3733 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3734 $statusType = 'status0';
3735 } elseif ($status == self::STATUS_VALIDATED) {
3736 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3737 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3738 $statusType = 'status1';
3739 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3740 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3741 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3742 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3743 if (!empty($this->delivery_date)) {
3744 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3745 }
3746 $statusType = 'status4';
3747 } elseif ($status == self::STATUS_CLOSED) {
3748 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtext;
3749 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3750 $statusType = 'status6';
3751 } else {
3752 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3753 $labelStatusShort = '';
3754 $statusType = '';
3755 $mode = 0;
3756 }
3757
3758 $parameters = array(
3759 'status' => $status,
3760 'mode' => $mode,
3761 'billed' => $billed,
3762 'donotshowbilled' => $donotshowbilled
3763 );
3764
3765 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3766
3767 if ($reshook > 0) {
3768 return $hookmanager->resPrint;
3769 }
3770
3771 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3772 }
3773
3780 public function getTooltipContentArray($params)
3781 {
3782 global $conf, $langs, $user;
3783
3784 $langs->load('orders');
3785 $datas = [];
3786 $nofetch = !empty($params['nofetch']);
3787
3788 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3789 return ['optimize' => $langs->trans("Order")];
3790 }
3791
3792 if ($user->hasRight('commande', 'lire')) {
3793 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').'<u>'.$langs->trans("Order").'</u>';
3794 if (isset($this->statut)) {
3795 $datas['status'] = ' '.$this->getLibStatut(5);
3796 }
3797 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3798 if (!$nofetch) {
3799 $langs->load('companies');
3800 if (empty($this->thirdparty)) {
3801 $this->fetch_thirdparty();
3802 }
3803 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3804 }
3805 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3806 if (!$nofetch) {
3807 $langs->load('project');
3808 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3809 $res = $this->fetchProject();
3810 if ($res > 0 && $this->project instanceof Project) {
3811 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3812 }
3813 }
3814 }
3815 if (!empty($this->total_ht)) {
3816 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3817 }
3818 if (!empty($this->total_tva)) {
3819 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3820 }
3821 if (!empty($this->total_ttc)) {
3822 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3823 }
3824 if (!empty($this->date)) {
3825 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3826 }
3827 if (!empty($this->delivery_date)) {
3828 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3829 }
3830 }
3831
3832 return $datas;
3833 }
3834
3848 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3849 {
3850 global $conf, $langs, $user, $hookmanager;
3851
3852 if (!empty($conf->dol_no_mouse_hover)) {
3853 $notooltip = 1; // Force disable tooltips
3854 }
3855
3856 $result = '';
3857
3858 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3859 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3860 } else {
3861 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3862 }
3863
3864 if (!$user->hasRight('commande', 'lire')) {
3865 $option = 'nolink';
3866 }
3867
3868 if ($option !== 'nolink') {
3869 // Add param to save lastsearch_values or not
3870 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3871 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3872 $add_save_lastsearch_values = 1;
3873 }
3874 if ($add_save_lastsearch_values) {
3875 $url .= '&save_lastsearch_values=1';
3876 }
3877 }
3878
3879 if ($short) {
3880 return $url;
3881 }
3882 $params = [
3883 'id' => $this->id,
3884 'objecttype' => $this->element,
3885 'option' => $option,
3886 'nofetch' => 1,
3887 ];
3888 $classfortooltip = 'classfortooltip';
3889 $dataparams = '';
3890 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3891 $classfortooltip = 'classforajaxtooltip';
3892 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3893 $label = '';
3894 } else {
3895 $label = implode($this->getTooltipContentArray($params));
3896 }
3897
3898 $linkclose = '';
3899 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3900 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3901 $label = $langs->trans("Order");
3902 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3903 }
3904 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3905 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3906
3907 $target_value = array('_self', '_blank', '_parent', '_top');
3908 if (in_array($target, $target_value)) {
3909 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3910 }
3911 }
3912
3913 $linkstart = '<a href="'.$url.'"';
3914 $linkstart .= $linkclose.'>';
3915 $linkend = '</a>';
3916
3917 if ($option === 'nolink') {
3918 $linkstart = '';
3919 $linkend = '';
3920 }
3921
3922 $result .= $linkstart;
3923 if ($withpicto) {
3924 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3925 }
3926 if ($withpicto != 2) {
3927 $result .= $this->ref;
3928 }
3929 $result .= $linkend;
3930
3931 if ($addlinktonotes) {
3932 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3933 if ($txttoshow) {
3934 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3935 $result .= ' <span class="note inline-block">';
3936 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3937 $result .= img_picto('', 'note');
3938 $result .= '</a>';
3939 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3940 //$result.='</a>';
3941 $result .= '</span>';
3942 }
3943 }
3944
3945 global $action;
3946 $hookmanager->initHooks(array($this->element . 'dao'));
3947 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3948 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3949 if ($reshook > 0) {
3950 $result = $hookmanager->resPrint;
3951 } else {
3952 $result .= $hookmanager->resPrint;
3953 }
3954 return $result;
3955 }
3956
3957
3964 public function info($id)
3965 {
3966 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3967 $sql .= ' date_valid as datev,';
3968 $sql .= ' date_cloture as datecloture,';
3969 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3970 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
3971 $sql .= ' WHERE c.rowid = '.((int) $id);
3972 $result = $this->db->query($sql);
3973 if ($result) {
3974 if ($this->db->num_rows($result)) {
3975 $obj = $this->db->fetch_object($result);
3976 $this->id = $obj->rowid;
3977 if ($obj->fk_user_author) {
3978 $this->user_creation_id = $obj->fk_user_author;
3979 }
3980 if ($obj->fk_user_valid) {
3981 $this->user_validation_id = $obj->fk_user_valid;
3982 }
3983 if ($obj->fk_user_cloture) {
3984 $this->user_closing_id = $obj->fk_user_cloture;
3985 }
3986
3987 $this->date_creation = $this->db->jdate($obj->datec);
3988 $this->date_modification = $this->db->jdate($obj->datem);
3989 $this->date_validation = $this->db->jdate($obj->datev);
3990 $this->date_cloture = $this->db->jdate($obj->datecloture);
3991 }
3992
3993 $this->db->free($result);
3994 } else {
3995 dol_print_error($this->db);
3996 }
3997 }
3998
3999
4007 public function initAsSpecimen()
4008 {
4009 global $conf, $langs;
4010
4011 dol_syslog(get_class($this)."::initAsSpecimen");
4012
4013 // Load array of products prodids
4014 $num_prods = 0;
4015 $prodids = array();
4016 $sql = "SELECT rowid";
4017 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4018 $sql .= " WHERE entity IN (".getEntity('product').")";
4019 $sql .= $this->db->plimit(100);
4020
4021 $resql = $this->db->query($sql);
4022 if ($resql) {
4023 $num_prods = $this->db->num_rows($resql);
4024 $i = 0;
4025 while ($i < $num_prods) {
4026 $i++;
4027 $row = $this->db->fetch_row($resql);
4028 $prodids[$i] = $row[0];
4029 }
4030 }
4031
4032 // Initialise parameters
4033 $this->id = 0;
4034 $this->ref = 'SPECIMEN';
4035 $this->specimen = 1;
4036 $this->entity = $conf->entity;
4037 $this->socid = 1;
4038 $this->date = time();
4039 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4040 $this->cond_reglement_code = 'RECEP';
4041 $this->mode_reglement_code = 'CHQ';
4042 $this->availability_code = 'DSP';
4043 $this->demand_reason_code = 'SRC_00';
4044
4045 $this->note_public = 'This is a comment (public)';
4046 $this->note_private = 'This is a comment (private)';
4047
4048 $this->multicurrency_tx = 1;
4049 $this->multicurrency_code = $conf->currency;
4050
4051 $this->status = $this::STATUS_DRAFT;
4052
4053 // Lines
4054 $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)
4055 $xnbp = 0;
4056
4057 while ($xnbp < $nbp) {
4058 $line = new OrderLine($this->db);
4059
4060 $line->desc = $langs->trans("Description")." ".$xnbp;
4061 $line->qty = 1;
4062 $line->subprice = 100;
4063 $line->price = 100;
4064 $line->tva_tx = 20;
4065 if ($xnbp == 2) {
4066 $line->total_ht = 50;
4067 $line->total_ttc = 60;
4068 $line->total_tva = 10;
4069 $line->remise_percent = 50;
4070 } else {
4071 $line->total_ht = 100;
4072 $line->total_ttc = 120;
4073 $line->total_tva = 20;
4074 $line->remise_percent = 0;
4075 }
4076 if ($num_prods > 0) {
4077 $prodid = mt_rand(1, $num_prods);
4078 $line->fk_product = $prodids[$prodid];
4079 $line->product_ref = 'SPECIMEN';
4080 }
4081
4082 $this->lines[$xnbp] = $line;
4083
4084 $this->total_ht += $line->total_ht;
4085 $this->total_tva += $line->total_tva;
4086 $this->total_ttc += $line->total_ttc;
4087
4088 $xnbp++;
4089 }
4090
4091 return 1;
4092 }
4093
4094
4100 public function loadStateBoard()
4101 {
4102 global $user;
4103
4104 $this->nb = array();
4105 $clause = "WHERE";
4106
4107 $sql = "SELECT count(co.rowid) as nb";
4108 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as co";
4109 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4110 if (!$user->hasRight('societe', 'client', 'voir')) {
4111 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4112 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4113 $clause = "AND";
4114 }
4115 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4116
4117 $resql = $this->db->query($sql);
4118 if ($resql) {
4119 while ($obj = $this->db->fetch_object($resql)) {
4120 $this->nb["orders"] = $obj->nb;
4121 }
4122 $this->db->free($resql);
4123 return 1;
4124 } else {
4125 dol_print_error($this->db);
4126 $this->error = $this->db->error();
4127 return -1;
4128 }
4129 }
4130
4136 public function getLinesArray()
4137 {
4138 return $this->fetch_lines();
4139 }
4140
4152 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4153 {
4154 global $conf, $langs;
4155
4156 $langs->load("orders");
4157 $outputlangs->load("products");
4158
4159 if (!dol_strlen($modele)) {
4160 $modele = 'einstein';
4161
4162 if (!empty($this->model_pdf)) {
4163 $modele = $this->model_pdf;
4164 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4165 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4166 }
4167 }
4168
4169 $modelpath = "core/modules/commande/doc/";
4170
4171 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4172 }
4173
4174
4183 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4184 {
4185 $tables = array(
4186 'commande'
4187 );
4188
4189 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4190 }
4191
4200 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4201 {
4202 $tables = array(
4203 'commandedet',
4204 );
4205
4206 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4207 }
4208
4214 public function hasDelay()
4215 {
4216 global $conf;
4217
4218 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4219 return false; // Never late if not inside this status range
4220 }
4221
4222 $now = dol_now();
4223
4224 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4225 }
4226
4232 public function showDelay()
4233 {
4234 global $conf, $langs;
4235
4236 if (empty($this->delivery_date)) {
4237 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4238 } else {
4239 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4240 }
4241 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4242
4243 return $text;
4244 }
4245
4255 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4256 {
4257 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4258 }
4259}
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_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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)
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 '.
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_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return 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