dolibarr 21.0.0-alpha
commande.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2011 Jean Heimburger <jean@tiaris.info>
8 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
9 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
10 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
11 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
12 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
13 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2021-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
17 * 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
951 dol_syslog(get_class($this)."::create user=".$user->id);
952
953 // Check parameters
954 if (!empty($this->ref)) { // We check that ref is not already used
955 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
956 if ($result > 0) {
957 $this->error = 'ErrorRefAlreadyExists';
958 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
959 $this->db->rollback();
960 return -1;
961 }
962 }
963
964 $soc = new Societe($this->db);
965 $result = $soc->fetch($this->socid);
966 if ($result < 0) {
967 $this->error = "Failed to fetch company";
968 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
969 return -2;
970 }
971 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
972 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
973 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
974 return -1;
975 }
976
977 $now = dol_now();
978
979 $this->db->begin();
980
981 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
982 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
983 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
984 $sql .= ", fk_shipping_method";
985 $sql .= ", fk_warehouse";
986 $sql .= ", fk_incoterms, location_incoterms";
987 $sql .= ", entity, module_source, pos_source";
988 $sql .= ", fk_multicurrency";
989 $sql .= ", multicurrency_code";
990 $sql .= ", multicurrency_tx";
991 $sql .= ")";
992 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
993 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
994 $sql .= ", '".$this->db->idate($date)."'";
995 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
996 $sql .= ", '".$this->db->escape($this->note_private)."'";
997 $sql .= ", '".$this->db->escape($this->note_public)."'";
998 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
999 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1000 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1001 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1002 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1003 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1004 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1005 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1006 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1007 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1008 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1009 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1010 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1011 $sql .= ", ".(int) $this->fk_incoterms;
1012 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1013 $sql .= ", ".setEntity($this);
1014 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1015 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1016 $sql .= ", ".(int) $this->fk_multicurrency;
1017 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1018 $sql .= ", ".(float) $this->multicurrency_tx;
1019 $sql .= ")";
1020
1021 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1022 $resql = $this->db->query($sql);
1023 if ($resql) {
1024 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1025
1026 if ($this->id) {
1027 $fk_parent_line = 0;
1028 $num = count($this->lines);
1029
1030 /*
1031 * Insert products details into db
1032 */
1033 for ($i = 0; $i < $num; $i++) {
1034 $line = $this->lines[$i];
1035
1036 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1037 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1038 if (!is_object($line)) {
1039 $line = (object) $line;
1040 }
1041
1042 // Reset fk_parent_line for no child products and special product
1043 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1044 $fk_parent_line = 0;
1045 }
1046
1047 // Complete vat rate with code
1048 $vatrate = $line->tva_tx;
1049 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1050 $vatrate .= ' ('.$line->vat_src_code.')';
1051 }
1052
1053 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1054 $originid = $line->origin_id;
1055 $origintype = $line->origin;
1056 } else {
1057 $originid = $line->id;
1058 $origintype = $this->element;
1059 }
1060
1061 // ref_ext
1062 if (empty($line->ref_ext)) {
1063 $line->ref_ext = '';
1064 }
1065
1066 $result = $this->addline(
1067 $line->desc,
1068 $line->subprice,
1069 $line->qty,
1070 $vatrate,
1071 $line->localtax1_tx,
1072 $line->localtax2_tx,
1073 $line->fk_product,
1074 $line->remise_percent,
1075 $line->info_bits,
1076 $line->fk_remise_except,
1077 'HT',
1078 0,
1079 $line->date_start,
1080 $line->date_end,
1081 $line->product_type,
1082 $line->rang,
1083 $line->special_code,
1084 $fk_parent_line,
1085 $line->fk_fournprice,
1086 $line->pa_ht,
1087 $line->label,
1088 $line->array_options,
1089 $line->fk_unit,
1090 $origintype,
1091 $originid,
1092 0,
1093 $line->ref_ext,
1094 1
1095 );
1096 if ($result < 0) {
1097 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1098 $this->error = $this->db->lasterror();
1099 $this->errors[] = $this->error;
1100 dol_print_error($this->db);
1101 }
1102 $this->db->rollback();
1103 return -1;
1104 }
1105 // Defined the new fk_parent_line
1106 if ($result > 0 && $line->product_type == 9) {
1107 $fk_parent_line = $result;
1108 }
1109 }
1110
1111 $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.
1112
1113 // update ref
1114 $initialref = '(PROV'.$this->id.')';
1115 if (!empty($this->ref)) {
1116 $initialref = $this->ref;
1117 }
1118
1119 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1120 if ($this->db->query($sql)) {
1121 $this->ref = $initialref;
1122
1123 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1124 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1125 }
1126
1127 // Add object linked
1128 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1129 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1130 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, ...))
1131 foreach ($tmp_origin_id as $origin_id) {
1132 $ret = $this->add_object_linked($origin, $origin_id);
1133 if (!$ret) {
1134 $this->error = $this->db->lasterror();
1135 $error++;
1136 }
1137 }
1138 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1139 $origin_id = $tmp_origin_id;
1140 $ret = $this->add_object_linked($origin, $origin_id);
1141 if (!$ret) {
1142 $this->error = $this->db->lasterror();
1143 $error++;
1144 }
1145 }
1146 }
1147 }
1148
1149 if (!$error && $this->id && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1150 $originforcontact = $this->origin;
1151 $originidforcontact = $this->origin_id;
1152 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1153 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1154 $exp = new Expedition($this->db);
1155 $exp->fetch($this->origin_id);
1156 $exp->fetchObjectLinked();
1157 if (count($exp->linkedObjectsIds['commande']) > 0) {
1158 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1159 $originforcontact = 'commande';
1160 if (is_object($value)) {
1161 $originidforcontact = $value->id;
1162 } else {
1163 $originidforcontact = $value;
1164 }
1165 break; // We take first one
1166 }
1167 }
1168 }
1169
1170 $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";
1171 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1172
1173 $resqlcontact = $this->db->query($sqlcontact);
1174 if ($resqlcontact) {
1175 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1176 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1177 $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
1178 }
1179 } else {
1180 dol_print_error($this->db);
1181 }
1182 }
1183
1184 if (!$error) {
1185 $result = $this->insertExtraFields();
1186 if ($result < 0) {
1187 $error++;
1188 }
1189 }
1190
1191 if (!$error && !$notrigger) {
1192 // Call trigger
1193 $result = $this->call_trigger('ORDER_CREATE', $user);
1194 if ($result < 0) {
1195 $error++;
1196 }
1197 // End call triggers
1198 }
1199
1200 if (!$error) {
1201 $this->db->commit();
1202 return $this->id;
1203 } else {
1204 $this->db->rollback();
1205 return -1 * $error;
1206 }
1207 } else {
1208 $this->error = $this->db->lasterror();
1209 $this->db->rollback();
1210 return -1;
1211 }
1212 }
1213
1214 return 0;
1215 } else {
1216 $this->error = $this->db->lasterror();
1217 $this->db->rollback();
1218 return -1;
1219 }
1220 }
1221
1222
1230 public function createFromClone(User $user, $socid = 0)
1231 {
1232 global $conf, $user, $hookmanager;
1233
1234 $error = 0;
1235
1236 $this->db->begin();
1237
1238 // get lines so they will be clone
1239 foreach ($this->lines as $line) {
1240 $line->fetch_optionals();
1241 }
1242
1243 // Load source object
1244 $objFrom = clone $this;
1245
1246 // Change socid if needed
1247 if (!empty($socid) && $socid != $this->socid) {
1248 $objsoc = new Societe($this->db);
1249
1250 if ($objsoc->fetch($socid) > 0) {
1251 $this->socid = $objsoc->id;
1252 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1253 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1254 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1255 $this->fk_project = 0;
1256 $this->fk_delivery_address = 0;
1257 }
1258
1259 // TODO Change product price if multi-prices
1260 }
1261
1262 $this->id = 0;
1263 $this->ref = '';
1264 $this->statut = self::STATUS_DRAFT;
1265
1266 // Clear fields
1267 $this->user_author_id = $user->id;
1268 $this->user_validation_id = 0;
1269 $this->date = dol_now();
1270 $this->date_commande = dol_now();
1271 $this->date_creation = '';
1272 $this->date_validation = '';
1273 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1274 $this->ref_client = '';
1275 $this->ref_customer = '';
1276 }
1277
1278 // Do not clone ref_ext
1279 $num = count($this->lines);
1280 for ($i = 0; $i < $num; $i++) {
1281 $this->lines[$i]->ref_ext = '';
1282 }
1283
1284 // Create clone
1285 $this->context['createfromclone'] = 'createfromclone';
1286 $result = $this->create($user);
1287 if ($result < 0) {
1288 $error++;
1289 }
1290
1291 if (!$error) {
1292 // copy internal contacts
1293 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1294 $error++;
1295 }
1296 }
1297
1298 if (!$error) {
1299 // copy external contacts if same company
1300 if ($this->socid == $objFrom->socid) {
1301 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1302 $error++;
1303 }
1304 }
1305 }
1306
1307 if (!$error) {
1308 // Hook of thirdparty module
1309 if (is_object($hookmanager)) {
1310 $parameters = array('objFrom' => $objFrom);
1311 $action = '';
1312 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1313 if ($reshook < 0) {
1314 $this->setErrorsFromObject($hookmanager);
1315 $error++;
1316 }
1317 }
1318 }
1319
1320 unset($this->context['createfromclone']);
1321
1322 // End
1323 if (!$error) {
1324 $this->db->commit();
1325 return $this->id;
1326 } else {
1327 $this->db->rollback();
1328 return -1;
1329 }
1330 }
1331
1332
1340 public function createFromProposal($object, User $user)
1341 {
1342 global $conf, $hookmanager;
1343
1344 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1345 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1346
1347 $error = 0;
1348
1349 $this->date_commande = dol_now();
1350 $this->date = dol_now();
1351 $this->source = 0;
1352
1353 $num = count($object->lines);
1354 for ($i = 0; $i < $num; $i++) {
1355 $line = new OrderLine($this->db);
1356
1357 $line->libelle = $object->lines[$i]->libelle;
1358 $line->label = $object->lines[$i]->label;
1359 $line->desc = $object->lines[$i]->desc;
1360 $line->price = $object->lines[$i]->price;
1361 $line->subprice = $object->lines[$i]->subprice;
1362 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1363 $line->tva_tx = $object->lines[$i]->tva_tx;
1364 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1365 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1366 $line->qty = $object->lines[$i]->qty;
1367 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1368 $line->remise_percent = $object->lines[$i]->remise_percent;
1369 $line->fk_product = $object->lines[$i]->fk_product;
1370 $line->info_bits = $object->lines[$i]->info_bits;
1371 $line->product_type = $object->lines[$i]->product_type;
1372 $line->rang = $object->lines[$i]->rang;
1373 $line->special_code = $object->lines[$i]->special_code;
1374 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1375 $line->fk_unit = $object->lines[$i]->fk_unit;
1376
1377 $line->date_start = $object->lines[$i]->date_start;
1378 $line->date_end = $object->lines[$i]->date_end;
1379
1380 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1381 $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);
1382 $line->pa_ht = $marginInfos[0];
1383 $line->marge_tx = $marginInfos[1];
1384 $line->marque_tx = $marginInfos[2];
1385
1386 $line->origin = $object->element;
1387 $line->origin_id = $object->lines[$i]->id;
1388
1389 // get extrafields from original line
1390 $object->lines[$i]->fetch_optionals();
1391 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1392 $line->array_options[$options_key] = $value;
1393 }
1394
1395 $this->lines[$i] = $line;
1396 }
1397
1398 $this->entity = $object->entity;
1399 $this->socid = $object->socid;
1400 $this->fk_project = $object->fk_project;
1401 $this->cond_reglement_id = $object->cond_reglement_id;
1402 $this->deposit_percent = $object->deposit_percent;
1403 $this->mode_reglement_id = $object->mode_reglement_id;
1404 $this->fk_account = $object->fk_account;
1405 $this->availability_id = $object->availability_id;
1406 $this->demand_reason_id = $object->demand_reason_id;
1407 $this->delivery_date = $object->delivery_date;
1408 $this->shipping_method_id = $object->shipping_method_id;
1409 $this->warehouse_id = $object->warehouse_id;
1410 $this->fk_delivery_address = $object->fk_delivery_address;
1411 $this->contact_id = $object->contact_id;
1412 $this->ref_client = $object->ref_client;
1413 $this->ref_customer = $object->ref_client;
1414
1415 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1416 $this->note_private = $object->note_private;
1417 $this->note_public = $object->note_public;
1418 }
1419
1420 $this->origin = $object->element;
1421 $this->origin_id = $object->id;
1422
1423 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1424 if (isModEnabled('multicurrency')) {
1425 if (!empty($object->multicurrency_code)) {
1426 $this->multicurrency_code = $object->multicurrency_code;
1427 }
1428 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1429 $this->multicurrency_tx = $object->multicurrency_tx;
1430 }
1431
1432 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1433 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1434 $this->fk_multicurrency = $tmparray[0];
1435 $this->multicurrency_tx = $tmparray[1];
1436 } else {
1437 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1438 }
1439 if (empty($this->fk_multicurrency)) {
1440 $this->multicurrency_code = $conf->currency;
1441 $this->fk_multicurrency = 0;
1442 $this->multicurrency_tx = 1;
1443 }
1444 }
1445
1446 // get extrafields from original line
1447 $object->fetch_optionals();
1448
1449 $e = new ExtraFields($this->db);
1450 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1451
1452 foreach ($object->array_options as $options_key => $value) {
1453 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1454 $this->array_options[$options_key] = $value;
1455 }
1456 }
1457 // Possibility to add external linked objects with hooks
1458 $this->linked_objects[$this->origin] = $this->origin_id;
1459 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1460 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1461 }
1462
1463 $ret = $this->create($user);
1464
1465 if ($ret > 0) {
1466 // Actions hooked (by external module)
1467 $hookmanager->initHooks(array('orderdao'));
1468
1469 $parameters = array('objFrom' => $object);
1470 $action = '';
1471 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1472 if ($reshook < 0) {
1473 $this->setErrorsFromObject($hookmanager);
1474 $error++;
1475 }
1476
1477 if (!$error) {
1478 // Validate immediately the order
1479 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1480 $this->fetch($ret);
1481 $this->valid($user);
1482 }
1483 return $ret;
1484 } else {
1485 return -1;
1486 }
1487 } else {
1488 return -1;
1489 }
1490 }
1491
1492
1533 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)
1534 {
1535 global $mysoc, $conf, $langs, $user;
1536
1537 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1538 $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";
1539 $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";
1540 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1541
1542 if ($this->statut == self::STATUS_DRAFT) {
1543 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1544
1545 // Clean parameters
1546
1547 if (empty($remise_percent)) {
1548 $remise_percent = 0;
1549 }
1550 if (empty($qty)) {
1551 $qty = 0;
1552 }
1553 if (empty($info_bits)) {
1554 $info_bits = 0;
1555 }
1556 if (empty($rang)) {
1557 $rang = 0;
1558 }
1559 if (empty($txtva)) {
1560 $txtva = 0;
1561 }
1562 if (empty($txlocaltax1)) {
1563 $txlocaltax1 = 0;
1564 }
1565 if (empty($txlocaltax2)) {
1566 $txlocaltax2 = 0;
1567 }
1568 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1569 $fk_parent_line = 0;
1570 }
1571 if (empty($this->fk_multicurrency)) {
1572 $this->fk_multicurrency = 0;
1573 }
1574 if (empty($ref_ext)) {
1575 $ref_ext = '';
1576 }
1577
1578 $remise_percent = (float) price2num($remise_percent);
1579 $qty = (float) price2num($qty);
1580 $pu_ht = price2num($pu_ht);
1581 $pu_ht_devise = price2num($pu_ht_devise);
1582 $pu_ttc = price2num($pu_ttc);
1583 $pa_ht = (float) price2num($pa_ht);
1584 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1585 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1586 }
1587 $txlocaltax1 = price2num($txlocaltax1);
1588 $txlocaltax2 = price2num($txlocaltax2);
1589 if ($price_base_type == 'HT') {
1590 $pu = $pu_ht;
1591 } else {
1592 $pu = $pu_ttc;
1593 }
1594 $label = trim($label);
1595 $desc = trim($desc);
1596
1597 // Check parameters
1598 if ($type < 0) {
1599 return -1;
1600 }
1601
1602 if ($date_start && $date_end && $date_start > $date_end) {
1603 $langs->load("errors");
1604 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1605 return -1;
1606 }
1607
1608 $this->db->begin();
1609
1610 $product_type = $type;
1611 if (!empty($fk_product) && $fk_product > 0) {
1612 $product = new Product($this->db);
1613 $result = $product->fetch($fk_product);
1614 $product_type = $product->type;
1615
1616 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
1617 $langs->load("errors");
1618 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1619 $this->errors[] = $this->error;
1620 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1621 $this->db->rollback();
1623 }
1624 }
1625 // Calcul du total TTC et de la TVA pour la ligne a partir de
1626 // qty, pu, remise_percent et txtva
1627 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1628 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1629
1630 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1631
1632 // Clean vat code
1633 $reg = array();
1634 $vat_src_code = '';
1635 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1636 $vat_src_code = $reg[1];
1637 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1638 }
1639
1640 $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);
1641
1642 /*var_dump($txlocaltax1);
1643 var_dump($txlocaltax2);
1644 var_dump($localtaxes_type);
1645 var_dump($tabprice);
1646 var_dump($tabprice[9]);
1647 var_dump($tabprice[10]);
1648 exit;*/
1649
1650 $total_ht = $tabprice[0];
1651 $total_tva = $tabprice[1];
1652 $total_ttc = $tabprice[2];
1653 $total_localtax1 = $tabprice[9];
1654 $total_localtax2 = $tabprice[10];
1655 $pu_ht = $tabprice[3];
1656
1657 // MultiCurrency
1658 $multicurrency_total_ht = $tabprice[16];
1659 $multicurrency_total_tva = $tabprice[17];
1660 $multicurrency_total_ttc = $tabprice[18];
1661 $pu_ht_devise = $tabprice[19];
1662
1663 // Rang to use
1664 $ranktouse = $rang;
1665 if ($ranktouse == -1) {
1666 $rangmax = $this->line_max($fk_parent_line);
1667 $ranktouse = $rangmax + 1;
1668 }
1669
1670 // TODO A virer
1671 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1672 $price = $pu;
1673 $remise = 0;
1674 if ($remise_percent > 0) {
1675 $remise = round(((float) $pu * $remise_percent / 100), 2);
1676 $price = (float) $pu - $remise;
1677 }
1678
1679 // Insert line
1680 $this->line = new OrderLine($this->db);
1681
1682 $this->line->context = $this->context;
1683
1684 $this->line->fk_commande = $this->id;
1685 $this->line->label = $label;
1686 $this->line->desc = $desc;
1687 $this->line->qty = $qty;
1688 $this->line->ref_ext = $ref_ext;
1689
1690 $this->line->vat_src_code = $vat_src_code;
1691 $this->line->tva_tx = $txtva;
1692 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1693 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1694 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1695 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1696 $this->line->fk_product = $fk_product;
1697 $this->line->product_type = $product_type;
1698 $this->line->fk_remise_except = $fk_remise_except;
1699 $this->line->remise_percent = $remise_percent;
1700 $this->line->subprice = $pu_ht;
1701 $this->line->rang = $ranktouse;
1702 $this->line->info_bits = $info_bits;
1703 $this->line->total_ht = $total_ht;
1704 $this->line->total_tva = $total_tva;
1705 $this->line->total_localtax1 = $total_localtax1;
1706 $this->line->total_localtax2 = $total_localtax2;
1707 $this->line->total_ttc = $total_ttc;
1708 $this->line->special_code = $special_code;
1709 $this->line->origin = $origin;
1710 $this->line->origin_id = $origin_id;
1711 $this->line->fk_parent_line = $fk_parent_line;
1712 $this->line->fk_unit = $fk_unit;
1713
1714 $this->line->date_start = $date_start;
1715 $this->line->date_end = $date_end;
1716
1717 $this->line->fk_fournprice = $fk_fournprice;
1718 $this->line->pa_ht = $pa_ht;
1719
1720 // Multicurrency
1721 $this->line->fk_multicurrency = $this->fk_multicurrency;
1722 $this->line->multicurrency_code = $this->multicurrency_code;
1723 $this->line->multicurrency_subprice = $pu_ht_devise;
1724 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1725 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1726 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1727
1728 // TODO Ne plus utiliser
1729 $this->line->price = $price;
1730
1731 if (is_array($array_options) && count($array_options) > 0) {
1732 $this->line->array_options = $array_options;
1733 }
1734
1735 $result = $this->line->insert($user);
1736 if ($result > 0) {
1737 // Reorder if child line
1738 if (!empty($fk_parent_line)) {
1739 $this->line_order(true, 'DESC');
1740 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1741 $linecount = count($this->lines);
1742 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1743 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1744 }
1745 }
1746
1747 // Mise a jour information denormalisees au niveau de la commande meme
1748 if (empty($noupdateafterinsertline)) {
1749 $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.
1750 }
1751
1752 if ($result > 0) {
1753 $this->db->commit();
1754 $this->lines[] = $this->line;
1755 return $this->line->id;
1756 } else {
1757 $this->db->rollback();
1758 return -1;
1759 }
1760 } else {
1761 $this->error = $this->line->error;
1762 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1763 $this->db->rollback();
1764 return -2;
1765 }
1766 } else {
1767 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1768 return -3;
1769 }
1770 }
1771
1772
1773 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1787 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1788 {
1789 // phpcs:enable
1790 global $conf, $mysoc;
1791
1792 if (!$qty) {
1793 $qty = 1;
1794 }
1795
1796 if ($idproduct > 0) {
1797 $prod = new Product($this->db);
1798 $prod->fetch($idproduct);
1799
1800 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1801 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1802 if (empty($tva_tx)) {
1803 $tva_npr = 0;
1804 }
1805 $vat_src_code = ''; // May be defined into tva_tx
1806
1807 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1808 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1809
1810 // multiprix
1811 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1812 $price = $prod->multiprices[$this->thirdparty->price_level];
1813 } else {
1814 $price = $prod->price;
1815 }
1816
1817 $line = new OrderLine($this->db);
1818
1819 $line->context = $this->context;
1820
1821 $line->fk_product = $idproduct;
1822 $line->desc = $prod->description;
1823 $line->qty = $qty;
1824 $line->subprice = $price;
1825 $line->remise_percent = $remise_percent;
1826 $line->vat_src_code = $vat_src_code;
1827 $line->tva_tx = $tva_tx;
1828 $line->localtax1_tx = $localtax1_tx;
1829 $line->localtax2_tx = $localtax2_tx;
1830
1831 $line->product_ref = $prod->ref;
1832 $line->product_label = $prod->label;
1833 $line->product_desc = $prod->description;
1834 $line->fk_unit = $prod->fk_unit;
1835
1836 // Save the start and end date of the line in the object
1837 if ($date_start) {
1838 $line->date_start = $date_start;
1839 }
1840 if ($date_end) {
1841 $line->date_end = $date_end;
1842 }
1843
1844 $this->lines[] = $line;
1845
1864 }
1865 }
1866
1867
1877 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1878 {
1879 // Check parameters
1880 if (empty($id) && empty($ref) && empty($ref_ext)) {
1881 return -1;
1882 }
1883
1884 $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';
1885 $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';
1886 $sql .= ', c.fk_account';
1887 $sql .= ', c.date_commande, c.date_valid, c.tms';
1888 $sql .= ', c.date_livraison as delivery_date';
1889 $sql .= ', c.fk_shipping_method';
1890 $sql .= ', c.fk_warehouse';
1891 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1892 $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';
1893 $sql .= ', c.fk_incoterms, c.location_incoterms';
1894 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1895 $sql .= ", c.module_source, c.pos_source";
1896 $sql .= ", i.libelle as label_incoterms";
1897 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1898 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1899 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1900 $sql .= ', dr.code as demand_reason_code';
1901 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
1902 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1903 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1904 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1905 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1906 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1907
1908 if ($id) {
1909 $sql .= " WHERE c.rowid=".((int) $id);
1910 } else {
1911 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
1912 }
1913
1914 if ($ref) {
1915 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1916 }
1917 if ($ref_ext) {
1918 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1919 }
1920
1921 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1922 $result = $this->db->query($sql);
1923 if ($result) {
1924 $obj = $this->db->fetch_object($result);
1925 if ($obj) {
1926 $this->id = $obj->rowid;
1927 $this->entity = $obj->entity;
1928
1929 $this->ref = $obj->ref;
1930 $this->ref_client = $obj->ref_client;
1931 $this->ref_customer = $obj->ref_client;
1932 $this->ref_ext = $obj->ref_ext;
1933
1934 $this->socid = $obj->fk_soc;
1935 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1936
1937 $this->fk_project = $obj->fk_project;
1938 $this->project = null; // Clear if another value was already set by fetch_projet
1939
1940 $this->statut = $obj->fk_statut;
1941 $this->status = $obj->fk_statut;
1942
1943 $this->user_author_id = $obj->fk_user_author;
1944 $this->user_creation_id = $obj->fk_user_author;
1945 $this->user_validation_id = $obj->fk_user_valid;
1946 $this->user_modification_id = $obj->fk_user_modif;
1947 $this->total_ht = $obj->total_ht;
1948 $this->total_tva = $obj->total_tva;
1949 $this->total_localtax1 = $obj->total_localtax1;
1950 $this->total_localtax2 = $obj->total_localtax2;
1951 $this->total_ttc = $obj->total_ttc;
1952 $this->date = $this->db->jdate($obj->date_commande);
1953 $this->date_commande = $this->db->jdate($obj->date_commande);
1954 $this->date_creation = $this->db->jdate($obj->date_creation);
1955 $this->date_validation = $this->db->jdate($obj->date_valid);
1956 $this->date_modification = $this->db->jdate($obj->tms);
1957 $this->source = $obj->source;
1958 $this->billed = $obj->billed;
1959 $this->note = $obj->note_private; // deprecated
1960 $this->note_private = $obj->note_private;
1961 $this->note_public = $obj->note_public;
1962 $this->model_pdf = $obj->model_pdf;
1963 $this->last_main_doc = $obj->last_main_doc;
1964 $this->mode_reglement_id = $obj->fk_mode_reglement;
1965 $this->mode_reglement_code = $obj->mode_reglement_code;
1966 $this->mode_reglement = $obj->mode_reglement_libelle;
1967 $this->cond_reglement_id = $obj->fk_cond_reglement;
1968 $this->cond_reglement_code = $obj->cond_reglement_code;
1969 $this->cond_reglement = $obj->cond_reglement_libelle;
1970 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1971 $this->deposit_percent = $obj->deposit_percent;
1972 $this->fk_account = $obj->fk_account;
1973 $this->availability_id = $obj->fk_availability;
1974 $this->availability_code = $obj->availability_code;
1975 $this->availability = $obj->availability_label;
1976 $this->demand_reason_id = $obj->fk_input_reason;
1977 $this->demand_reason_code = $obj->demand_reason_code;
1978 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1979 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1980 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1981 $this->fk_delivery_address = $obj->fk_delivery_address;
1982 $this->module_source = $obj->module_source;
1983 $this->pos_source = $obj->pos_source;
1984
1985 //Incoterms
1986 $this->fk_incoterms = $obj->fk_incoterms;
1987 $this->location_incoterms = $obj->location_incoterms;
1988 $this->label_incoterms = $obj->label_incoterms;
1989
1990 // Multicurrency
1991 $this->fk_multicurrency = $obj->fk_multicurrency;
1992 $this->multicurrency_code = $obj->multicurrency_code;
1993 $this->multicurrency_tx = $obj->multicurrency_tx;
1994 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1995 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1996 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1997
1998 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1999
2000 $this->lines = array();
2001
2002 // Retrieve all extrafield
2003 // fetch optionals attributes and labels
2004 $this->fetch_optionals();
2005
2006 $this->db->free($result);
2007
2008 // Lines
2009 $result = $this->fetch_lines();
2010 if ($result < 0) {
2011 return -3;
2012 }
2013 return 1;
2014 } else {
2015 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2016 return 0;
2017 }
2018 } else {
2019 $this->error = $this->db->error();
2020 return -1;
2021 }
2022 }
2023
2024
2025 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2032 public function insert_discount($idremise)
2033 {
2034 // phpcs:enable
2035 global $langs;
2036
2037 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2038 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2039
2040 $this->db->begin();
2041
2042 $remise = new DiscountAbsolute($this->db);
2043 $result = $remise->fetch($idremise);
2044
2045 if ($result > 0) {
2046 if ($remise->fk_facture) { // Protection against multiple submission
2047 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2048 $this->db->rollback();
2049 return -5;
2050 }
2051
2052 $line = new OrderLine($this->db);
2053
2054 $line->fk_commande = $this->id;
2055 $line->fk_remise_except = $remise->id;
2056 $line->desc = $remise->description; // Description ligne
2057 $line->vat_src_code = $remise->vat_src_code;
2058 $line->tva_tx = $remise->tva_tx;
2059 $line->subprice = -(float) $remise->amount_ht;
2060 $line->price = -(float) $remise->amount_ht;
2061 $line->fk_product = 0; // Id produit predefini
2062 $line->qty = 1;
2063 $line->remise_percent = 0;
2064 $line->rang = -1;
2065 $line->info_bits = 2;
2066
2067 $line->total_ht = -(float) $remise->amount_ht;
2068 $line->total_tva = -(float) $remise->amount_tva;
2069 $line->total_ttc = -(float) $remise->amount_ttc;
2070
2071 $result = $line->insert();
2072 if ($result > 0) {
2073 $result = $this->update_price(1);
2074 if ($result > 0) {
2075 $this->db->commit();
2076 return 1;
2077 } else {
2078 $this->db->rollback();
2079 return -1;
2080 }
2081 } else {
2082 $this->error = $line->error;
2083 $this->errors = $line->errors;
2084 $this->db->rollback();
2085 return -2;
2086 }
2087 } else {
2088 $this->db->rollback();
2089 return -2;
2090 }
2091 }
2092
2093
2094 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2102 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2103 {
2104 // phpcs:enable
2105 global $langs, $conf;
2106
2107 $this->lines = array();
2108
2109 $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,';
2110 $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,';
2111 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2112 $sql .= ' l.fk_unit,';
2113 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2114 $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,';
2115 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2116 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2117 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2118 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2119 if ($only_product) {
2120 $sql .= ' AND p.fk_product_type = 0';
2121 }
2122 $sql .= ' ORDER BY l.rang, l.rowid';
2123
2124 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2125 $result = $this->db->query($sql);
2126 if ($result) {
2127 $num = $this->db->num_rows($result);
2128
2129 $i = 0;
2130 while ($i < $num) {
2131 $objp = $this->db->fetch_object($result);
2132
2133 $line = new OrderLine($this->db);
2134
2135 $line->rowid = $objp->rowid;
2136 $line->id = $objp->rowid;
2137 $line->fk_commande = $objp->fk_commande;
2138 $line->commande_id = $objp->fk_commande;
2139 $line->label = $objp->custom_label;
2140 $line->desc = $objp->description;
2141 $line->description = $objp->description; // Description line
2142 $line->product_type = $objp->product_type;
2143 $line->qty = $objp->qty;
2144 $line->ref_ext = $objp->ref_ext;
2145
2146 $line->vat_src_code = $objp->vat_src_code;
2147 $line->tva_tx = $objp->tva_tx;
2148 $line->localtax1_tx = $objp->localtax1_tx;
2149 $line->localtax2_tx = $objp->localtax2_tx;
2150 $line->localtax1_type = $objp->localtax1_type;
2151 $line->localtax2_type = $objp->localtax2_type;
2152 $line->total_ht = $objp->total_ht;
2153 $line->total_ttc = $objp->total_ttc;
2154 $line->total_tva = $objp->total_tva;
2155 $line->total_localtax1 = $objp->total_localtax1;
2156 $line->total_localtax2 = $objp->total_localtax2;
2157 $line->subprice = $objp->subprice;
2158 $line->fk_remise_except = $objp->fk_remise_except;
2159 $line->remise_percent = $objp->remise_percent;
2160 $line->price = $objp->price;
2161 $line->fk_product = $objp->fk_product;
2162 $line->fk_fournprice = $objp->fk_fournprice;
2163 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2164 $line->pa_ht = $marginInfos[0];
2165 $line->marge_tx = $marginInfos[1];
2166 $line->marque_tx = $marginInfos[2];
2167 $line->rang = $objp->rang;
2168 $line->info_bits = $objp->info_bits;
2169 $line->special_code = $objp->special_code;
2170 $line->fk_parent_line = $objp->fk_parent_line;
2171
2172 $line->ref = $objp->product_ref;
2173 $line->libelle = $objp->product_label;
2174
2175 $line->product_ref = $objp->product_ref;
2176 $line->product_label = $objp->product_label;
2177 $line->product_tosell = $objp->product_tosell;
2178 $line->product_tobuy = $objp->product_tobuy;
2179 $line->product_desc = $objp->product_desc;
2180 $line->product_tobatch = $objp->product_tobatch;
2181 $line->product_barcode = $objp->product_barcode;
2182
2183 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2184 $line->fk_unit = $objp->fk_unit;
2185
2186 $line->weight = $objp->weight;
2187 $line->weight_units = $objp->weight_units;
2188 $line->volume = $objp->volume;
2189 $line->volume_units = $objp->volume_units;
2190
2191 $line->date_start = $this->db->jdate($objp->date_start);
2192 $line->date_end = $this->db->jdate($objp->date_end);
2193
2194 // Multicurrency
2195 $line->fk_multicurrency = $objp->fk_multicurrency;
2196 $line->multicurrency_code = $objp->multicurrency_code;
2197 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2198 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2199 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2200 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2201
2202 $line->fetch_optionals();
2203
2204 // multilangs
2205 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2206 $tmpproduct = new Product($this->db);
2207 $tmpproduct->fetch($objp->fk_product);
2208 $tmpproduct->getMultiLangs();
2209
2210 $line->multilangs = $tmpproduct->multilangs;
2211 }
2212
2213 $this->lines[$i] = $line;
2214
2215 $i++;
2216 }
2217
2218 $this->db->free($result);
2219
2220 return 1;
2221 } else {
2222 $this->error = $this->db->error();
2223 return -3;
2224 }
2225 }
2226
2227
2233 public function getNbOfProductsLines()
2234 {
2235 $nb = 0;
2236 foreach ($this->lines as $line) {
2237 if ($line->product_type == 0) {
2238 $nb++;
2239 }
2240 }
2241 return $nb;
2242 }
2243
2249 public function getNbOfServicesLines()
2250 {
2251 $nb = 0;
2252 foreach ($this->lines as $line) {
2253 if ($line->product_type == 1) {
2254 $nb++;
2255 }
2256 }
2257 return $nb;
2258 }
2259
2265 public function getNbOfShipments()
2266 {
2267 $nb = 0;
2268
2269 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2270 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2271 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2272 $sql .= ' WHERE';
2273 $sql .= ' ed.fk_elementdet = cd.rowid';
2274 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2275 //print $sql;
2276
2277 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2278 $resql = $this->db->query($sql);
2279 if ($resql) {
2280 $obj = $this->db->fetch_object($resql);
2281 if ($obj) {
2282 $nb = $obj->nb;
2283 }
2284
2285 $this->db->free($resql);
2286 return $nb;
2287 } else {
2288 $this->error = $this->db->lasterror();
2289 return -1;
2290 }
2291 }
2292
2301 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2302 {
2303 $this->expeditions = array();
2304
2305 $sql = 'SELECT cd.rowid, cd.fk_product,';
2306 $sql .= ' sum(ed.qty) as qty';
2307 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2308 if ($filtre_statut >= 0) {
2309 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2310 }
2311 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2312 $sql .= ' WHERE';
2313 if ($filtre_statut >= 0) {
2314 $sql .= ' ed.fk_expedition = e.rowid AND';
2315 }
2316 $sql .= ' ed.fk_elementdet = cd.rowid';
2317 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2318 if ($fk_product > 0) {
2319 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2320 }
2321 if ($filtre_statut >= 0) {
2322 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2323 }
2324 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2325 //print $sql;
2326
2327 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2328 $resql = $this->db->query($sql);
2329 if ($resql) {
2330 $num = $this->db->num_rows($resql);
2331 $i = 0;
2332 while ($i < $num) {
2333 $obj = $this->db->fetch_object($resql);
2334 $this->expeditions[$obj->rowid] = $obj->qty;
2335 $i++;
2336 }
2337 $this->db->free($resql);
2338 return $num;
2339 } else {
2340 $this->error = $this->db->lasterror();
2341 return -1;
2342 }
2343 }
2344
2350 public function countNbOfShipments()
2351 {
2352 $sql = 'SELECT count(*)';
2353 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2354 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2355 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2356 $sql .= " AND el.sourcetype = 'commande'";
2357 $sql .= " AND el.fk_target = e.rowid";
2358 $sql .= " AND el.targettype = 'shipping'";
2359
2360 $resql = $this->db->query($sql);
2361 if ($resql) {
2362 $row = $this->db->fetch_row($resql);
2363 return $row[0];
2364 } else {
2365 dol_print_error($this->db);
2366 }
2367
2368 return 0;
2369 }
2370
2371 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2380 /*public function stock_array($filtre_statut = self::STATUS_CANCELED)
2381 {
2382 // phpcs:enable
2383 $this->stocks = array();
2384
2385 // Tableau des id de produit de la commande
2386 $array_of_product = array();
2387
2388 // Recherche total en stock pour chaque produit
2389 // TODO $array_of_product est défini vide juste au dessus !!
2390 if (count($array_of_product)) {
2391 $sql = "SELECT fk_product, sum(ps.reel) as total";
2392 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2393 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2394 $sql .= ' GROUP BY fk_product';
2395 $resql = $this->db->query($sql);
2396 if ($resql) {
2397 $num = $this->db->num_rows($resql);
2398 $i = 0;
2399 while ($i < $num) {
2400 $obj = $this->db->fetch_object($resql);
2401 $this->stocks[$obj->fk_product] = $obj->total;
2402 $i++;
2403 }
2404 $this->db->free($resql);
2405 }
2406 }
2407 return 0;
2408 }*/
2409
2418 public function deleteLine($user = null, $lineid = 0, $id = 0)
2419 {
2420 if ($this->statut == self::STATUS_DRAFT) {
2421 $this->db->begin();
2422
2423 // Delete line
2424 $line = new OrderLine($this->db);
2425
2426 $line->context = $this->context;
2427
2428 // Load data
2429 $line->fetch($lineid);
2430
2431 if ($id > 0 && $line->fk_commande != $id) {
2432 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2433 return -1;
2434 }
2435
2436 // Memorize previous line for triggers
2437 $staticline = clone $line;
2438 $line->oldline = $staticline;
2439
2440 if ($line->delete($user) > 0) {
2441 $result = $this->update_price(1);
2442
2443 if ($result > 0) {
2444 $this->db->commit();
2445 return 1;
2446 } else {
2447 $this->db->rollback();
2448 $this->error = $this->db->lasterror();
2449 return -1;
2450 }
2451 } else {
2452 $this->db->rollback();
2453 $this->error = $line->error;
2454 return -1;
2455 }
2456 } else {
2457 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2458 return -1;
2459 }
2460 }
2461
2462 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2473 public function set_remise($user, $remise, $notrigger = 0)
2474 {
2475 // phpcs:enable
2476 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2477 // @phan-suppress-next-line PhanDeprecatedFunction
2478 return $this->setDiscount($user, $remise, $notrigger);
2479 }
2480
2489 public function setDiscount($user, $remise, $notrigger = 0)
2490 {
2491 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2492
2493 if ($user->hasRight('commande', 'creer')) {
2494 $error = 0;
2495
2496 $this->db->begin();
2497
2498 $remise = price2num($remise, 2);
2499
2500 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2501 $sql .= ' SET remise_percent = '.((float) $remise);
2502 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2503
2504 dol_syslog(__METHOD__, LOG_DEBUG);
2505 $resql = $this->db->query($sql);
2506 if (!$resql) {
2507 $this->errors[] = $this->db->error();
2508 $error++;
2509 }
2510
2511 if (!$error) {
2512 $this->oldcopy = clone $this;
2513 $this->remise_percent = $remise;
2514 $this->update_price(1);
2515 }
2516
2517 if (!$notrigger && empty($error)) {
2518 // Call trigger
2519 $result = $this->call_trigger('ORDER_MODIFY', $user);
2520 if ($result < 0) {
2521 $error++;
2522 }
2523 // End call triggers
2524 }
2525
2526 if (!$error) {
2527 $this->db->commit();
2528 return 1;
2529 } else {
2530 foreach ($this->errors as $errmsg) {
2531 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2532 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2533 }
2534 $this->db->rollback();
2535 return -1 * $error;
2536 }
2537 }
2538
2539 return 0;
2540 }
2541
2542
2543 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2552 /*
2553 public function set_remise_absolue($user, $remise, $notrigger = 0)
2554 {
2555 // phpcs:enable
2556 if (empty($remise)) {
2557 $remise = 0;
2558 }
2559
2560 $remise = price2num($remise);
2561
2562 if ($user->hasRight('commande', 'creer')) {
2563 $error = 0;
2564
2565 $this->db->begin();
2566
2567 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2568 $sql .= ' SET remise_absolue = '.((float) $remise);
2569 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2570
2571 dol_syslog(__METHOD__, LOG_DEBUG);
2572 $resql = $this->db->query($sql);
2573 if (!$resql) {
2574 $this->errors[] = $this->db->error();
2575 $error++;
2576 }
2577
2578 if (!$error) {
2579 $this->oldcopy = clone $this;
2580 $this->remise_absolue = $remise;
2581 $this->update_price(1);
2582 }
2583
2584 if (!$notrigger && empty($error)) {
2585 // Call trigger
2586 $result = $this->call_trigger('ORDER_MODIFY', $user);
2587 if ($result < 0) {
2588 $error++;
2589 }
2590 // End call triggers
2591 }
2592
2593 if (!$error) {
2594 $this->db->commit();
2595 return 1;
2596 } else {
2597 foreach ($this->errors as $errmsg) {
2598 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2599 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2600 }
2601 $this->db->rollback();
2602 return -1 * $error;
2603 }
2604 }
2605
2606 return 0;
2607 }
2608 */
2609
2610 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2619 public function set_date($user, $date, $notrigger = 0)
2620 {
2621 // phpcs:enable
2622 if ($user->hasRight('commande', 'creer')) {
2623 $error = 0;
2624
2625 $this->db->begin();
2626
2627 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2628 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2629 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2630
2631 dol_syslog(__METHOD__, LOG_DEBUG);
2632 $resql = $this->db->query($sql);
2633 if (!$resql) {
2634 $this->errors[] = $this->db->error();
2635 $error++;
2636 }
2637
2638 if (!$error) {
2639 $this->oldcopy = clone $this;
2640 $this->date = $date;
2641 }
2642
2643 if (!$notrigger && empty($error)) {
2644 // Call trigger
2645 $result = $this->call_trigger('ORDER_MODIFY', $user);
2646 if ($result < 0) {
2647 $error++;
2648 }
2649 // End call triggers
2650 }
2651
2652 if (!$error) {
2653 $this->db->commit();
2654 return 1;
2655 } else {
2656 foreach ($this->errors as $errmsg) {
2657 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2658 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2659 }
2660 $this->db->rollback();
2661 return -1 * $error;
2662 }
2663 } else {
2664 return -2;
2665 }
2666 }
2667
2668 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2678 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2679 {
2680 // phpcs:enable
2681 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2682 }
2683
2692 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2693 {
2694 if ($user->hasRight('commande', 'creer')) {
2695 $error = 0;
2696
2697 $this->db->begin();
2698
2699 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2700 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2701 $sql .= " WHERE rowid = ".((int) $this->id);
2702
2703 dol_syslog(__METHOD__, LOG_DEBUG);
2704 $resql = $this->db->query($sql);
2705 if (!$resql) {
2706 $this->errors[] = $this->db->error();
2707 $error++;
2708 }
2709
2710 if (!$error) {
2711 $this->oldcopy = clone $this;
2712 $this->delivery_date = $delivery_date;
2713 }
2714
2715 if (!$notrigger && empty($error)) {
2716 // Call trigger
2717 $result = $this->call_trigger('ORDER_MODIFY', $user);
2718 if ($result < 0) {
2719 $error++;
2720 }
2721 // End call triggers
2722 }
2723
2724 if (!$error) {
2725 $this->db->commit();
2726 return 1;
2727 } else {
2728 foreach ($this->errors as $errmsg) {
2729 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2730 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2731 }
2732 $this->db->rollback();
2733 return -1 * $error;
2734 }
2735 } else {
2736 return -2;
2737 }
2738 }
2739
2740 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2754 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2755 {
2756 // phpcs:enable
2757 global $user;
2758
2759 $ga = array();
2760
2761 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2762 $sql .= " c.rowid as cid, c.ref";
2763 if (!$user->hasRight('societe', 'client', 'voir')) {
2764 $sql .= ", sc.fk_soc, sc.fk_user";
2765 }
2766 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2767 if (!$user->hasRight('societe', 'client', 'voir')) {
2768 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2769 }
2770 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2771 $sql .= " AND c.fk_soc = s.rowid";
2772 if (!$user->hasRight('societe', 'client', 'voir')) {
2773 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2774 }
2775 if ($socid) {
2776 $sql .= " AND s.rowid = ".((int) $socid);
2777 }
2778 if ($draft) {
2779 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2780 }
2781 if (is_object($excluser)) {
2782 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2783 }
2784 $sql .= $this->db->order($sortfield, $sortorder);
2785 $sql .= $this->db->plimit($limit, $offset);
2786
2787 $result = $this->db->query($sql);
2788 if ($result) {
2789 $numc = $this->db->num_rows($result);
2790 if ($numc) {
2791 $i = 0;
2792 while ($i < $numc) {
2793 $obj = $this->db->fetch_object($result);
2794
2795 if ($shortlist == 1) {
2796 $ga[$obj->cid] = $obj->ref;
2797 } elseif ($shortlist == 2) {
2798 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2799 } else {
2800 $ga[$i]['id'] = $obj->cid;
2801 $ga[$i]['ref'] = $obj->ref;
2802 $ga[$i]['name'] = $obj->name;
2803 }
2804 $i++;
2805 }
2806 }
2807 return $ga;
2808 } else {
2809 dol_print_error($this->db);
2810 return -1;
2811 }
2812 }
2813
2821 public function availability($availability_id, $notrigger = 0)
2822 {
2823 global $user;
2824
2825 dol_syslog('Commande::availability('.$availability_id.')');
2826 if ($this->statut >= self::STATUS_DRAFT) {
2827 $error = 0;
2828
2829 $this->db->begin();
2830
2831 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2832 $sql .= ' SET fk_availability = '.((int) $availability_id);
2833 $sql .= ' WHERE rowid='.((int) $this->id);
2834
2835 dol_syslog(__METHOD__, LOG_DEBUG);
2836 $resql = $this->db->query($sql);
2837 if (!$resql) {
2838 $this->errors[] = $this->db->error();
2839 $error++;
2840 }
2841
2842 if (!$error) {
2843 $this->oldcopy = clone $this;
2844 $this->availability_id = $availability_id;
2845 }
2846
2847 if (!$notrigger && empty($error)) {
2848 // Call trigger
2849 $result = $this->call_trigger('ORDER_MODIFY', $user);
2850 if ($result < 0) {
2851 $error++;
2852 }
2853 // End call triggers
2854 }
2855
2856 if (!$error) {
2857 $this->db->commit();
2858 return 1;
2859 } else {
2860 foreach ($this->errors as $errmsg) {
2861 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2862 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2863 }
2864 $this->db->rollback();
2865 return -1 * $error;
2866 }
2867 } else {
2868 $error_str = 'Command status do not meet requirement '.$this->statut;
2869 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2870 $this->error = $error_str;
2871 $this->errors[] = $this->error;
2872 return -2;
2873 }
2874 }
2875
2876 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2884 public function demand_reason($demand_reason_id, $notrigger = 0)
2885 {
2886 // phpcs:enable
2887 global $user;
2888
2889 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2890 if ($this->statut >= self::STATUS_DRAFT) {
2891 $error = 0;
2892
2893 $this->db->begin();
2894
2895 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2896 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2897 $sql .= ' WHERE rowid='.((int) $this->id);
2898
2899 dol_syslog(__METHOD__, LOG_DEBUG);
2900 $resql = $this->db->query($sql);
2901 if (!$resql) {
2902 $this->errors[] = $this->db->error();
2903 $error++;
2904 }
2905
2906 if (!$error) {
2907 $this->oldcopy = clone $this;
2908 $this->demand_reason_id = $demand_reason_id;
2909 }
2910
2911 if (!$notrigger && empty($error)) {
2912 // Call trigger
2913 $result = $this->call_trigger('ORDER_MODIFY', $user);
2914 if ($result < 0) {
2915 $error++;
2916 }
2917 // End call triggers
2918 }
2919
2920 if (!$error) {
2921 $this->db->commit();
2922 return 1;
2923 } else {
2924 foreach ($this->errors as $errmsg) {
2925 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2926 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2927 }
2928 $this->db->rollback();
2929 return -1 * $error;
2930 }
2931 } else {
2932 $error_str = 'order status do not meet requirement '.$this->statut;
2933 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2934 $this->error = $error_str;
2935 $this->errors[] = $this->error;
2936 return -2;
2937 }
2938 }
2939
2940 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2949 public function set_ref_client($user, $ref_client, $notrigger = 0)
2950 {
2951 // phpcs:enable
2952 if ($user->hasRight('commande', 'creer')) {
2953 $error = 0;
2954
2955 $this->db->begin();
2956
2957 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2958 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2959 $sql .= ' WHERE rowid = '.((int) $this->id);
2960
2961 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2962 $resql = $this->db->query($sql);
2963 if (!$resql) {
2964 $this->errors[] = $this->db->error();
2965 $error++;
2966 }
2967
2968 if (!$error) {
2969 $this->oldcopy = clone $this;
2970 $this->ref_client = $ref_client;
2971 $this->ref_customer = $ref_client;
2972 }
2973
2974 if (!$notrigger && empty($error)) {
2975 // Call trigger
2976 $result = $this->call_trigger('ORDER_MODIFY', $user);
2977 if ($result < 0) {
2978 $error++;
2979 }
2980 // End call triggers
2981 }
2982 if (!$error) {
2983 $this->db->commit();
2984 return 1;
2985 } else {
2986 foreach ($this->errors as $errmsg) {
2987 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2988 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2989 }
2990 $this->db->rollback();
2991 return -1 * $error;
2992 }
2993 } else {
2994 return -1;
2995 }
2996 }
2997
3005 public function classifyBilled(User $user, $notrigger = 0)
3006 {
3007 $error = 0;
3008
3009 if ($this->billed) {
3010 return 0;
3011 }
3012
3013 $this->db->begin();
3014
3015 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3016 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3017
3018 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3019 if ($this->db->query($sql)) {
3020 if (!$error) {
3021 $this->oldcopy = clone $this;
3022 $this->billed = 1;
3023 }
3024
3025 if (!$notrigger && empty($error)) {
3026 // Call trigger
3027 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3028 if ($result < 0) {
3029 $error++;
3030 }
3031 // End call triggers
3032 }
3033
3034 if (!$error) {
3035 $this->db->commit();
3036 return 1;
3037 } else {
3038 foreach ($this->errors as $errmsg) {
3039 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3040 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3041 }
3042 $this->db->rollback();
3043 return -1 * $error;
3044 }
3045 } else {
3046 $this->error = $this->db->error();
3047 $this->db->rollback();
3048 return -1;
3049 }
3050 }
3051
3059 public function classifyUnBilled(User $user, $notrigger = 0)
3060 {
3061 $error = 0;
3062
3063 $this->db->begin();
3064
3065 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3066 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3067
3068 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3069 if ($this->db->query($sql)) {
3070 if (!$error) {
3071 $this->oldcopy = clone $this;
3072 $this->billed = 1;
3073 }
3074
3075 if (!$notrigger && empty($error)) {
3076 // Call trigger
3077 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3078 if ($result < 0) {
3079 $error++;
3080 }
3081 // End call triggers
3082 }
3083
3084 if (!$error) {
3085 $this->billed = 0;
3086
3087 $this->db->commit();
3088 return 1;
3089 } else {
3090 foreach ($this->errors as $errmsg) {
3091 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3092 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3093 }
3094 $this->db->rollback();
3095 return -1 * $error;
3096 }
3097 } else {
3098 $this->error = $this->db->error();
3099 $this->db->rollback();
3100 return -1;
3101 }
3102 }
3103
3104
3135 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)
3136 {
3137 global $conf, $mysoc, $langs, $user;
3138
3139 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");
3140 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3141
3142 if ($this->statut == Commande::STATUS_DRAFT) {
3143 // Clean parameters
3144 if (empty($qty)) {
3145 $qty = 0;
3146 }
3147 if (empty($info_bits)) {
3148 $info_bits = 0;
3149 }
3150 if (empty($txtva)) {
3151 $txtva = 0;
3152 }
3153 if (empty($txlocaltax1)) {
3154 $txlocaltax1 = 0;
3155 }
3156 if (empty($txlocaltax2)) {
3157 $txlocaltax2 = 0;
3158 }
3159 if (empty($remise_percent)) {
3160 $remise_percent = 0;
3161 }
3162 if (empty($special_code) || $special_code == 3) {
3163 $special_code = 0;
3164 }
3165 if (empty($ref_ext)) {
3166 $ref_ext = '';
3167 }
3168
3169 if ($date_start && $date_end && $date_start > $date_end) {
3170 $langs->load("errors");
3171 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3172 return -1;
3173 }
3174
3175 $remise_percent = (float) price2num($remise_percent);
3176 $qty = (float) price2num($qty);
3177 $pu = price2num($pu);
3178 $pa_ht = (float) price2num($pa_ht);
3179 $pu_ht_devise = price2num($pu_ht_devise);
3180 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3181 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3182 }
3183 $txlocaltax1 = (float) price2num($txlocaltax1);
3184 $txlocaltax2 = (float) price2num($txlocaltax2);
3185
3186 $this->db->begin();
3187
3188 // Calcul du total TTC et de la TVA pour la ligne a partir de
3189 // qty, pu, remise_percent et txtva
3190 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3191 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3192
3193 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3194
3195 // Clean vat code
3196 $vat_src_code = '';
3197 $reg = array();
3198 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3199 $vat_src_code = $reg[1];
3200 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3201 }
3202
3203 $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);
3204
3205 $total_ht = $tabprice[0];
3206 $total_tva = $tabprice[1];
3207 $total_ttc = $tabprice[2];
3208 $total_localtax1 = $tabprice[9];
3209 $total_localtax2 = $tabprice[10];
3210 $pu_ht = $tabprice[3];
3211 $pu_tva = $tabprice[4];
3212 $pu_ttc = $tabprice[5];
3213
3214 // MultiCurrency
3215 $multicurrency_total_ht = $tabprice[16];
3216 $multicurrency_total_tva = $tabprice[17];
3217 $multicurrency_total_ttc = $tabprice[18];
3218 $pu_ht_devise = $tabprice[19];
3219
3220 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3221 $price = $pu_ht;
3222 if ($price_base_type == 'TTC') {
3223 $subprice = $pu_ttc;
3224 } else {
3225 $subprice = $pu_ht;
3226 }
3227 $remise = 0;
3228 if ($remise_percent > 0) {
3229 $remise = round(((float) $pu * $remise_percent / 100), 2);
3230 $price = ((float) $pu - $remise);
3231 }
3232
3233 // Fetch current line from the database and then clone the object and set it in $oldline property
3234 $line = new OrderLine($this->db);
3235 $line->fetch($rowid);
3236 $line->fetch_optionals();
3237
3238 if (!empty($line->fk_product)) {
3239 $product = new Product($this->db);
3240 $result = $product->fetch($line->fk_product);
3241 $product_type = $product->type;
3242
3243 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
3244 $langs->load("errors");
3245 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3246 $this->errors[] = $this->error;
3247
3248 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3249
3250 $this->db->rollback();
3252 }
3253 }
3254
3255 $staticline = clone $line;
3256
3257 $line->oldline = $staticline;
3258 $this->line = $line;
3259 $this->line->context = $this->context;
3260 $this->line->rang = $rang;
3261
3262 // Reorder if fk_parent_line change
3263 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3264 $rangmax = $this->line_max($fk_parent_line);
3265 $this->line->rang = $rangmax + 1;
3266 }
3267
3268 $this->line->id = $rowid;
3269 $this->line->label = $label;
3270 $this->line->desc = $desc;
3271 $this->line->qty = $qty;
3272 $this->line->ref_ext = $ref_ext;
3273
3274 $this->line->vat_src_code = $vat_src_code;
3275 $this->line->tva_tx = $txtva;
3276 $this->line->localtax1_tx = $txlocaltax1;
3277 $this->line->localtax2_tx = $txlocaltax2;
3278 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3279 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3280 $this->line->remise_percent = $remise_percent;
3281 $this->line->subprice = $pu_ht;
3282 $this->line->info_bits = $info_bits;
3283 $this->line->special_code = $special_code;
3284 $this->line->total_ht = $total_ht;
3285 $this->line->total_tva = $total_tva;
3286 $this->line->total_localtax1 = $total_localtax1;
3287 $this->line->total_localtax2 = $total_localtax2;
3288 $this->line->total_ttc = $total_ttc;
3289 $this->line->date_start = $date_start;
3290 $this->line->date_end = $date_end;
3291 $this->line->product_type = $type;
3292 $this->line->fk_parent_line = $fk_parent_line;
3293 $this->line->skip_update_total = $skip_update_total;
3294 $this->line->fk_unit = $fk_unit;
3295
3296 $this->line->fk_fournprice = $fk_fournprice;
3297 $this->line->pa_ht = $pa_ht;
3298
3299 // Multicurrency
3300 $this->line->multicurrency_subprice = $pu_ht_devise;
3301 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3302 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3303 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3304
3305 // TODO deprecated
3306 $this->line->price = $price;
3307
3308 if (is_array($array_options) && count($array_options) > 0) {
3309 // We replace values in this->line->array_options only for entries defined into $array_options
3310 foreach ($array_options as $key => $value) {
3311 $this->line->array_options[$key] = $array_options[$key];
3312 }
3313 }
3314
3315 $result = $this->line->update($user, $notrigger);
3316 if ($result > 0) {
3317 // Reorder if child line
3318 if (!empty($fk_parent_line)) {
3319 $this->line_order(true, 'DESC');
3320 }
3321
3322 // Mise a jour info denormalisees
3323 $this->update_price(1, 'auto');
3324
3325 $this->db->commit();
3326 return $result;
3327 } else {
3328 $this->error = $this->line->error;
3329
3330 $this->db->rollback();
3331 return -1;
3332 }
3333 } else {
3334 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3335 $this->errors = array('OrderStatusMakeOperationForbidden');
3336 return -2;
3337 }
3338 }
3339
3347 public function update(User $user, $notrigger = 0)
3348 {
3349 global $conf;
3350
3351 $error = 0;
3352
3353 // Clean parameters
3354 if (isset($this->ref)) {
3355 $this->ref = trim($this->ref);
3356 }
3357 if (isset($this->ref_client)) {
3358 $this->ref_client = trim($this->ref_client);
3359 }
3360 if (isset($this->ref_customer)) {
3361 $this->ref_customer = trim($this->ref_customer);
3362 }
3363 if (isset($this->note) || isset($this->note_private)) {
3364 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3365 }
3366 if (isset($this->note_public)) {
3367 $this->note_public = trim($this->note_public);
3368 }
3369 if (isset($this->model_pdf)) {
3370 $this->model_pdf = trim($this->model_pdf);
3371 }
3372 if (isset($this->import_key)) {
3373 $this->import_key = trim($this->import_key);
3374 }
3375 $delivery_date = $this->delivery_date;
3376
3377 // Check parameters
3378 // Put here code to add control on parameters values
3379
3380 // Update request
3381 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3382
3383 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3384 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3385 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3386 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3387 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3388 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3389 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3390 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3391 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3392 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3393 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3394 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3395 $sql .= " fk_user_modif=".(isset($user->id) ? $user->id : "null").",";
3396 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? $this->user_validation_id : "null").",";
3397 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3398 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3399 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3400 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3401 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3402 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3403 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3404 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3405 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3406 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3407 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3408 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3409
3410 $sql .= " WHERE rowid=".((int) $this->id);
3411
3412 $this->db->begin();
3413
3414 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3415 $resql = $this->db->query($sql);
3416 if (!$resql) {
3417 $error++;
3418 $this->errors[] = "Error ".$this->db->lasterror();
3419 }
3420
3421 if (!$error) {
3422 $result = $this->insertExtraFields();
3423 if ($result < 0) {
3424 $error++;
3425 }
3426 }
3427
3428 if (!$error && !$notrigger) {
3429 // Call trigger
3430 $result = $this->call_trigger('ORDER_MODIFY', $user);
3431 if ($result < 0) {
3432 $error++;
3433 }
3434 // End call triggers
3435 }
3436
3437 // Commit or rollback
3438 if ($error) {
3439 foreach ($this->errors as $errmsg) {
3440 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3441 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3442 }
3443 $this->db->rollback();
3444 return -1 * $error;
3445 } else {
3446 $this->db->commit();
3447 return 1;
3448 }
3449 }
3450
3458 public function delete($user, $notrigger = 0)
3459 {
3460 global $conf, $langs;
3461 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3462
3463 $error = 0;
3464
3465 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3466
3467 $this->db->begin();
3468
3469 if (!$notrigger) {
3470 // Call trigger
3471 $result = $this->call_trigger('ORDER_DELETE', $user);
3472 if ($result < 0) {
3473 $error++;
3474 }
3475 // End call triggers
3476 }
3477
3478 // Test we can delete
3479 if ($this->countNbOfShipments() != 0) {
3480 $this->errors[] = $langs->trans('SomeShipmentExists');
3481 $error++;
3482 }
3483
3484 // Delete extrafields of lines and lines
3485 if (!$error && !empty($this->table_element_line)) {
3486 $tabletodelete = $this->table_element_line;
3487 $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).")";
3488 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3489 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3490 $error++;
3491 $this->error = $this->db->lasterror();
3492 $this->errors[] = $this->error;
3493 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3494 }
3495 }
3496
3497 if (!$error) {
3498 // Delete linked object
3499 $res = $this->deleteObjectLinked();
3500 if ($res < 0) {
3501 $error++;
3502 }
3503 }
3504
3505 if (!$error) {
3506 // Delete linked contacts
3507 $res = $this->delete_linked_contact();
3508 if ($res < 0) {
3509 $error++;
3510 }
3511 }
3512
3513 // Removed extrafields of object
3514 if (!$error) {
3515 $result = $this->deleteExtraFields();
3516 if ($result < 0) {
3517 $error++;
3518 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3519 }
3520 }
3521
3522 // Delete main record
3523 if (!$error) {
3524 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3525 $res = $this->db->query($sql);
3526 if (!$res) {
3527 $error++;
3528 $this->error = $this->db->lasterror();
3529 $this->errors[] = $this->error;
3530 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3531 }
3532 }
3533
3534 // Delete record into ECM index and physically
3535 if (!$error) {
3536 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3537 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3538 if (!$res) {
3539 $error++;
3540 }
3541 }
3542
3543 if (!$error) {
3544 // We remove directory
3545 $ref = dol_sanitizeFileName($this->ref);
3546 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3547 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3548 $file = $dir."/".$ref.".pdf";
3549 if (file_exists($file)) {
3550 dol_delete_preview($this);
3551
3552 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3553 $this->error = 'ErrorFailToDeleteFile';
3554 $this->errors[] = $this->error;
3555 $this->db->rollback();
3556 return 0;
3557 }
3558 }
3559 if (file_exists($dir)) {
3560 $res = @dol_delete_dir_recursive($dir);
3561 if (!$res) {
3562 $this->error = 'ErrorFailToDeleteDir';
3563 $this->errors[] = $this->error;
3564 $this->db->rollback();
3565 return 0;
3566 }
3567 }
3568 }
3569 }
3570
3571 if (!$error) {
3572 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3573 $this->db->commit();
3574 return 1;
3575 } else {
3576 $this->db->rollback();
3577 return -1;
3578 }
3579 }
3580
3581
3582 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3590 public function load_board($user, $mode)
3591 {
3592 // phpcs:enable
3593 global $conf, $langs;
3594
3595 $clause = " WHERE";
3596
3597 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3598 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3599 if (!$user->hasRight('societe', 'client', 'voir')) {
3600 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3601 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3602 $clause = " AND";
3603 }
3604 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3605 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3606 if ($mode == 'toship') {
3607 // An order to ship is an open order (validated or in progress)
3608 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3609 }
3610 if ($mode == 'tobill') {
3611 // An order to bill is an order not already billed
3612 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3613 }
3614 if ($mode == 'shippedtobill') {
3615 // An order shipped and to bill is a delivered order not already billed
3616 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3617 }
3618 if ($user->socid) {
3619 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3620 }
3621
3622 $resql = $this->db->query($sql);
3623 if ($resql) {
3624 $delay_warning = 0;
3625 $label = $labelShort = $url = '';
3626 if ($mode == 'toship') {
3627 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3628 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3629 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3630 $labelShort = $langs->transnoentitiesnoconv("Opened");
3631 }
3632 if ($mode == 'tobill') {
3633 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3634 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3635 $labelShort = $langs->trans("ToBill");
3636 }
3637 if ($mode == 'shippedtobill') {
3638 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3639 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3640 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3641 }
3642
3643 $response = new WorkboardResponse();
3644
3645 $response->warning_delay = $delay_warning;
3646 $response->label = $label;
3647 $response->labelShort = $labelShort;
3648 $response->url = $url;
3649 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3650 $response->img = img_object('', "order");
3651
3652 $generic_commande = new Commande($this->db);
3653
3654 while ($obj = $this->db->fetch_object($resql)) {
3655 $response->nbtodo++;
3656 $response->total += $obj->total_ht;
3657
3658 $generic_commande->statut = $obj->fk_statut;
3659 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3660 $generic_commande->date = $this->db->jdate($obj->date_commande);
3661 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3662
3663 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3664 $response->nbtodolate++;
3665 }
3666 }
3667
3668 return $response;
3669 } else {
3670 $this->error = $this->db->error();
3671 return -1;
3672 }
3673 }
3674
3680 public function getLabelSource()
3681 {
3682 global $langs;
3683
3684 $label = $langs->trans('OrderSource'.$this->source);
3685
3686 if ($label == 'OrderSource') {
3687 return '';
3688 }
3689 return $label;
3690 }
3691
3698 public function getLibStatut($mode)
3699 {
3700 return $this->LibStatut($this->statut, $this->billed, $mode);
3701 }
3702
3703 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3713 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3714 {
3715 // phpcs:enable
3716 global $langs, $hookmanager;
3717
3718 $billedtext = '';
3719 if (empty($donotshowbilled)) {
3720 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3721 }
3722
3723 $labelTooltip = '';
3724
3725 if ($status == self::STATUS_CANCELED) {
3726 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3727 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3728 $statusType = 'status9';
3729 } elseif ($status == self::STATUS_DRAFT) {
3730 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3731 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3732 $statusType = 'status0';
3733 } elseif ($status == self::STATUS_VALIDATED) {
3734 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3735 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3736 $statusType = 'status1';
3737 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3738 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3739 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3740 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3741 if (!empty($this->delivery_date)) {
3742 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3743 }
3744 $statusType = 'status4';
3745 } elseif ($status == self::STATUS_CLOSED) {
3746 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtext;
3747 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3748 $statusType = 'status6';
3749 } else {
3750 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3751 $labelStatusShort = '';
3752 $statusType = '';
3753 $mode = 0;
3754 }
3755
3756 $parameters = array(
3757 'status' => $status,
3758 'mode' => $mode,
3759 'billed' => $billed,
3760 'donotshowbilled' => $donotshowbilled
3761 );
3762
3763 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3764
3765 if ($reshook > 0) {
3766 return $hookmanager->resPrint;
3767 }
3768
3769 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3770 }
3771
3778 public function getTooltipContentArray($params)
3779 {
3780 global $conf, $langs, $user;
3781
3782 $langs->load('orders');
3783 $datas = [];
3784 $nofetch = !empty($params['nofetch']);
3785
3786 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3787 return ['optimize' => $langs->trans("Order")];
3788 }
3789
3790 if ($user->hasRight('commande', 'lire')) {
3791 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3792 if (isset($this->statut)) {
3793 $datas['status'] = ' '.$this->getLibStatut(5);
3794 }
3795 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3796 if (!$nofetch) {
3797 $langs->load('companies');
3798 if (empty($this->thirdparty)) {
3799 $this->fetch_thirdparty();
3800 }
3801 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3802 }
3803 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3804 if (!$nofetch) {
3805 $langs->load('project');
3806 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3807 $res = $this->fetchProject();
3808 if ($res > 0 && $this->project instanceof Project) {
3809 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3810 }
3811 }
3812 }
3813 if (!empty($this->total_ht)) {
3814 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3815 }
3816 if (!empty($this->total_tva)) {
3817 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3818 }
3819 if (!empty($this->total_ttc)) {
3820 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3821 }
3822 if (!empty($this->date)) {
3823 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3824 }
3825 if (!empty($this->delivery_date)) {
3826 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3827 }
3828 }
3829
3830 return $datas;
3831 }
3832
3846 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3847 {
3848 global $conf, $langs, $user, $hookmanager;
3849
3850 if (!empty($conf->dol_no_mouse_hover)) {
3851 $notooltip = 1; // Force disable tooltips
3852 }
3853
3854 $result = '';
3855
3856 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3857 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3858 } else {
3859 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3860 }
3861
3862 if (!$user->hasRight('commande', 'lire')) {
3863 $option = 'nolink';
3864 }
3865
3866 if ($option !== 'nolink') {
3867 // Add param to save lastsearch_values or not
3868 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3869 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3870 $add_save_lastsearch_values = 1;
3871 }
3872 if ($add_save_lastsearch_values) {
3873 $url .= '&save_lastsearch_values=1';
3874 }
3875 }
3876
3877 if ($short) {
3878 return $url;
3879 }
3880 $params = [
3881 'id' => $this->id,
3882 'objecttype' => $this->element,
3883 'option' => $option,
3884 'nofetch' => 1,
3885 ];
3886 $classfortooltip = 'classfortooltip';
3887 $dataparams = '';
3888 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3889 $classfortooltip = 'classforajaxtooltip';
3890 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3891 $label = '';
3892 } else {
3893 $label = implode($this->getTooltipContentArray($params));
3894 }
3895
3896 $linkclose = '';
3897 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3898 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3899 $label = $langs->trans("Order");
3900 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3901 }
3902 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3903 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3904
3905 $target_value = array('_self', '_blank', '_parent', '_top');
3906 if (in_array($target, $target_value)) {
3907 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3908 }
3909 }
3910
3911 $linkstart = '<a href="'.$url.'"';
3912 $linkstart .= $linkclose.'>';
3913 $linkend = '</a>';
3914
3915 if ($option === 'nolink') {
3916 $linkstart = '';
3917 $linkend = '';
3918 }
3919
3920 $result .= $linkstart;
3921 if ($withpicto) {
3922 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3923 }
3924 if ($withpicto != 2) {
3925 $result .= $this->ref;
3926 }
3927 $result .= $linkend;
3928
3929 if ($addlinktonotes) {
3930 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3931 if ($txttoshow) {
3932 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3933 $result .= ' <span class="note inline-block">';
3934 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3935 $result .= img_picto('', 'note');
3936 $result .= '</a>';
3937 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3938 //$result.='</a>';
3939 $result .= '</span>';
3940 }
3941 }
3942
3943 global $action;
3944 $hookmanager->initHooks(array($this->element . 'dao'));
3945 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3946 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3947 if ($reshook > 0) {
3948 $result = $hookmanager->resPrint;
3949 } else {
3950 $result .= $hookmanager->resPrint;
3951 }
3952 return $result;
3953 }
3954
3955
3962 public function info($id)
3963 {
3964 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3965 $sql .= ' date_valid as datev,';
3966 $sql .= ' date_cloture as datecloture,';
3967 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3968 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
3969 $sql .= ' WHERE c.rowid = '.((int) $id);
3970 $result = $this->db->query($sql);
3971 if ($result) {
3972 if ($this->db->num_rows($result)) {
3973 $obj = $this->db->fetch_object($result);
3974 $this->id = $obj->rowid;
3975 if ($obj->fk_user_author) {
3976 $this->user_creation_id = $obj->fk_user_author;
3977 }
3978 if ($obj->fk_user_valid) {
3979 $this->user_validation_id = $obj->fk_user_valid;
3980 }
3981 if ($obj->fk_user_cloture) {
3982 $this->user_closing_id = $obj->fk_user_cloture;
3983 }
3984
3985 $this->date_creation = $this->db->jdate($obj->datec);
3986 $this->date_modification = $this->db->jdate($obj->datem);
3987 $this->date_validation = $this->db->jdate($obj->datev);
3988 $this->date_cloture = $this->db->jdate($obj->datecloture);
3989 }
3990
3991 $this->db->free($result);
3992 } else {
3993 dol_print_error($this->db);
3994 }
3995 }
3996
3997
4005 public function initAsSpecimen()
4006 {
4007 global $conf, $langs;
4008
4009 dol_syslog(get_class($this)."::initAsSpecimen");
4010
4011 // Load array of products prodids
4012 $num_prods = 0;
4013 $prodids = array();
4014 $sql = "SELECT rowid";
4015 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4016 $sql .= " WHERE entity IN (".getEntity('product').")";
4017 $sql .= $this->db->plimit(100);
4018
4019 $resql = $this->db->query($sql);
4020 if ($resql) {
4021 $num_prods = $this->db->num_rows($resql);
4022 $i = 0;
4023 while ($i < $num_prods) {
4024 $i++;
4025 $row = $this->db->fetch_row($resql);
4026 $prodids[$i] = $row[0];
4027 }
4028 }
4029
4030 // Initialise parameters
4031 $this->id = 0;
4032 $this->ref = 'SPECIMEN';
4033 $this->specimen = 1;
4034 $this->entity = $conf->entity;
4035 $this->socid = 1;
4036 $this->date = time();
4037 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4038 $this->cond_reglement_code = 'RECEP';
4039 $this->mode_reglement_code = 'CHQ';
4040 $this->availability_code = 'DSP';
4041 $this->demand_reason_code = 'SRC_00';
4042
4043 $this->note_public = 'This is a comment (public)';
4044 $this->note_private = 'This is a comment (private)';
4045
4046 $this->multicurrency_tx = 1;
4047 $this->multicurrency_code = $conf->currency;
4048
4049 $this->status = $this::STATUS_DRAFT;
4050
4051 // Lines
4052 $nbp = 5;
4053 $xnbp = 0;
4054 while ($xnbp < $nbp) {
4055 $line = new OrderLine($this->db);
4056
4057 $line->desc = $langs->trans("Description")." ".$xnbp;
4058 $line->qty = 1;
4059 $line->subprice = 100;
4060 $line->price = 100;
4061 $line->tva_tx = 20;
4062 if ($xnbp == 2) {
4063 $line->total_ht = 50;
4064 $line->total_ttc = 60;
4065 $line->total_tva = 10;
4066 $line->remise_percent = 50;
4067 } else {
4068 $line->total_ht = 100;
4069 $line->total_ttc = 120;
4070 $line->total_tva = 20;
4071 $line->remise_percent = 0;
4072 }
4073 if ($num_prods > 0) {
4074 $prodid = mt_rand(1, $num_prods);
4075 $line->fk_product = $prodids[$prodid];
4076 $line->product_ref = 'SPECIMEN';
4077 }
4078
4079 $this->lines[$xnbp] = $line;
4080
4081 $this->total_ht += $line->total_ht;
4082 $this->total_tva += $line->total_tva;
4083 $this->total_ttc += $line->total_ttc;
4084
4085 $xnbp++;
4086 }
4087
4088 return 1;
4089 }
4090
4091
4097 public function loadStateBoard()
4098 {
4099 global $user;
4100
4101 $this->nb = array();
4102 $clause = "WHERE";
4103
4104 $sql = "SELECT count(co.rowid) as nb";
4105 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as co";
4106 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4107 if (!$user->hasRight('societe', 'client', 'voir')) {
4108 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4109 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4110 $clause = "AND";
4111 }
4112 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4113
4114 $resql = $this->db->query($sql);
4115 if ($resql) {
4116 while ($obj = $this->db->fetch_object($resql)) {
4117 $this->nb["orders"] = $obj->nb;
4118 }
4119 $this->db->free($resql);
4120 return 1;
4121 } else {
4122 dol_print_error($this->db);
4123 $this->error = $this->db->error();
4124 return -1;
4125 }
4126 }
4127
4133 public function getLinesArray()
4134 {
4135 return $this->fetch_lines();
4136 }
4137
4149 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4150 {
4151 global $conf, $langs;
4152
4153 $langs->load("orders");
4154 $outputlangs->load("products");
4155
4156 if (!dol_strlen($modele)) {
4157 $modele = 'einstein';
4158
4159 if (!empty($this->model_pdf)) {
4160 $modele = $this->model_pdf;
4161 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4162 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4163 }
4164 }
4165
4166 $modelpath = "core/modules/commande/doc/";
4167
4168 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4169 }
4170
4171
4180 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4181 {
4182 $tables = array(
4183 'commande'
4184 );
4185
4186 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4187 }
4188
4197 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4198 {
4199 $tables = array(
4200 'commandedet',
4201 );
4202
4203 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4204 }
4205
4211 public function hasDelay()
4212 {
4213 global $conf;
4214
4215 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4216 return false; // Never late if not inside this status range
4217 }
4218
4219 $now = dol_now();
4220
4221 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4222 }
4223
4229 public function showDelay()
4230 {
4231 global $conf, $langs;
4232
4233 if (empty($this->delivery_date)) {
4234 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4235 } else {
4236 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4237 }
4238 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4239
4240 return $text;
4241 }
4242
4252 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4253 {
4254 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4255 }
4256}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
$object ref
Definition info.php:79
Class to manage customers orders.
getNbOfServicesLines()
Return number of line with type service.
getNbOfShipments()
Count number of shipments for this order.
createFromProposal($object, User $user)
Load an object from a proposal and create a new order into database.
setDraft($user, $idwarehouse=-1)
Set draft status.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
getLinesArray()
Create an array of order lines.
showDelay()
Show the customer delayed info.
set_date($user, $date, $notrigger=0)
Set a fixed amount discount.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
loadStateBoard()
Load the indicators this->nb for the state board.
fetch_lines($only_product=0, $loadalsotranslation=0)
Load array lines.
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:162
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)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return 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.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88