dolibarr 24.0.1
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-2025 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
17 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
18 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <https://www.gnu.org/licenses/>.
32 */
33
40require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
41require_once DOL_DOCUMENT_ROOT.'/commande/class/orderline.class.php';
42require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
43require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
44require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
45require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
46require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
47
48
52class Commande extends CommonOrder
53{
54 use CommonSubtotal;
55
59 public $element = 'commande';
60
65 public $TRIGGER_PREFIX = 'ORDER';
66
70 public $table_element = 'commande';
71
75 public $table_element_line = 'commandedet';
76
80 public $class_element_line = 'OrderLine';
81
85 public $fk_element = 'fk_commande';
86
90 public $picto = 'order';
91
96 public $restrictiononfksoc = 1;
97
101 protected $table_ref_field = 'ref';
102
106 public $ref;
107
111 public $socid;
112
116 public $ref_client;
117
121 public $ref_customer;
122
126 public $contactid;
127
133 public $statut;
134
139 public $status;
140
144 public $billed;
145
149 public $date_lim_reglement;
153 public $cond_reglement_code;
154
158 public $cond_reglement_doc;
159
164 public $deposit_percent;
165
169 public $fk_account;
170
174 public $mode_reglement;
175
179 public $mode_reglement_id;
180
184 public $mode_reglement_code;
185
190 public $availability_id;
191
196 public $availability_code;
197
202 public $availability;
203
207 public $demand_reason_id;
208
212 public $demand_reason_code;
213
217 public $date;
218
224 public $date_commande;
225
229 public $delivery_date;
230
234 public $fk_remise_except;
235
240 public $remise_percent;
241
245 public $source;
246
251 public $signed_status = 0;
252
256 public $warehouse_id;
257
261 public $extraparams = array();
262
266 public $user_author_id;
267
271 public $line;
272
276 public $lines = array();
277
278
282 public $module_source;
286 public $pos_source;
287
291 public $expeditions;
292
296 public $online_payment_url;
297
298
299
325 // BEGIN MODULEBUILDER PROPERTIES
329 public $fields = array(
330 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
331 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
332 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 25),
333 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 26),
334 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 28),
335 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 20),
336 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 25),
337 'date_commande' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'csslist' => 'nowraponall'),
338 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 62, 'csslist' => 'nowraponall'),
339 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'csslist' => 'nowraponall'),
340 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 85),
341 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => -1, 'position' => 90),
342 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => -1, 'position' => 95),
343 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
344 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
345 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
346 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
347 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
348 'signed_status' => array('type' => 'smallint(6)', 'label' => 'SignedStatus', 'enabled' => 1, 'visible' => -1, 'position' => 146, 'arrayofkeyval' => array(0 => 'NoSignature', 1 => 'SignedSender', 2 => 'SignedReceiver', 9 => 'SignedAll')),
349 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 150),
350 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 155),
351 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 160),
352 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 170),
353 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 175),
354 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 180),
355 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 181),
356 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 185),
357 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'csslist' => 'nowraponall'),
358 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 195),
359 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'DefaultWarehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 200, 'nodepth' => 1),
360 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 205),
361 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 210),
362 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
363 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 225),
364 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 230),
365 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 235),
366 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
367 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245),
368 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
369 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
370 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 260, 'isameasure' => 1),
371 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 265, 'isameasure' => 1),
372 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 270),
373 'module_source' => array('type' => 'varchar(32)', 'label' => 'POSModule', 'enabled' => 1, 'visible' => -1, 'position' => 275),
374 'pos_source' => array('type' => 'varchar(32)', 'label' => 'POSTerminal', 'enabled' => 1, 'visible' => -1, 'position' => 280),
375 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 300),
376 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 302),
377 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 304, 'csslist' => 'nowraponall'),
378 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 306),
379 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 400),
380 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'position' => 500),
381 );
382 // END MODULEBUILDER PROPERTIES
383
388
392 const STATUS_CANCELED = -1;
396 const STATUS_DRAFT = 0;
404 const STATUS_SHIPMENTONPROCESS = 2; // We set this status when a shipment is validated
405
411
415 const STATUS_CLOSED = 3;
416
417 /*
418 * No signature
419 */
420 const STATUS_NO_SIGNATURE = 0;
421
422 /*
423 * Signed by sender
424 */
425 const STATUS_SIGNED_SENDER = 1;
426
427 /*
428 * Signed by receiver
429 */
430 const STATUS_SIGNED_RECEIVER = 2;
431
432 /*
433 * Signed by all
434 */
435 const STATUS_SIGNED_ALL = 9; // To handle future kind of signature (ex: tripartite contract)
436
437
443 public function __construct($db)
444 {
445 $this->db = $db;
446
447 $this->ismultientitymanaged = 1;
448 $this->isextrafieldmanaged = 1;
449
450 $this->fields['ref_ext']['visible'] = getDolGlobalInt('MAIN_LIST_SHOW_REF_EXT');
451 }
452
460 public function getNextNumRef($soc)
461 {
462 global $langs, $conf;
463 $langs->load("order");
464
465 if (getDolGlobalString('COMMANDE_ADDON')) {
466 $mybool = false;
467
468 $file = getDolGlobalString('COMMANDE_ADDON') . ".php";
469 $classname = getDolGlobalString('COMMANDE_ADDON');
470
471 // Include file with class
472 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
473 foreach ($dirmodels as $reldir) {
474 $dir = dol_buildpath($reldir."core/modules/commande/");
475
476 // Load file with numbering class (if found)
477 $mybool = ((bool) @include_once $dir.$file) || $mybool;
478 }
479
480 if (!$mybool) {
481 dol_print_error(null, "Failed to include file ".$file);
482 return '';
483 }
484
485 $obj = new $classname();
487 '@phan-var-force ModeleNumRefCommandes $obj';
488
489 $numref = $obj->getNextValue($soc, $this);
490
491 if ($numref != "") {
492 return $numref;
493 } else {
494 $this->error = $obj->error;
495 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
496 return "";
497 }
498 } else {
499 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
500 return "";
501 }
502 }
503
504
513 public function valid($user, $idwarehouse = 0, $notrigger = 0)
514 {
515 global $conf, $langs;
516
517 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
518
519 $error = 0;
520
521 // Protection
522 if ($this->status == self::STATUS_VALIDATED) {
523 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
524 return 0;
525 }
526
527 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
528 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
529 $this->error = 'NotEnoughPermissions';
530 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
531 return -1;
532 }
533 if (empty($this->socid)) {
534 $this->error = 'ErrorWrongParameters';
535 return -1;
536 }
537 if (!getDolGlobalBool('ORDER_NOCHECK_ONSALE_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines()) {
538 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
539 return -1;
540 }
541 $now = dol_now();
542
543 $this->db->begin();
544
545 // Definition du nom de module de numerotation de commande
546 $soc = new Societe($this->db);
547 $soc->fetch($this->socid);
548
549 // Class of company linked to order
550 $result = $soc->setAsCustomer();
551
552 // Define new ref
553 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
554 $num = $this->getNextNumRef($soc);
555 } else {
556 $num = (string) $this->ref;
557 }
558 $this->newref = dol_sanitizeFileName($num);
559
560 // Validate
561 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
562 $sql .= " SET ref = '".$this->db->escape($num)."',";
563 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
564 $sql .= " date_valid = '".$this->db->idate($now)."',";
565 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
566 $sql .= " fk_user_modif = ".((int) $user->id);
567 $sql .= " WHERE rowid = ".((int) $this->id);
568
569 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
570 $resql = $this->db->query($sql);
571 if (!$resql) {
572 dol_print_error($this->db);
573 $this->error = $this->db->lasterror();
574 $error++;
575 }
576
577 if (!$error) {
578 // If stock is incremented on validate order, we must increment it
579 if ($result >= 0 && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
580 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
581 $langs->load("agenda");
582
583 // Loop on each line
584 $cpt = count($this->lines);
585 for ($i = 0; $i < $cpt; $i++) {
586 if ($this->lines[$i]->fk_product > 0) {
587 $mouvP = new MouvementStock($this->db);
588 $mouvP->origin = &$this;
589 $mouvP->setOrigin($this->element, $this->id);
590 // We decrement stock of product (and sub-products)
591 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
592 if ($result < 0) {
593 $error++;
594 $this->setErrorsFromObject($mouvP);
595 }
596 }
597 if ($error) {
598 break;
599 }
600 }
601 }
602 }
603
604 if (!$error && !$notrigger) {
605 // Call trigger
606 $result = $this->call_trigger('ORDER_VALIDATE', $user);
607 if ($result < 0) {
608 $error++;
609 }
610 // End call triggers
611 }
612
613 if (!$error) {
614 $this->oldref = $this->ref;
615
616 // Rename directory if dir was a temporary ref
617 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
618 // Now we rename also files into index
619 $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)."'";
620 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
621 $resql = $this->db->query($sql);
622 if (!$resql) {
623 $error++;
624 $this->error = $this->db->lasterror();
625 }
626 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
627 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
628 $resql = $this->db->query($sql);
629 if (!$resql) {
630 $error++;
631 $this->error = $this->db->lasterror();
632 }
633
634 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
635 $oldref = dol_sanitizeFileName($this->ref);
636 $newref = dol_sanitizeFileName($num);
637 $dirsource = getMultidirOutput($this).'/'.$oldref;
638 $dirdest = getMultidirOutput($this).'/'.$newref;
639 if (!$error && file_exists($dirsource)) {
640 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
641
642 if (@rename($dirsource, $dirdest)) {
643 dol_syslog("Rename ok");
644 // Rename docs starting with $oldref with $newref
645 $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
646 foreach ($listoffiles as $fileentry) {
647 $dirsource = $fileentry['name'];
648 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
649 $dirsource = $fileentry['path'].'/'.$dirsource;
650 $dirdest = $fileentry['path'].'/'.$dirdest;
651 @rename($dirsource, $dirdest);
652 }
653 }
654 }
655 }
656 }
657
658 // Set new ref and current status
659 if (!$error) {
660 $this->ref = $num;
661 $this->statut = self::STATUS_VALIDATED; // deprecated
663 }
664
665 if (!$error) {
666 $this->db->commit();
667 return 1;
668 } else {
669 $this->db->rollback();
670 return -1;
671 }
672 }
673
674 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
682 public function setDraft($user, $idwarehouse = -1)
683 {
684 //phpcs:enable
685 global $langs;
686
687 $error = 0;
688
689 // Protection
690 if ($this->status <= self::STATUS_DRAFT && !getDolGlobalInt('ORDER_REOPEN_TO_DRAFT')) {
691 return 0;
692 }
693
694 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
695 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
696 $this->error = 'Permission denied';
697 return -1;
698 }
699
700 dol_syslog(__METHOD__, LOG_DEBUG);
701
702 $this->db->begin();
703
704 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
705 $sql .= " SET fk_statut = ".((int) self::STATUS_DRAFT).",";
706 $sql .= " fk_user_modif = ".((int) $user->id);
707 $sql .= " WHERE rowid = ".((int) $this->id);
708
709 if ($this->db->query($sql)) {
710 $this->oldcopy = clone $this;
711
712 // If stock is decremented on validate order, we must reincrement it
713 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
714 $result = 0;
715
716 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
717 $langs->load("agenda");
718
719 $num = count($this->lines);
720 for ($i = 0; $i < $num; $i++) {
721 if ($this->lines[$i]->fk_product > 0) {
722 $mouvP = new MouvementStock($this->db);
723 $mouvP->origin = &$this;
724 $mouvP->setOrigin($this->element, $this->id);
725 // We increment stock of product (and sub-products)
726 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
727 if ($result < 0) {
728 $error++;
729 $this->setErrorsFromObject($mouvP);
730 break;
731 }
732 }
733 }
734 }
735
736 if (!$error) {
737 // Call trigger
738 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
739 if ($result < 0) {
740 $error++;
741 }
742 }
743
744 if (!$error) {
745 $this->statut = self::STATUS_DRAFT; // deprecated
746 $this->status = self::STATUS_DRAFT;
747 $this->db->commit();
748 return 1;
749 } else {
750 $this->db->rollback();
751 return -1;
752 }
753 } else {
754 $this->error = $this->db->error();
755 $this->db->rollback();
756 return -1;
757 }
758 }
759
760
761 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
769 public function set_reopen($user)
770 {
771 // phpcs:enable
772 $error = 0;
773
774 if ($this->status != self::STATUS_CANCELED && $this->status != self::STATUS_CLOSED) {
775 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
776 return 0;
777 }
778
779 $this->db->begin();
780
781 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
782 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
783 $sql .= " fk_user_modif = ".((int) $user->id);
784 $sql .= " WHERE rowid = ".((int) $this->id);
785
786 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
787 $resql = $this->db->query($sql);
788 if ($resql) {
789 // Call trigger
790 $result = $this->call_trigger('ORDER_REOPEN', $user);
791 if ($result < 0) {
792 $error++;
793 }
794 // End call triggers
795 } else {
796 $error++;
797 $this->error = $this->db->lasterror();
798 dol_print_error($this->db);
799 }
800
801 if (!$error) {
802 $this->statut = self::STATUS_VALIDATED; // deprecated
804 $this->billed = 0;
805
806 $this->db->commit();
807 return 1;
808 } else {
809 foreach ($this->errors as $errmsg) {
810 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
811 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
812 }
813 $this->db->rollback();
814 return -1 * $error;
815 }
816 }
817
825 public function cloture($user, $notrigger = 0)
826 {
827 $error = 0;
828
829 $usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
830 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'close')));
831
832 if ($usercanclose) {
833 if ($this->status == self::STATUS_CLOSED) {
834 return 0;
835 }
836 $this->db->begin();
837
838 $now = dol_now();
839
840 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
841 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
842 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
843 $sql .= " date_cloture = '".$this->db->idate($now)."',";
844 $sql .= " fk_user_modif = ".((int) $user->id);
845 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
846
847 if ($this->db->query($sql)) {
848 if (!$notrigger) {
849 // Call trigger
850 $result = $this->call_trigger('ORDER_CLOSE', $user);
851 if ($result < 0) {
852 $error++;
853 }
854 // End call triggers
855 }
856
857 if (!$error) {
858 $this->statut = self::STATUS_CLOSED; // deprecated
860
861 $this->db->commit();
862 return 1;
863 } else {
864 $this->db->rollback();
865 return -1;
866 }
867 } else {
868 $this->error = $this->db->lasterror();
869
870 $this->db->rollback();
871 return -1;
872 }
873 }
874 return 0;
875 }
876
888 public function setCategories($categories)
889 {
890 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
891 return parent::setCategoriesCommon($categories, Categorie::TYPE_ORDER);
892 }
893
902 public function cancel($user, $idwarehouse = -1)
903 {
904 global $user, $langs;
905
906 $error = 0;
907
908 $this->db->begin();
909
910 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
911 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
912 $sql .= " fk_user_modif = ".((int) $user->id);
913 $sql .= " WHERE rowid = ".((int) $this->id);
914 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
915
916 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
917 if ($this->db->query($sql)) {
918 // If stock is decremented on validate order, we must reincrement it
919 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
920 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
921 $langs->load("agenda");
922
923 $num = count($this->lines);
924 for ($i = 0; $i < $num; $i++) {
925 if ($this->lines[$i]->fk_product > 0) {
926 $mouvP = new MouvementStock($this->db);
927 $mouvP->setOrigin($this->element, $this->id);
928 // We increment stock of product (and sub-products)
929 $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
930 if ($result < 0) {
931 $error++;
932 $this->setErrorsFromObject($mouvP);
933 break;
934 }
935 }
936 }
937 }
938
939 if (!$error) {
940 // Call trigger
941 $result = $this->call_trigger('ORDER_CANCEL', $user);
942 if ($result < 0) {
943 $error++;
944 }
945 // End call triggers
946 }
947
948 if (!$error) {
949 $this->statut = self::STATUS_CANCELED; // deprecated
951 $this->db->commit();
952 return 1;
953 } else {
954 foreach ($this->errors as $errmsg) {
955 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
956 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
957 }
958 $this->db->rollback();
959 return -1 * $error;
960 }
961 } else {
962 $this->error = $this->db->error();
963 $this->db->rollback();
964 return -1;
965 }
966 }
967
976 public function create($user, $notrigger = 0)
977 {
978 global $conf, $langs, $mysoc;
979 $error = 0;
980
981 // Clean parameters
982
983 if (empty($this->socid)) {
984 $this->error = 'ErrorWrongParameters';
985 return -1;
986 }
987
988 // Set tmp vars
989 $date = ($this->date_commande ? $this->date_commande : $this->date);
990 $this->import_key = trim((string) $this->import_key);
991 $delivery_date = $this->delivery_date;
992
993 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
994 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
995 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
996 } else {
997 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
998 }
999 if (empty($this->fk_multicurrency)) {
1000 $this->multicurrency_code = $conf->currency;
1001 $this->fk_multicurrency = 0;
1002 $this->multicurrency_tx = 1;
1003 }
1004 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
1005 $this->entity = setEntity($this);
1006
1007 dol_syslog(get_class($this)."::create user=".$user->id);
1008
1009 // Check parameters
1010 if (!empty($this->ref)) { // We check that ref is not already used
1011 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1012 if ($result > 0) {
1013 $this->error = 'ErrorRefAlreadyExists';
1014 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1015 $this->db->rollback();
1016 return -1;
1017 }
1018 }
1019
1020 $soc = new Societe($this->db);
1021 $result = $soc->fetch($this->socid);
1022 if ($result < 0) {
1023 $this->error = "Failed to fetch company";
1024 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1025 return -2;
1026 }
1027 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
1028 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
1029 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1030 return -1;
1031 }
1032
1033 $this->date_creation = dol_now();
1034
1035 $this->db->begin();
1036
1037 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
1038 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
1039 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
1040 $sql .= ", fk_shipping_method";
1041 $sql .= ", fk_warehouse";
1042 $sql .= ", fk_incoterms, location_incoterms";
1043 $sql .= ", entity, module_source, pos_source";
1044 $sql .= ", fk_multicurrency";
1045 $sql .= ", multicurrency_code";
1046 $sql .= ", multicurrency_tx";
1047 $sql .= ", import_key";
1048 $sql .= ")";
1049 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($this->date_creation)."', ".($user->id > 0 ? ((int) $user->id) : "null");
1050 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1051 $sql .= ", '".$this->db->idate($date)."'";
1052 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape((string) $this->source) : 'null');
1053 $sql .= ", '".$this->db->escape($this->note_private)."'";
1054 $sql .= ", '".$this->db->escape($this->note_public)."'";
1055 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
1056 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1057 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1058 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1059 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1060 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1061 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1062 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1063 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1064 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1065 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1066 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1067 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1068 $sql .= ", ".(int) $this->fk_incoterms;
1069 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1070 $sql .= ", ".(int) $this->entity;
1071 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1072 $sql .= ", ".((!is_null($this->pos_source) && $this->pos_source != '') ? "'".$this->db->escape($this->pos_source)."'" : "null"); // Can be null, '', '0', '1'
1073 $sql .= ", ".(int) $this->fk_multicurrency;
1074 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1075 $sql .= ", ".(float) $this->multicurrency_tx;
1076 $sql .= ", '".$this->db->escape($this->import_key)."'";
1077 $sql .= ")";
1078
1079 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1080 $resql = $this->db->query($sql);
1081 if ($resql) {
1082 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1083
1084 if ($this->id) {
1085 $fk_parent_line = 0;
1086 $num = count($this->lines);
1087
1088 /*
1089 * Insert products details into db
1090 */
1091 for ($i = 0; $i < $num; $i++) {
1092 $line = $this->lines[$i];
1093
1094 // Test and convert into OrderLine object this->lines[$i]. When coming from REST API, we may still have an array
1095 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1096 if (!is_object($line)) {
1097 $lineobj = new OrderLine($this->db);
1098 foreach ($line as $key => $val) {
1099 $lineobj->$key = $val;
1100 }
1101 $line = $lineobj;
1102 $this->lines[$i] = $line;
1103 }
1104
1105 // Reset fk_parent_line for no child products and special product
1106 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1107 $fk_parent_line = 0;
1108 }
1109
1110 // Complete vat rate with code
1111 $vatrate = $line->tva_tx;
1112 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1113 $vatrate .= ' ('.$line->vat_src_code.')';
1114 }
1115
1116 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1117 $originid = $line->origin_id;
1118 $origintype = empty($line->origin_type) ? $line->origin : $line->origin_type;
1119 } else { // old but bugged version (we store id of line and type of parent object)
1120 $originid = $line->id;
1121 $origintype = $this->element;
1122 }
1123
1124 // ref_ext
1125 if (empty($line->ref_ext)) {
1126 $line->ref_ext = '';
1127 }
1128
1129 $result = $this->addline(
1130 $line->desc,
1131 $line->subprice,
1132 $line->qty,
1133 $vatrate,
1134 $line->localtax1_tx,
1135 $line->localtax2_tx,
1136 $line->fk_product,
1137 $line->remise_percent,
1138 $line->info_bits,
1139 $line->fk_remise_except,
1140 'HT',
1141 0,
1142 $line->date_start,
1143 $line->date_end,
1144 $line->product_type,
1145 $line->rang,
1146 $line->special_code,
1147 $fk_parent_line,
1148 $line->fk_fournprice,
1149 $line->pa_ht,
1150 $line->label,
1151 $line->array_options,
1152 $line->fk_unit,
1153 (string) $origintype,
1154 $originid,
1155 0,
1156 $line->ref_ext,
1157 1
1158 );
1159
1160 if ($result < 0) {
1161 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1162 $this->error = $this->db->lasterror();
1163 $this->errors[] = $this->error;
1164 dol_print_error($this->db);
1165 }
1166 $this->db->rollback();
1167 return -1;
1168 }
1169 // Defined the new fk_parent_line
1170 if ($result > 0 && $line->product_type == 9) {
1171 $fk_parent_line = $result;
1172 }
1173 }
1174
1175 $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.
1176
1177 // update ref
1178 $initialref = '(PROV'.$this->id.')';
1179 if (!empty($this->ref)) {
1180 $initialref = $this->ref;
1181 }
1182
1183 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1184 if ($this->db->query($sql)) {
1185 $this->ref = $initialref;
1186
1187 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1188 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1189 }
1190
1191 // Add object linked
1192 if (!empty($this->linked_objects) && is_array($this->linked_objects)) {
1193 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1194 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, ...))
1195 foreach ($tmp_origin_id as $origin_id) {
1196 $ret = $this->add_object_linked($origin, $origin_id);
1197 if (!$ret) {
1198 $this->error = $this->db->lasterror();
1199 $error++;
1200 }
1201 }
1202 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1203 $origin_id = $tmp_origin_id;
1204 $ret = $this->add_object_linked($origin, $origin_id);
1205 if (!$ret) {
1206 $this->error = $this->db->lasterror();
1207 $error++;
1208 }
1209 }
1210 }
1211 }
1212
1213 if (!$error && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1214 $originforcontact = empty($this->origin_type) ? $this->origin : $this->origin_type;
1215 $originidforcontact = $this->origin_id;
1216 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1217 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1218 $exp = new Expedition($this->db);
1219 $exp->fetch($this->origin_id);
1220 $exp->fetchObjectLinked();
1221 if (count($exp->linkedObjectsIds['commande']) > 0) {
1222 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1223 $originforcontact = 'commande';
1224 if (is_object($value)) {
1225 $originidforcontact = $value->id;
1226 } else {
1227 $originidforcontact = $value;
1228 }
1229 break; // We take first one
1230 }
1231 }
1232 }
1233
1234 $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";
1235 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1236
1237 $resqlcontact = $this->db->query($sqlcontact);
1238 if ($resqlcontact) {
1239 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1240 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1241 $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
1242 }
1243 } else {
1244 dol_print_error($this->db);
1245 }
1246 }
1247
1248 if (!$error) {
1249 $result = $this->insertExtraFields();
1250 if ($result < 0) {
1251 $error++;
1252 }
1253 }
1254
1255 if (!$error && !$notrigger) {
1256 // Call trigger
1257 $result = $this->call_trigger('ORDER_CREATE', $user);
1258 if ($result < 0) {
1259 $error++;
1260 }
1261 // End call triggers
1262 }
1263
1264 if (!$error) {
1265 $this->db->commit();
1266 return $this->id;
1267 } else {
1268 $this->db->rollback();
1269 return -1 * $error;
1270 }
1271 } else {
1272 $this->error = $this->db->lasterror();
1273 $this->db->rollback();
1274 return -1;
1275 }
1276 }
1277
1278 return 0;
1279 } else {
1280 $this->error = $this->db->lasterror();
1281 $this->db->rollback();
1282 return -1;
1283 }
1284 }
1285
1286
1295 public function createFromClone(User $user, $socid = 0, $forceentity = null)
1296 {
1297 global $hookmanager;
1298
1299 $error = 0;
1300
1301 $object = new self($this->db);
1302
1303 $this->db->begin();
1304
1305 // Load source object
1306 $object->fetch($this->id);
1307
1308 $objFrom = clone $object;
1309
1310 // Change socid if needed
1311 if (!empty($socid) && $socid != $object->socid) {
1312 $objsoc = new Societe($this->db);
1313
1314 if ($objsoc->fetch($socid) > 0) {
1315 $object->socid = $objsoc->id;
1316 $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1317 $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : '0');
1318 $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1319 $object->fk_project = 0;
1320 $object->fk_delivery_address = 0;
1321 }
1322
1323 // TODO Change product price if multi-prices
1324 }
1325
1326 $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1327
1328 $object->id = 0;
1329 $object->ref = '';
1330 $object->statut = self::STATUS_DRAFT; // deprecated
1331 $object->status = self::STATUS_DRAFT;
1332
1333 // Clear fields
1334 $object->user_author_id = $user->id;
1335 $object->user_validation_id = 0;
1336 $object->date = dol_now();
1337 $object->date_commande = dol_now();
1338 $object->date_creation = '';
1339 $object->date_validation = '';
1340 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1341 $object->ref_client = '';
1342 $object->ref_customer = '';
1343 }
1344
1345 // Do not clone ref_ext
1346 $num = count($object->lines);
1347 for ($i = 0; $i < $num; $i++) {
1348 $object->lines[$i]->ref_ext = '';
1349 }
1350
1351 // Create clone
1352 $object->context['createfromclone'] = 'createfromclone';
1353 $result = $object->create($user);
1354 if ($result < 0) {
1355 $error++;
1356 $this->setErrorsFromObject($object);
1357 }
1358
1359 if (!$error) {
1360 // copy internal contacts
1361 if ($object->copy_linked_contact($objFrom, 'internal') < 0) {
1362 $error++;
1363 }
1364 }
1365
1366 if (!$error) {
1367 // copy external contacts if same company
1368 if ($object->socid == $objFrom->socid) {
1369 if ($object->copy_linked_contact($objFrom, 'external') < 0) {
1370 $error++;
1371 }
1372 }
1373 }
1374
1375 if (!$error) {
1376 // Hook of thirdparty module
1377 if (is_object($hookmanager)) {
1378 $parameters = array('objFrom' => $objFrom, 'clonedObj' => $object);
1379 $action = '';
1380 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1381 if ($reshook < 0) {
1382 $this->setErrorsFromObject($hookmanager);
1383 $error++;
1384 }
1385 }
1386 }
1387
1388 unset($object->context['createfromclone']);
1389
1390 // End
1391 if (!$error) {
1392 $this->db->commit();
1393 return $object->id;
1394 } else {
1395 $this->db->rollback();
1396 return -1;
1397 }
1398 }
1399
1400
1408 public function createFromProposal($object, User $user)
1409 {
1410 global $conf, $hookmanager, $langs;
1411
1412 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1413 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1414
1415 $error = 0;
1416
1417 $this->date_commande = dol_now();
1418 $this->date = dol_now();
1419 $this->source = 0;
1420
1421 $num = count($object->lines);
1422 for ($i = 0; $i < $num; $i++) {
1423 $line = new OrderLine($this->db);
1424
1425 $line->libelle = $object->lines[$i]->libelle;
1426 $line->label = $object->lines[$i]->label;
1427 $line->desc = $object->lines[$i]->desc;
1428 $line->price = $object->lines[$i]->price;
1429 $line->subprice = $object->lines[$i]->subprice;
1430 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1431 $line->tva_tx = $object->lines[$i]->tva_tx;
1432 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1433 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1434 $line->qty = $object->lines[$i]->qty;
1435 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1436 $line->remise_percent = $object->lines[$i]->remise_percent;
1437 $line->fk_product = $object->lines[$i]->fk_product;
1438 $line->info_bits = $object->lines[$i]->info_bits;
1439 $line->product_type = $object->lines[$i]->product_type;
1440 $line->rang = $object->lines[$i]->rang;
1441 $line->special_code = $object->lines[$i]->special_code;
1442 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1443 $line->fk_unit = $object->lines[$i]->fk_unit;
1444
1445 $line->date_start = $object->lines[$i]->date_start;
1446 $line->date_end = $object->lines[$i]->date_end;
1447
1448 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1449 $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);
1450 $line->pa_ht = $marginInfos[0];
1451 $line->marge_tx = $marginInfos[1];
1452 $line->marque_tx = $marginInfos[2];
1453
1454 $line->origin = $object->element;
1455 $line->origin_id = $object->lines[$i]->id;
1456
1457 // get extrafields from original line
1458 $object->lines[$i]->fetch_optionals();
1459 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1460 $line->array_options[$options_key] = $value;
1461 }
1462
1463 $this->lines[$i] = $line;
1464 }
1465
1466 $this->entity = $object->entity;
1467 $this->socid = $object->socid;
1468 $this->fk_project = $object->fk_project;
1469 $this->cond_reglement_id = $object->cond_reglement_id;
1470 $this->deposit_percent = $object->deposit_percent;
1471 $this->mode_reglement_id = $object->mode_reglement_id;
1472 $this->fk_account = $object->fk_account;
1473 $this->fk_incoterms = $object->fk_incoterms;
1474 $this->location_incoterms = $object->location_incoterms;
1475 $this->availability_id = $object->availability_id;
1476 $this->demand_reason_id = $object->demand_reason_id;
1477 $this->delivery_date = $object->delivery_date;
1478 $this->shipping_method_id = $object->shipping_method_id;
1479 $this->warehouse_id = $object->warehouse_id;
1480 $this->fk_delivery_address = $object->fk_delivery_address;
1481 $this->contact_id = $object->contact_id;
1482 $this->ref_client = $object->ref_client;
1483 $this->ref_customer = $object->ref_client;
1484
1485 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1486 $this->note_private = $object->note_private;
1487 $this->note_public = $object->note_public;
1488 }
1489
1490 $this->origin = $object->element;
1491 $this->origin_id = $object->id;
1492
1493 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1494 if (isModEnabled('multicurrency')) {
1495 if (!empty($object->multicurrency_code)) {
1496 $this->multicurrency_code = $object->multicurrency_code;
1497 }
1498 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1499 $this->multicurrency_tx = $object->multicurrency_tx;
1500 }
1501
1502 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1503 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1504 $this->fk_multicurrency = $tmparray[0];
1505 $this->multicurrency_tx = $tmparray[1];
1506 } else {
1507 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1508 }
1509 if (empty($this->fk_multicurrency)) {
1510 $this->multicurrency_code = $conf->currency;
1511 $this->fk_multicurrency = 0;
1512 $this->multicurrency_tx = 1;
1513 }
1514 }
1515
1516 // get extrafields from original line
1517 $object->fetch_optionals();
1518
1519 $e = new ExtraFields($this->db);
1520 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1521
1522 foreach ($object->array_options as $options_key => $value) {
1523 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1524 $this->array_options[$options_key] = $value;
1525 }
1526 }
1527 // Possibility to add external linked objects with hooks
1528 $this->linked_objects[$this->origin] = $this->origin_id;
1529 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1530 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1531 }
1532
1533 $ret = $this->create($user);
1534
1535 if ($ret > 0) {
1536 // Actions hooked (by external module)
1537 $hookmanager->initHooks(array('orderdao'));
1538
1539 $parameters = array('objFrom' => $object);
1540 $action = '';
1541 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1542 if ($reshook < 0) {
1543 $this->setErrorsFromObject($hookmanager);
1544 $error++;
1545 }
1546
1547 if (!$error) {
1548 // Validate immediately the order
1549 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1550 $this->fetch($ret);
1551 $result = $this->valid($user);
1552 if ($result > 0 && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1553 $this->fetch($this->id);
1554 $this->fetch_thirdparty();
1555
1556 $outputlangs = $langs;
1557 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($this->thirdparty->default_lang)) {
1558 $outputlangs = new Translate('', $conf);
1559 $outputlangs->setDefaultLang($this->thirdparty->default_lang);
1560 $outputlangs->loadLangs(array('main', 'orders'));
1561 }
1562
1563 $this->generateDocument($this->model_pdf, $outputlangs);
1564 }
1565 }
1566 return $ret;
1567 } else {
1568 return -1;
1569 }
1570 } else {
1571 return -1;
1572 }
1573 }
1574
1575
1615 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)
1616 {
1617 global $mysoc, $langs, $user;
1618
1619 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1620 $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";
1621 $logtext .= ", date_end=$date_end, type=$type special_code=$special_code, fk_unit=$fk_unit, origin=$origin, origin_id=$origin_id, pu_ht_devise=$pu_ht_devise, ref_ext=$ref_ext rang=$rang";
1622 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1623
1624 if ($this->status == self::STATUS_DRAFT) {
1625 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1626
1627 // Clean parameters
1628
1629 if (empty($remise_percent)) {
1630 $remise_percent = 0;
1631 }
1632 if (empty($qty)) {
1633 $qty = 0;
1634 }
1635 if (empty($info_bits)) {
1636 $info_bits = 0;
1637 }
1638 if (empty($rang)) {
1639 $rang = 0;
1640 }
1641 if (empty($txtva)) {
1642 $txtva = 0;
1643 }
1644 if (empty($txlocaltax1)) {
1645 $txlocaltax1 = 0;
1646 }
1647 if (empty($txlocaltax2)) {
1648 $txlocaltax2 = 0;
1649 }
1650 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1651 $fk_parent_line = 0;
1652 }
1653 if (empty($this->fk_multicurrency)) {
1654 $this->fk_multicurrency = 0;
1655 }
1656 if (empty($ref_ext)) {
1657 $ref_ext = '';
1658 }
1659
1660 $remise_percent = (float) price2num($remise_percent);
1661 $qty = (float) price2num($qty);
1662 $pu_ht = price2num($pu_ht);
1663 $pu_ht_devise = price2num($pu_ht_devise);
1664 $pu_ttc = price2num($pu_ttc);
1665 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
1666 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1667 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1668 }
1669 $txlocaltax1 = price2num($txlocaltax1);
1670 $txlocaltax2 = price2num($txlocaltax2);
1671 if ($price_base_type == 'HT') {
1672 $pu = $pu_ht;
1673 } else {
1674 $pu = $pu_ttc;
1675 }
1676 $label = trim($label);
1677 $desc = trim($desc);
1678
1679 // Check parameters
1680 if ($type < 0) {
1681 return -1;
1682 }
1683
1684 if ($date_start && $date_end && $date_start > $date_end) {
1685 $langs->load("errors");
1686 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1687 return -1;
1688 }
1689
1690 $this->db->begin();
1691
1692 $product_type = $type;
1693 if (!empty($fk_product) && $fk_product > 0) {
1694 $product = new Product($this->db);
1695 $result = $product->fetch($fk_product);
1696 $product_type = $product->type;
1697
1698 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product->isStockManaged()) {
1699 // get real stock
1700 $productChildrenNb = 0;
1701 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
1702 $productChildrenNb = $product->hasFatherOrChild(1);
1703 }
1704 if ($productChildrenNb > 0) {
1705 // compute real stock from each subcomponent
1706 $product_stock = null;
1707 $product->loadStockForVirtualProduct('warehouseopen', $qty);
1708 foreach ($product->stock_warehouse as $componentStockWarehouse) {
1709 if ($product_stock === null) {
1710 $product_stock = $componentStockWarehouse->real;
1711 } else {
1712 $product_stock = min($product_stock, $componentStockWarehouse->real);
1713 }
1714 }
1715 if ($product_stock === null) {
1716 $product_stock = 0;
1717 }
1718 } else {
1719 $product_stock = $product->stock_reel;
1720 }
1721
1722 if ($product_stock < $qty) {
1723 $langs->load("errors");
1724 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1725 $this->errors[] = $this->error;
1726 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1727 $this->db->rollback();
1729 }
1730 }
1731 }
1732 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
1733 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
1734 // and this is done at the line level, which has its own VAT rate
1735
1736 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1737
1738 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1739 $tmpproduct = new Product($this->db);
1740 $result = $tmpproduct->fetch($fk_product);
1741 if (abs((float) $qty) < $tmpproduct->packaging) {
1742 $qty = (float) $tmpproduct->packaging;
1743 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'warnings');
1744 } else {
1745 if (!empty($tmpproduct->packaging) && (float) price2num(fmod((float) $qty, (float) $tmpproduct->packaging), 'MS')) {
1746 $coeff = intval(abs((float) $qty) / $tmpproduct->packaging) + 1;
1747 $qty = price2num((float) $tmpproduct->packaging * $coeff, 'MS');
1748 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'warnings');
1749 }
1750 }
1751 }
1752
1753 // Clean vat code
1754 $reg = array();
1755 $vat_src_code = '';
1756 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1757 $vat_src_code = $reg[1];
1758 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1759 }
1760
1761 $tabprice = calcul_price_total($qty, (float) $pu, $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_ht_devise);
1762
1763 /*var_dump($txlocaltax1);
1764 var_dump($txlocaltax2);
1765 var_dump($localtaxes_type);
1766 var_dump($tabprice);
1767 var_dump($tabprice[9]);
1768 var_dump($tabprice[10]);
1769 exit;*/
1770
1771 $total_ht = $tabprice[0];
1772 $total_tva = $tabprice[1];
1773 $total_ttc = $tabprice[2];
1774 $total_localtax1 = $tabprice[9];
1775 $total_localtax2 = $tabprice[10];
1776 $pu_ht = $tabprice[3];
1777
1778 // MultiCurrency
1779 $multicurrency_total_ht = $tabprice[16];
1780 $multicurrency_total_tva = $tabprice[17];
1781 $multicurrency_total_ttc = $tabprice[18];
1782 $pu_ht_devise = $tabprice[19];
1783
1784 // Rang to use
1785 $ranktouse = $rang;
1786
1787 if (empty($ranktouse) || $ranktouse == -1) {
1788 $rangmax = $this->line_max($fk_parent_line);
1789 $ranktouse = $rangmax + 1;
1790 }
1791
1792 // Insert line
1793 $this->line = new OrderLine($this->db);
1794
1795 $this->line->context = $this->context;
1796
1797 $this->line->fk_commande = $this->id;
1798 $this->line->label = $label;
1799 $this->line->desc = $desc;
1800 $this->line->qty = (float) $qty;
1801 $this->line->ref_ext = $ref_ext;
1802
1803 $this->line->vat_src_code = $vat_src_code;
1804 $this->line->tva_tx = $txtva;
1805 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1806 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1807 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1808 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1809 $this->line->fk_product = $fk_product;
1810 $this->line->product_type = $product_type;
1811 $this->line->fk_remise_except = $fk_remise_except;
1812 $this->line->remise_percent = $remise_percent;
1813 $this->line->subprice = (float) $pu_ht;
1814 $this->line->rang = $ranktouse;
1815 $this->line->info_bits = $info_bits;
1816 $this->line->total_ht = (float) $total_ht;
1817 $this->line->total_tva = (float) $total_tva;
1818 $this->line->total_localtax1 = (float) $total_localtax1;
1819 $this->line->total_localtax2 = (float) $total_localtax2;
1820 $this->line->total_ttc = (float) $total_ttc;
1821 $this->line->special_code = $special_code;
1822 if (empty($qty) && empty($special_code)) {
1823 $this->line->special_code = 3;
1824 }
1825 $this->line->origin = $origin;
1826 $this->line->origin_type = $origin;
1827 $this->line->origin_id = $origin_id;
1828
1829 $this->line->fk_parent_line = $fk_parent_line;
1830 $this->line->fk_unit = $fk_unit;
1831
1832 $this->line->date_start = $date_start;
1833 $this->line->date_end = $date_end;
1834
1835 $this->line->fk_fournprice = $fk_fournprice;
1836 $this->line->pa_ht = $pa_ht; // Can be '' when not defined or 0 if defined to 0 or a price value
1837
1838 // Multicurrency
1839 $this->line->fk_multicurrency = $this->fk_multicurrency;
1840 $this->line->multicurrency_code = $this->multicurrency_code;
1841 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
1842 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
1843 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
1844 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
1845
1846 if (is_array($array_options) && count($array_options) > 0) {
1847 $this->line->array_options = $array_options;
1848 }
1849
1850 $result = $this->line->insert($user);
1851 if ($result > 0) {
1852 // Update denormalized fields at the order level
1853 if (empty($noupdateafterinsertline)) {
1854 $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.
1855 }
1856
1857 if ($result > 0) {
1858 if (!isset($this->context['createfromclone'])) {
1859 if (!empty($fk_parent_line)) {
1860 // Always reorder if child line
1861 $this->line_order(true, 'DESC');
1862 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1863 // Update all rank of all other lines starting from the same $ranktouse
1864 foreach ($this->lines as $tmpline) {
1865 if ($tmpline->rang >= $ranktouse) {
1866 if (!empty($tmpline->id)) {
1867 $this->updateRangOfLine($tmpline->id, $tmpline->rang + 1);
1868 }
1869 }
1870 }
1871 }
1872
1873 $this->lines[] = $this->line;
1874 } else {
1875 // Loop on all lines of parent object
1876 foreach ($this->lines as $tmpline) {
1877 if ($tmpline->id == $origin_id && $tmpline->element = $origin) {
1878 $this->line->extraparams = $tmpline->extraparams;
1879 $this->line->setExtraParameters();
1880 }
1881 }
1882 }
1883
1884 $this->db->commit();
1885 return $this->line->id;
1886 } else {
1887 $this->db->rollback();
1888 return -1;
1889 }
1890 } else {
1891 $this->setErrorsFromObject($this->line);
1892 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1893 $this->db->rollback();
1894 return -2;
1895 }
1896 } else {
1897 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1898 return -3;
1899 }
1900 }
1901
1902
1903 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1917 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1918 {
1919 // phpcs:enable
1920 global $conf, $mysoc;
1921
1922 if (!$qty) {
1923 $qty = 1;
1924 }
1925
1926 if ($idproduct > 0) {
1927 $prod = new Product($this->db);
1928 $prod->fetch($idproduct);
1929
1930 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1931 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1932 if (empty($tva_tx)) {
1933 $tva_npr = 0;
1934 }
1935 $vat_src_code = ''; // May be defined into tva_tx
1936
1937 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1938 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1939
1940 // multiprix
1941 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
1942 $price = $prod->multiprices[$this->thirdparty->price_level];
1943 $price_ttc = $prod->multiprices_ttc[$this->thirdparty->price_level];
1944 } else {
1945 $price = $prod->price;
1946 $price_ttc = $prod->price_ttc;
1947 }
1948
1949 $line = new OrderLine($this->db);
1950
1951 $line->context = $this->context;
1952
1953 $line->fk_product = $idproduct;
1954 $line->desc = $prod->description;
1955 $line->qty = $qty;
1956 $line->subprice = $price;
1957 $line->subprice_ttc = $price_ttc;
1958 $line->remise_percent = $remise_percent;
1959 $line->vat_src_code = $vat_src_code;
1960 $line->tva_tx = $tva_tx;
1961 $line->localtax1_tx = $localtax1_tx;
1962 $line->localtax2_tx = $localtax2_tx;
1963
1964 $line->product_ref = $prod->ref;
1965 $line->product_label = $prod->label;
1966 $line->product_desc = $prod->description;
1967 $line->fk_unit = $prod->fk_unit;
1968
1969 // Save the start and end date of the line in the object
1970 if ($date_start) {
1971 $line->date_start = $date_start;
1972 }
1973 if ($date_end) {
1974 $line->date_end = $date_end;
1975 }
1976
1977 $this->lines[] = $line;
1978
1996 }
1997 }
1998
1999
2009 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
2010 {
2011 // Check parameters
2012 if (empty($id) && empty($ref) && empty($ref_ext)) {
2013 return -1;
2014 }
2015
2016 $sql = 'SELECT c.rowid, c.entity, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_user_modif, c.fk_statut as status';
2017 $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';
2018 $sql .= ', c.fk_account';
2019 $sql .= ', c.date_commande, c.date_valid, c.tms';
2020 $sql .= ', c.date_livraison as delivery_date';
2021 $sql .= ', c.fk_shipping_method';
2022 $sql .= ', c.fk_warehouse';
2023 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
2024 $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';
2025 $sql .= ', c.fk_incoterms, c.location_incoterms';
2026 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
2027 $sql .= ", c.module_source, c.pos_source";
2028 $sql .= ", i.libelle as label_incoterms";
2029 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
2030 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
2031 $sql .= ', ca.code as availability_code, ca.label as availability_label';
2032 $sql .= ', dr.code as demand_reason_code';
2033 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
2034 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
2035 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
2036 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
2037 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
2038 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
2039
2040 if ($id) {
2041 $sql .= " WHERE c.rowid = ".((int) $id);
2042 } else {
2043 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
2044 }
2045
2046 if ($ref) {
2047 $sql .= " AND c.ref = '".$this->db->escape($ref)."'";
2048 }
2049 if ($ref_ext) {
2050 $sql .= " AND c.ref_ext = '".$this->db->escape($ref_ext)."'";
2051 }
2052
2053 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2054 $result = $this->db->query($sql);
2055 if ($result) {
2056 $obj = $this->db->fetch_object($result);
2057 if ($obj) {
2058 $this->id = $obj->rowid;
2059 $this->entity = $obj->entity;
2060
2061 $this->ref = $obj->ref;
2062 $this->ref_client = $obj->ref_client;
2063 $this->ref_customer = $obj->ref_client;
2064 $this->ref_ext = $obj->ref_ext;
2065
2066 $this->socid = $obj->fk_soc;
2067 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
2068
2069 $this->fk_project = $obj->fk_project;
2070 $this->project = null; // Clear if another value was already set by fetch_projet
2071
2072 $this->statut = $obj->status; // deprecated
2073 $this->status = $obj->status;
2074
2075 $this->user_author_id = $obj->fk_user_author;
2076 $this->user_creation_id = $obj->fk_user_author;
2077 $this->user_validation_id = $obj->fk_user_valid;
2078 $this->user_modification_id = $obj->fk_user_modif;
2079 $this->total_ht = $obj->total_ht;
2080 $this->total_tva = $obj->total_tva;
2081 $this->total_localtax1 = $obj->total_localtax1;
2082 $this->total_localtax2 = $obj->total_localtax2;
2083 $this->total_ttc = $obj->total_ttc;
2084 $this->date = $this->db->jdate($obj->date_commande);
2085 $this->date_commande = $this->db->jdate($obj->date_commande);
2086 $this->date_creation = $this->db->jdate($obj->date_creation);
2087 $this->date_validation = $this->db->jdate($obj->date_valid);
2088 $this->date_modification = $this->db->jdate($obj->tms);
2089 $this->source = $obj->source;
2090 $this->billed = $obj->billed;
2091 $this->note = $obj->note_private; // deprecated
2092 $this->note_private = $obj->note_private;
2093 $this->note_public = $obj->note_public;
2094 $this->model_pdf = $obj->model_pdf;
2095 $this->last_main_doc = $obj->last_main_doc;
2096 $this->mode_reglement_id = $obj->fk_mode_reglement;
2097 $this->mode_reglement_code = $obj->mode_reglement_code;
2098 $this->mode_reglement = $obj->mode_reglement_libelle;
2099 $this->cond_reglement_id = $obj->fk_cond_reglement;
2100 $this->cond_reglement_code = $obj->cond_reglement_code;
2101 $this->cond_reglement = $obj->cond_reglement_libelle;
2102 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
2103 $this->deposit_percent = $obj->deposit_percent;
2104 $this->fk_account = $obj->fk_account;
2105 $this->availability_id = $obj->fk_availability;
2106 $this->availability_code = $obj->availability_code;
2107 $this->availability = $obj->availability_label;
2108 $this->demand_reason_id = $obj->fk_input_reason;
2109 $this->demand_reason_code = $obj->demand_reason_code;
2110 $this->delivery_date = $this->db->jdate($obj->delivery_date);
2111 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
2112 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
2113 $this->fk_delivery_address = $obj->fk_delivery_address;
2114 $this->module_source = $obj->module_source;
2115 $this->pos_source = $obj->pos_source;
2116
2117 //Incoterms
2118 $this->fk_incoterms = $obj->fk_incoterms;
2119 $this->location_incoterms = $obj->location_incoterms;
2120 $this->label_incoterms = $obj->label_incoterms;
2121
2122 // Multicurrency
2123 $this->fk_multicurrency = $obj->fk_multicurrency;
2124 $this->multicurrency_code = $obj->multicurrency_code;
2125 $this->multicurrency_tx = $obj->multicurrency_tx;
2126 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
2127 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
2128 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2129
2130 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2131
2132 $this->lines = array();
2133
2134 // Retrieve all extrafield
2135 // fetch optionals attributes and labels
2136 $this->fetch_optionals();
2137
2138 $this->db->free($result);
2139
2140 // Lines
2141 $result = $this->fetch_lines();
2142 if ($result < 0) {
2143 return -3;
2144 }
2145 return 1;
2146 } else {
2147 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2148 return 0;
2149 }
2150 } else {
2151 $this->error = $this->db->error();
2152 return -1;
2153 }
2154 }
2155
2156
2157 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2164 public function insert_discount($idremise)
2165 {
2166 // phpcs:enable
2167 global $langs;
2168
2169 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2170 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2171
2172 $this->db->begin();
2173
2174 $remise = new DiscountAbsolute($this->db);
2175 $result = $remise->fetch($idremise);
2176
2177 if ($result > 0) {
2178 if ($remise->fk_facture) { // Protection against multiple submissions
2179 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2180 $this->db->rollback();
2181 return -5;
2182 }
2183
2184 $line = new OrderLine($this->db);
2185
2186 $line->fk_commande = $this->id;
2187 $line->fk_remise_except = $remise->id;
2188 $line->desc = $remise->description; // Description for the order line
2189 $line->vat_src_code = $remise->vat_src_code;
2190 $line->tva_tx = $remise->tva_tx;
2191 $line->localtax1_tx = $remise->localtax1_tx;
2192 $line->localtax1_type = $remise->localtax1_type;
2193 $line->localtax2_tx = $remise->localtax1_tx;
2194 $line->localtax2_type = $remise->localtax1_type;
2195 $line->subprice = -(float) $remise->amount_ht;
2196 $line->price = -(float) $remise->amount_ht;
2197 $line->fk_product = 0; // Predefined Product ID
2198 $line->qty = 1;
2199 $line->remise_percent = 0;
2200 $line->rang = -1;
2201 $line->info_bits = 2;
2202
2203 $line->total_ht = -(float) $remise->amount_ht;
2204 $line->total_tva = -(float) $remise->amount_tva;
2205 $line->total_ttc = -(float) $remise->amount_ttc;
2206 $line->total_localtax1 = -(float) $remise->total_localtax1;
2207 $line->total_localtax2 = -(float) $remise->total_localtax2;
2208
2209 $result = $line->insert();
2210 if ($result > 0) {
2211 $result = $this->update_price(1);
2212 if ($result > 0) {
2213 $this->db->commit();
2214 return 1;
2215 } else {
2216 $this->db->rollback();
2217 return -1;
2218 }
2219 } else {
2220 $this->setErrorsFromObject($line);
2221 $this->db->rollback();
2222 return -2;
2223 }
2224 } else {
2225 $this->db->rollback();
2226 return -2;
2227 }
2228 }
2229
2230
2231 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2239 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2240 {
2241 // phpcs:enable
2242 $this->lines = array();
2243
2244 $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,';
2245 $sql .= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.fk_remise_except, l.remise_percent, l.subprice, l.subprice_ttc, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht, l.rang, l.info_bits, l.special_code,';
2246 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2247 $sql .= ' l.fk_unit, l.extraparams,';
2248 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_subprice_ttc, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2249 $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,';
2250 $sql .= ' p.customcode, p.fk_country as country_id, c.code as country_code,';
2251 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units, p.packaging';
2252 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2253 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2254 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON c.rowid = p.fk_country';
2255 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2256 if ($only_product) {
2257 $sql .= ' AND p.fk_product_type = 0';
2258 }
2259 $sql .= ' ORDER BY l.rang, l.rowid';
2260
2261 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2262 $result = $this->db->query($sql);
2263 if ($result) {
2264 $num = $this->db->num_rows($result);
2265
2266 $i = 0;
2267 while ($i < $num) {
2268 $objp = $this->db->fetch_object($result);
2269
2270 $line = new OrderLine($this->db);
2271
2272 $line->rowid = $objp->rowid;
2273 $line->id = $objp->rowid;
2274 $line->fk_commande = $objp->fk_commande;
2275 $line->commande_id = $objp->fk_commande;
2276 $line->label = $objp->custom_label;
2277 $line->desc = $objp->description;
2278 $line->description = $objp->description; // Description line
2279 $line->product_type = $objp->product_type;
2280 $line->qty = $objp->qty;
2281 $line->ref_ext = $objp->ref_ext;
2282
2283 $line->vat_src_code = $objp->vat_src_code;
2284 $line->tva_tx = $objp->tva_tx;
2285 $line->localtax1_tx = $objp->localtax1_tx;
2286 $line->localtax2_tx = $objp->localtax2_tx;
2287 $line->localtax1_type = $objp->localtax1_type;
2288 $line->localtax2_type = $objp->localtax2_type;
2289 $line->total_ht = $objp->total_ht;
2290 $line->total_ttc = $objp->total_ttc;
2291 $line->total_tva = $objp->total_tva;
2292 $line->total_localtax1 = $objp->total_localtax1;
2293 $line->total_localtax2 = $objp->total_localtax2;
2294 $line->subprice = (float) $objp->subprice;
2295 $line->subprice_ttc = (float) $objp->subprice_ttc;
2296 $line->fk_remise_except = $objp->fk_remise_except;
2297 $line->remise_percent = $objp->remise_percent;
2298 $line->price = $objp->price;
2299 $line->fk_product = $objp->fk_product;
2300 $line->fk_fournprice = $objp->fk_fournprice;
2301 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2302 $line->pa_ht = $marginInfos[0];
2303 $line->marge_tx = $marginInfos[1];
2304 $line->marque_tx = $marginInfos[2];
2305 $line->rang = $objp->rang;
2306 $line->info_bits = $objp->info_bits;
2307 $line->special_code = $objp->special_code;
2308 $line->fk_parent_line = $objp->fk_parent_line;
2309
2310 $line->ref = $objp->product_ref;
2311 $line->libelle = $objp->product_label;
2312
2313 $line->product_ref = $objp->product_ref;
2314 $line->product_label = $objp->product_label;
2315 $line->product_tosell = $objp->product_tosell;
2316 $line->product_tobuy = $objp->product_tobuy;
2317 $line->product_desc = $objp->product_desc;
2318 $line->product_tobatch = $objp->product_tobatch;
2319 $line->product_barcode = $objp->product_barcode;
2320 $line->product_custom_code = $objp->customcode;
2321 $line->product_custom_country_id = $objp->country_id;
2322 $line->product_custom_country_code = $objp->country_code;
2323
2324 $line->fk_product_type = $objp->fk_product_type; // Product or service
2325 $line->fk_unit = $objp->fk_unit;
2326
2327 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
2328
2329 $line->weight = $objp->weight;
2330 $line->weight_units = $objp->weight_units;
2331 $line->volume = $objp->volume;
2332 $line->volume_units = $objp->volume_units;
2333 $line->packaging = $objp->packaging;
2334
2335 $line->date_start = $this->db->jdate($objp->date_start);
2336 $line->date_end = $this->db->jdate($objp->date_end);
2337
2338 // Multicurrency
2339 $line->fk_multicurrency = $objp->fk_multicurrency;
2340 $line->multicurrency_code = $objp->multicurrency_code;
2341 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2342 $line->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
2343 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2344 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2345 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2346
2347 $line->fetch_optionals();
2348
2349 // multilangs
2350 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2351 $tmpproduct = new Product($this->db);
2352 $tmpproduct->fetch($objp->fk_product);
2353 $tmpproduct->getMultiLangs();
2354
2355 $line->multilangs = $tmpproduct->multilangs;
2356 }
2357
2358 $this->lines[$i] = $line;
2359
2360 $i++;
2361 }
2362
2363 $this->db->free($result);
2364
2365 return 1;
2366 } else {
2367 $this->error = $this->db->error();
2368 return -3;
2369 }
2370 }
2371
2372
2378 public function getNbOfProductsLines()
2379 {
2380 $nb = 0;
2381 foreach ($this->lines as $line) {
2382 if ($line->product_type == 0) {
2383 $nb++;
2384 }
2385 }
2386 return $nb;
2387 }
2388
2394 public function getNbOfServicesLines()
2395 {
2396 $nb = 0;
2397 foreach ($this->lines as $line) {
2398 if ($line->product_type == 1) {
2399 $nb++;
2400 }
2401 }
2402 return $nb;
2403 }
2404
2410 public function getNbOfShipments()
2411 {
2412 $nb = 0;
2413
2414 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2415 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2416 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2417 $sql .= ' WHERE';
2418 $sql .= ' ed.fk_elementdet = cd.rowid';
2419 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2420 //print $sql;
2421
2422 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2423 $resql = $this->db->query($sql);
2424 if ($resql) {
2425 $obj = $this->db->fetch_object($resql);
2426 if ($obj) {
2427 $nb = (int) $obj->nb;
2428 }
2429
2430 $this->db->free($resql);
2431 return $nb;
2432 } else {
2433 $this->error = $this->db->lasterror();
2434 return -1;
2435 }
2436 }
2437
2446 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2447 {
2448 $this->expeditions = array();
2449
2450 $sql = 'SELECT cd.rowid, cd.fk_product,';
2451 $sql .= ' sum(ed.qty) as qty';
2452 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2453 if ($filtre_statut >= 0) {
2454 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2455 }
2456 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2457 $sql .= ' WHERE';
2458 if ($filtre_statut >= 0) {
2459 $sql .= ' ed.fk_expedition = e.rowid AND';
2460 }
2461 $sql .= ' ed.fk_elementdet = cd.rowid';
2462 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2463 if ($fk_product > 0) {
2464 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2465 }
2466 if ($filtre_statut >= 0) {
2467 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2468 }
2469 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2470 //print $sql;
2471
2472 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2473 $resql = $this->db->query($sql);
2474 if ($resql) {
2475 $num = $this->db->num_rows($resql);
2476 $i = 0;
2477 while ($i < $num) {
2478 $obj = $this->db->fetch_object($resql);
2479 $this->expeditions[$obj->rowid] = $obj->qty;
2480 $i++;
2481 }
2482 $this->db->free($resql);
2483 return $num;
2484 } else {
2485 $this->error = $this->db->lasterror();
2486 return -1;
2487 }
2488 }
2489
2495 public function countNbOfShipments()
2496 {
2497 $sql = 'SELECT count(*)';
2498 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2499 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2500 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2501 $sql .= " AND el.sourcetype = 'commande'";
2502 $sql .= " AND el.fk_target = e.rowid";
2503 $sql .= " AND el.targettype = 'shipping'";
2504
2505 $resql = $this->db->query($sql);
2506 if ($resql) {
2507 $row = $this->db->fetch_row($resql);
2508 return $row[0];
2509 } else {
2510 dol_print_error($this->db);
2511 }
2512
2513 return 0;
2514 }
2515
2524 public function deleteLine($user = null, $lineid = 0, $id = 0)
2525 {
2526 if ($this->status == self::STATUS_DRAFT) {
2527 $this->db->begin();
2528
2529 // Delete line
2530 $line = new OrderLine($this->db);
2531
2532 $line->context = $this->context;
2533
2534 // Load data
2535 $line->fetch($lineid);
2536
2537 if ($id > 0 && $line->fk_commande != $id) {
2538 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2539 return -1;
2540 }
2541
2542 // Memorize previous line for triggers
2543 $staticline = clone $line;
2544 $line->oldline = $staticline;
2545
2546 if ($line->delete($user) > 0) {
2547 $result = $this->update_price(1);
2548
2549 if ($result > 0) {
2550 $this->db->commit();
2551 return 1;
2552 } else {
2553 $this->db->rollback();
2554 $this->error = $this->db->lasterror();
2555 return -1;
2556 }
2557 } else {
2558 $this->db->rollback();
2559 $this->setErrorsFromObject($line);
2560 return -1;
2561 }
2562 } else {
2563 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2564 return -1;
2565 }
2566 }
2567
2568 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2579 public function set_remise($user, $remise, $notrigger = 0)
2580 {
2581 // phpcs:enable
2582 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2583 // @phan-suppress-next-line PhanDeprecatedFunction
2584 return $this->setDiscount($user, $remise, $notrigger);
2585 }
2586
2595 public function setDiscount($user, $remise, $notrigger = 0)
2596 {
2597 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2598
2599 if ($user->hasRight('commande', 'creer')) {
2600 $error = 0;
2601
2602 $this->db->begin();
2603
2604 $remise = price2num($remise, 2);
2605
2606 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2607 $sql .= ' SET remise_percent = '.((float) $remise);
2608 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2609
2610 dol_syslog(__METHOD__, LOG_DEBUG);
2611 $resql = $this->db->query($sql);
2612 if (!$resql) {
2613 $this->errors[] = $this->db->error();
2614 $error++;
2615 }
2616
2617 if (!$error) {
2618 $this->oldcopy = clone $this;
2619 $this->remise_percent = $remise;
2620 $this->update_price(1);
2621 }
2622
2623 if (!$notrigger && empty($error)) {
2624 // Call trigger
2625 $result = $this->call_trigger('ORDER_MODIFY', $user);
2626 if ($result < 0) {
2627 $error++;
2628 }
2629 // End call triggers
2630 }
2631
2632 if (!$error) {
2633 $this->db->commit();
2634 return 1;
2635 } else {
2636 foreach ($this->errors as $errmsg) {
2637 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2638 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2639 }
2640 $this->db->rollback();
2641 return -1 * $error;
2642 }
2643 }
2644
2645 return 0;
2646 }
2647
2648
2649 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2658 public function set_date($user, $date, $notrigger = 0)
2659 {
2660 // phpcs:enable
2661 if ($user->hasRight('commande', 'creer')) {
2662 $error = 0;
2663
2664 $this->db->begin();
2665
2666 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2667 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2668 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2669
2670 dol_syslog(__METHOD__, LOG_DEBUG);
2671 $resql = $this->db->query($sql);
2672 if (!$resql) {
2673 $this->errors[] = $this->db->error();
2674 $error++;
2675 }
2676
2677 if (!$error) {
2678 $this->oldcopy = clone $this;
2679 $this->date = $date;
2680 }
2681
2682 if (!$notrigger && empty($error)) {
2683 // Call trigger
2684 $result = $this->call_trigger('ORDER_MODIFY', $user);
2685 if ($result < 0) {
2686 $error++;
2687 }
2688 // End call triggers
2689 }
2690
2691 if (!$error) {
2692 $this->db->commit();
2693 return 1;
2694 } else {
2695 foreach ($this->errors as $errmsg) {
2696 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2697 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2698 }
2699 $this->db->rollback();
2700 return -1 * $error;
2701 }
2702 } else {
2703 return -2;
2704 }
2705 }
2706
2707 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2717 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2718 {
2719 // phpcs:enable
2720 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2721 }
2722
2731 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2732 {
2733 if ($user->hasRight('commande', 'creer')) {
2734 $error = 0;
2735
2736 $this->db->begin();
2737
2738 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2739 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2740 $sql .= " WHERE rowid = ".((int) $this->id);
2741
2742 dol_syslog(__METHOD__, LOG_DEBUG);
2743 $resql = $this->db->query($sql);
2744 if (!$resql) {
2745 $this->errors[] = $this->db->error();
2746 $error++;
2747 }
2748
2749 if (!$error) {
2750 $this->oldcopy = clone $this;
2751 $this->delivery_date = $delivery_date;
2752 }
2753
2754 if (!$notrigger && empty($error)) {
2755 // Call trigger
2756 $result = $this->call_trigger('ORDER_MODIFY', $user);
2757 if ($result < 0) {
2758 $error++;
2759 }
2760 // End call triggers
2761 }
2762
2763 if (!$error) {
2764 $this->db->commit();
2765 return 1;
2766 } else {
2767 foreach ($this->errors as $errmsg) {
2768 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2769 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2770 }
2771 $this->db->rollback();
2772 return -1 * $error;
2773 }
2774 } else {
2775 return -2;
2776 }
2777 }
2778
2779 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2793 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2794 {
2795 // phpcs:enable
2796 global $user;
2797
2798 $ga = array();
2799
2800 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2801 $sql .= " c.rowid as cid, c.ref";
2802 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2803 $sql .= ", sc.fk_soc, sc.fk_user";
2804 }
2805 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2806 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2807 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2808 }
2809 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2810 $sql .= " AND c.fk_soc = s.rowid";
2811 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2812 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2813 }
2814 if ($socid) {
2815 $sql .= " AND s.rowid = ".((int) $socid);
2816 }
2817 if ($draft) {
2818 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2819 }
2820 if (is_object($excluser)) {
2821 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2822 }
2823 $sql .= $this->db->order($sortfield, $sortorder);
2824 $sql .= $this->db->plimit($limit, $offset);
2825
2826 $result = $this->db->query($sql);
2827 if ($result) {
2828 $numc = $this->db->num_rows($result);
2829 if ($numc) {
2830 $i = 0;
2831 while ($i < $numc) {
2832 $obj = $this->db->fetch_object($result);
2833
2834 if ($shortlist == 1) {
2835 $ga[$obj->cid] = $obj->ref;
2836 } elseif ($shortlist == 2) {
2837 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2838 } else {
2839 $ga[$i]['id'] = $obj->cid;
2840 $ga[$i]['ref'] = $obj->ref;
2841 $ga[$i]['name'] = $obj->name;
2842 }
2843 $i++;
2844 }
2845 }
2846 return $ga;
2847 } else {
2848 dol_print_error($this->db);
2849 return -1;
2850 }
2851 }
2852
2860 public function availability($availability_id, $notrigger = 0)
2861 {
2862 global $user;
2863
2864 dol_syslog('Commande::availability('.$availability_id.')');
2865 if ($this->status >= self::STATUS_DRAFT) {
2866 $error = 0;
2867
2868 $this->db->begin();
2869
2870 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2871 $sql .= ' SET fk_availability = '.((int) $availability_id);
2872 $sql .= ' WHERE rowid='.((int) $this->id);
2873
2874 dol_syslog(__METHOD__, LOG_DEBUG);
2875 $resql = $this->db->query($sql);
2876 if (!$resql) {
2877 $this->errors[] = $this->db->error();
2878 $error++;
2879 }
2880
2881 if (!$error) {
2882 $this->oldcopy = clone $this;
2883 $this->availability_id = $availability_id;
2884 }
2885
2886 if (!$notrigger && empty($error)) {
2887 // Call trigger
2888 $result = $this->call_trigger('ORDER_MODIFY', $user);
2889 if ($result < 0) {
2890 $error++;
2891 }
2892 // End call triggers
2893 }
2894
2895 if (!$error) {
2896 $this->db->commit();
2897 return 1;
2898 } else {
2899 foreach ($this->errors as $errmsg) {
2900 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2901 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2902 }
2903 $this->db->rollback();
2904 return -1 * $error;
2905 }
2906 } else {
2907 $error_str = 'Command status do not meet requirement '.$this->status;
2908 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2909 $this->error = $error_str;
2910 $this->errors[] = $this->error;
2911 return -2;
2912 }
2913 }
2914
2915 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2923 public function demand_reason($demand_reason_id, $notrigger = 0)
2924 {
2925 // phpcs:enable
2926 global $user;
2927
2928 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2929 if ($this->status >= self::STATUS_DRAFT) {
2930 $error = 0;
2931
2932 $this->db->begin();
2933
2934 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2935 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2936 $sql .= ' WHERE rowid='.((int) $this->id);
2937
2938 dol_syslog(__METHOD__, LOG_DEBUG);
2939 $resql = $this->db->query($sql);
2940 if (!$resql) {
2941 $this->errors[] = $this->db->error();
2942 $error++;
2943 }
2944
2945 if (!$error) {
2946 $this->oldcopy = clone $this;
2947 $this->demand_reason_id = $demand_reason_id;
2948 }
2949
2950 if (!$notrigger && empty($error)) {
2951 // Call trigger
2952 $result = $this->call_trigger('ORDER_MODIFY', $user);
2953 if ($result < 0) {
2954 $error++;
2955 }
2956 // End call triggers
2957 }
2958
2959 if (!$error) {
2960 $this->db->commit();
2961 return 1;
2962 } else {
2963 foreach ($this->errors as $errmsg) {
2964 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2965 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2966 }
2967 $this->db->rollback();
2968 return -1 * $error;
2969 }
2970 } else {
2971 $error_str = 'order status do not meet requirement '.$this->status;
2972 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2973 $this->error = $error_str;
2974 $this->errors[] = $this->error;
2975 return -2;
2976 }
2977 }
2978
2979 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2988 public function set_ref_client($user, $ref_client, $notrigger = 0)
2989 {
2990 // phpcs:enable
2991 if ($user->hasRight('commande', 'creer')) {
2992 $error = 0;
2993
2994 $this->db->begin();
2995
2996 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2997 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2998 $sql .= ' WHERE rowid = '.((int) $this->id);
2999
3000 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
3001 $resql = $this->db->query($sql);
3002 if (!$resql) {
3003 $this->errors[] = $this->db->error();
3004 $error++;
3005 }
3006
3007 if (!$error) {
3008 $this->oldcopy = clone $this;
3009 $this->ref_client = $ref_client;
3010 $this->ref_customer = $ref_client;
3011 }
3012
3013 if (!$notrigger && empty($error)) {
3014 // Call trigger
3015 $result = $this->call_trigger('ORDER_MODIFY', $user);
3016 if ($result < 0) {
3017 $error++;
3018 }
3019 // End call triggers
3020 }
3021 if (!$error) {
3022 $this->db->commit();
3023 return 1;
3024 } else {
3025 foreach ($this->errors as $errmsg) {
3026 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3027 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3028 }
3029 $this->db->rollback();
3030 return -1 * $error;
3031 }
3032 } else {
3033 return -1;
3034 }
3035 }
3036
3044 public function classifyBilled(User $user, $notrigger = 0)
3045 {
3046 $error = 0;
3047
3048 if ($this->billed) {
3049 return 0;
3050 }
3051
3052 $this->db->begin();
3053
3054 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3055 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3056
3057 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3058 if ($this->db->query($sql)) {
3059 $this->oldcopy = clone $this;
3060 $this->billed = 1;
3061
3062 if (!$notrigger && empty($error)) {
3063 // Call trigger
3064 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3065 if ($result < 0) {
3066 $error++;
3067 }
3068 // End call triggers
3069 }
3070
3071 if (!$error) {
3072 $this->db->commit();
3073 return 1;
3074 } else {
3075 foreach ($this->errors as $errmsg) {
3076 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3077 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3078 }
3079 $this->db->rollback();
3080 return -1 * $error;
3081 }
3082 } else {
3083 $this->error = $this->db->error();
3084 $this->db->rollback();
3085 return -1;
3086 }
3087 }
3088
3096 public function classifyUnBilled(User $user, $notrigger = 0)
3097 {
3098 $error = 0;
3099
3100 $this->db->begin();
3101
3102 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3103 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3104
3105 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3106 if ($this->db->query($sql)) {
3107 $this->oldcopy = clone $this;
3108 $this->billed = 1;
3109
3110 if (!$notrigger && empty($error)) {
3111 // Call trigger
3112 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3113 if ($result < 0) {
3114 $error++;
3115 }
3116 // End call triggers
3117 }
3118
3119 if (!$error) {
3120 $this->billed = 0;
3121
3122 $this->db->commit();
3123 return 1;
3124 } else {
3125 foreach ($this->errors as $errmsg) {
3126 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3127 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3128 }
3129 $this->db->rollback();
3130 return -1 * $error;
3131 }
3132 } else {
3133 $this->error = $this->db->error();
3134 $this->db->rollback();
3135 return -1;
3136 }
3137 }
3138
3139
3170 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)
3171 {
3172 global $mysoc, $langs, $user;
3173
3174 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");
3175 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3176
3177 if ($this->status == Commande::STATUS_DRAFT) {
3178 // Clean parameters
3179 if (empty($qty)) {
3180 $qty = 0;
3181 }
3182 if (empty($info_bits)) {
3183 $info_bits = 0;
3184 }
3185 if (empty($txtva)) {
3186 $txtva = 0;
3187 }
3188 if (empty($txlocaltax1)) {
3189 $txlocaltax1 = 0;
3190 }
3191 if (empty($txlocaltax2)) {
3192 $txlocaltax2 = 0;
3193 }
3194 if (empty($remise_percent)) {
3195 $remise_percent = 0;
3196 }
3197 if (empty($qty) && empty($special_code)) {
3198 $special_code = 3; // Set option tag
3199 }
3200 if (!empty($qty) && $special_code == 3) {
3201 $special_code = 0; // Remove option tag
3202 }
3203 if (empty($ref_ext)) {
3204 $ref_ext = '';
3205 }
3206
3207 if ($date_start && $date_end && $date_start > $date_end) {
3208 $langs->load("errors");
3209 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3210 return -1;
3211 }
3212
3213 $remise_percent = (float) price2num($remise_percent);
3214 $qty = (float) price2num($qty);
3215 $pu = price2num($pu);
3216 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
3217 $pu_ht_devise = price2num($pu_ht_devise);
3218 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3219 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3220 }
3221 $txlocaltax1 = (float) price2num($txlocaltax1);
3222 $txlocaltax2 = (float) price2num($txlocaltax2);
3223
3224 $this->db->begin();
3225
3226 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
3227 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
3228 // and this is done at the line level, which has its own VAT rate
3229
3230 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3231
3232 // Clean vat code
3233 $vat_src_code = '';
3234 $reg = array();
3235 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3236 $vat_src_code = $reg[1];
3237 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3238 }
3239
3240 $tabprice = calcul_price_total($qty, (float) $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_ht_devise);
3241
3242 $total_ht = $tabprice[0];
3243 $total_tva = $tabprice[1];
3244 $total_ttc = $tabprice[2];
3245 $total_localtax1 = $tabprice[9];
3246 $total_localtax2 = $tabprice[10];
3247 $pu_ht = $tabprice[3];
3248 $pu_tva = $tabprice[4];
3249 $pu_ttc = $tabprice[5];
3250
3251 // MultiCurrency
3252 $multicurrency_total_ht = $tabprice[16];
3253 $multicurrency_total_tva = $tabprice[17];
3254 $multicurrency_total_ttc = $tabprice[18];
3255 $pu_ht_devise = $tabprice[19];
3256
3257 // Fetch current line from the database and then clone the object and set it in $oldline property
3258 $line = new OrderLine($this->db);
3259 $line->fetch($rowid);
3260 $line->fetch_optionals();
3261
3262 if (!empty($line->fk_product)) {
3263 $product = new Product($this->db);
3264 $result = $product->fetch($line->fk_product);
3265 $product_type = $product->type;
3266
3267 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product->isStockManaged()) {
3268 // get real stock
3269 $productChildrenNb = 0;
3270 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
3271 $productChildrenNb = $product->hasFatherOrChild(1);
3272 }
3273 if ($productChildrenNb > 0) {
3274 // compute real stock from each subcomponent
3275 $product_stock = null;
3276 $product->loadStockForVirtualProduct('warehouseopen', $qty);
3277 foreach ($product->stock_warehouse as $componentStockWarehouse) {
3278 if ($product_stock === null) {
3279 $product_stock = $componentStockWarehouse->real;
3280 } else {
3281 $product_stock = min($product_stock, $componentStockWarehouse->real);
3282 }
3283 }
3284 if ($product_stock === null) {
3285 $product_stock = 0;
3286 }
3287 } else {
3288 $product_stock = $product->stock_reel;
3289 }
3290
3291 if ($product_stock < $qty) {
3292 $langs->load("errors");
3293 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', (string) $product->ref);
3294 $this->errors[] = $this->error;
3295
3296 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3297
3298 $this->db->rollback();
3300 }
3301 }
3302 }
3303
3304 $staticline = clone $line;
3305
3306 $line->oldline = $staticline;
3307 $this->line = $line;
3308 $this->line->context = $this->context;
3309 $this->line->rang = $rang;
3310
3311 // Reorder if fk_parent_line change
3312 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3313 $rangmax = $this->line_max($fk_parent_line);
3314 $this->line->rang = $rangmax + 1;
3315 }
3316
3317 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
3318 if (abs((float) $qty) < $this->line->packaging) {
3319 $qty = $this->line->packaging;
3320 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'warnings');
3321 } else {
3322 if (!empty($this->line->packaging)
3323 && is_numeric($this->line->packaging)
3324 && (float) $this->line->packaging > 0
3325 && (float) price2num(fmod((float) $qty, (float) $this->line->packaging), 'MS')) {
3326 // Use abs() to keep the rounding consistent for negative qty,
3327 // matching what addline() at line 1725 already does (#38782 bug 5).
3328 $coeff = intval(abs((float) $qty) / $this->line->packaging) + 1;
3329 $qty = price2num((float) $this->line->packaging * $coeff, 'MS');
3330 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'warnings');
3331 }
3332 }
3333 }
3334
3335 $this->line->id = $rowid;
3336 $this->line->label = $label;
3337 $this->line->desc = $desc;
3338 $this->line->qty = $qty;
3339 $this->line->ref_ext = $ref_ext;
3340
3341 $this->line->vat_src_code = $vat_src_code;
3342 $this->line->tva_tx = $txtva;
3343 $this->line->localtax1_tx = $txlocaltax1;
3344 $this->line->localtax2_tx = $txlocaltax2;
3345 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3346 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3347 $this->line->remise_percent = $remise_percent;
3348 $this->line->subprice = (float) $pu_ht;
3349 $this->line->info_bits = $info_bits;
3350 $this->line->special_code = $special_code;
3351 $this->line->total_ht = (float) $total_ht;
3352 $this->line->total_tva = (float) $total_tva;
3353 $this->line->total_localtax1 = (float) $total_localtax1;
3354 $this->line->total_localtax2 = (float) $total_localtax2;
3355 $this->line->total_ttc = (float) $total_ttc;
3356 $this->line->date_start = $date_start;
3357 $this->line->date_end = $date_end;
3358 $this->line->product_type = $type;
3359 $this->line->fk_parent_line = $fk_parent_line;
3360 $this->line->skip_update_total = $skip_update_total;
3361 $this->line->fk_unit = $fk_unit;
3362
3363 $this->line->fk_fournprice = $fk_fournprice;
3364 $this->line->pa_ht = $pa_ht;
3365
3366 // Multicurrency
3367 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3368 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3369 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3370 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3371
3372 if (is_array($array_options) && count($array_options) > 0) {
3373 // We replace values in this->line->array_options only for entries defined into $array_options
3374 foreach ($array_options as $key => $value) {
3375 $this->line->array_options[$key] = $array_options[$key];
3376 }
3377 }
3378
3379 $result = $this->line->update($user, $notrigger);
3380 if ($result > 0) {
3381 // Reorder if child line
3382 if (!empty($fk_parent_line)) {
3383 $this->line_order(true, 'DESC');
3384 }
3385
3386 // Mise a jour info denormalisees
3387 $this->update_price(1, 'auto');
3388
3389 $this->db->commit();
3390 return $result;
3391 } else {
3392 $this->setErrorsFromObject($this->line);
3393
3394 $this->db->rollback();
3395 return -1;
3396 }
3397 } else {
3398 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3399 $this->errors = array('OrderStatusMakeOperationForbidden');
3400 return -2;
3401 }
3402 }
3403
3411 public function update(User $user, $notrigger = 0)
3412 {
3413 $error = 0;
3414
3415 // Clean parameters
3416 if (isset($this->ref)) {
3417 $this->ref = trim($this->ref);
3418 }
3419 if (isset($this->ref_client)) {
3420 $this->ref_client = trim($this->ref_client);
3421 }
3422 if (isset($this->ref_customer)) {
3423 $this->ref_customer = trim($this->ref_customer);
3424 }
3425 if (isset($this->note) || isset($this->note_private)) {
3426 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3427 }
3428 if (isset($this->note_public)) {
3429 $this->note_public = trim($this->note_public);
3430 }
3431 if (isset($this->model_pdf)) {
3432 $this->model_pdf = trim($this->model_pdf);
3433 }
3434 if (isset($this->import_key)) {
3435 $this->import_key = trim($this->import_key);
3436 }
3437 $delivery_date = $this->delivery_date;
3438
3439 // Check parameters
3440 // Put here code to add control on parameters values
3441
3442 // Update request
3443 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3444
3445 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3446 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3447 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3448 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
3449 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3450 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3451
3452 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
3453 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
3454 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
3455 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
3456 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
3457 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : "null").",";
3458 $sql .= " fk_user_modif=".(isset($user->id) ? ((int) $user->id) : "null").",";
3459 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? ((int) $this->user_validation_id) : "null").",";
3460 $sql .= " fk_projet=".(isset($this->fk_project) ? ((int) $this->fk_project) : "null").",";
3461 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
3462 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
3463 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
3464 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3465 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? ((int) $this->shipping_method_id) : "null").",";
3466 $sql .= " fk_account=".($this->fk_account > 0 ? ((int) $this->fk_account) : "null").",";
3467 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null").",";
3468 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3469 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3470 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3471 $sql .= " fk_warehouse=".($this->warehouse_id > 0 ? ((int) $this->warehouse_id) : "null").",";
3472 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").",";
3473 $sql .= " module_source = ".(isset($this->module_source) ? "'".$this->db->escape($this->module_source)."'" : "null").",";
3474 $sql .= " pos_source = ".(isset($this->pos_source) ? "'".$this->db->escape($this->pos_source)."'" : "null");
3475 $sql .= " WHERE rowid=".((int) $this->id);
3476
3477 $this->db->begin();
3478
3479 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3480 $resql = $this->db->query($sql);
3481 if (!$resql) {
3482 $error++;
3483 $this->errors[] = "Error ".$this->db->lasterror();
3484 }
3485
3486 if (!$error) {
3487 $result = $this->insertExtraFields();
3488 if ($result < 0) {
3489 $error++;
3490 }
3491 }
3492
3493 if (!$error && !$notrigger) {
3494 // Call trigger
3495 $result = $this->call_trigger('ORDER_MODIFY', $user);
3496 if ($result < 0) {
3497 $error++;
3498 }
3499 // End call triggers
3500 }
3501
3502 // Commit or rollback
3503 if ($error) {
3504 foreach ($this->errors as $errmsg) {
3505 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3506 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3507 }
3508 $this->db->rollback();
3509 return -1 * $error;
3510 } else {
3511 $this->db->commit();
3512 return 1;
3513 }
3514 }
3515
3523 public function delete($user, $notrigger = 0)
3524 {
3525 global $conf, $langs;
3526 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3527
3528 $error = 0;
3529
3530 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3531
3532 $this->db->begin();
3533
3534 if (!$notrigger) {
3535 // Call trigger
3536 $result = $this->call_trigger('ORDER_DELETE', $user);
3537 if ($result < 0) {
3538 $error++;
3539 }
3540 // End call triggers
3541 }
3542
3543 // Test we can delete
3544 if ($this->countNbOfShipments() != 0) {
3545 $this->errors[] = $langs->trans('SomeShipmentExists');
3546 $error++;
3547 }
3548
3549 // Remove linked categories.
3550 if (!$error) {
3551 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_order";
3552 $sql .= " WHERE fk_order = ".((int) $this->id);
3553
3554 $result = $this->db->query($sql);
3555 if (!$result) {
3556 $error++;
3557 $this->errors[] = $this->db->lasterror();
3558 }
3559 }
3560
3561 // Delete extrafields of lines and lines
3562 if (!$error && !empty($this->table_element_line)) {
3563 $tabletodelete = $this->table_element_line;
3564 $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).")";
3565 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3566 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3567 $error++;
3568 $this->error = $this->db->lasterror();
3569 $this->errors[] = $this->error;
3570 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3571 }
3572 }
3573
3574 if (!$error) {
3575 // Delete linked object
3576 $res = $this->deleteObjectLinked();
3577 if ($res < 0) {
3578 $error++;
3579 }
3580 }
3581
3582 if (!$error) {
3583 // Delete linked contacts
3584 $res = $this->delete_linked_contact();
3585 if ($res < 0) {
3586 $error++;
3587 }
3588 }
3589
3590 // Removed extrafields of object
3591 if (!$error) {
3592 $result = $this->deleteExtraFields();
3593 if ($result < 0) {
3594 $error++;
3595 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3596 }
3597 }
3598
3599 // Delete main record
3600 if (!$error) {
3601 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3602 $res = $this->db->query($sql);
3603 if (!$res) {
3604 $error++;
3605 $this->error = $this->db->lasterror();
3606 $this->errors[] = $this->error;
3607 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3608 }
3609 }
3610
3611 // Delete record into ECM index and physically
3612 if (!$error) {
3613 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive, old method.
3614 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3615 if (!$res) {
3616 $error++;
3617 }
3618 }
3619
3620 if (!$error) {
3621 // We remove directory
3622 $ref = dol_sanitizeFileName($this->ref);
3623 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3624 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3625 $file = $dir."/".$ref.".pdf";
3626 if (file_exists($file)) {
3627 dol_delete_preview($this);
3628
3629 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3630 $this->error = 'ErrorFailToDeleteFile';
3631 $this->errors[] = $this->error;
3632 $this->db->rollback();
3633 return 0;
3634 }
3635 }
3636 if (file_exists($dir)) {
3637 $res = @dol_delete_dir_recursive($dir);
3638 if (!$res) {
3639 $this->error = 'ErrorFailToDeleteDir';
3640 $this->errors[] = $this->error;
3641 $this->db->rollback();
3642 return 0;
3643 }
3644 }
3645 }
3646 }
3647
3648 if (!$error) {
3649 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3650 $this->db->commit();
3651 return 1;
3652 } else {
3653 $this->db->rollback();
3654 return -1;
3655 }
3656 }
3657
3658
3659 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3667 public function load_board($user, $mode)
3668 {
3669 // phpcs:enable
3670 global $conf, $langs, $hookmanager;
3671
3672 $clause = " WHERE";
3673
3674 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3675 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3676 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3677 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3678 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3679 $clause = " AND";
3680 }
3681 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3682 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3683 if ($mode == 'toship') {
3684 // An order to ship is an open order (validated or in progress)
3685 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3686 }
3687 if ($mode == 'tobill') {
3688 // An order to bill is an order not already billed
3689 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3690 }
3691 if ($mode == 'shippedtobill') {
3692 // An order shipped and to bill is a delivered order not already billed
3693 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3694 }
3695 if ($user->socid) {
3696 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3697 }
3698 // Add where from hooks
3699 $parameters = array('socid' => $user->socid);
3700 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $this); // Note that $action and $object may have been modified by hook
3701 $sql .= $hookmanager->resPrint;
3702 $resql = $this->db->query($sql);
3703 if ($resql) {
3704 $delay_warning = 0;
3705 $label = $labelShort = $url = '';
3706 if ($mode == 'toship') {
3707 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3708 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3709 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3710 $labelShort = $langs->transnoentitiesnoconv("Opened");
3711 }
3712 if ($mode == 'tobill') {
3713 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3714 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3715 $labelShort = $langs->trans("ToBill");
3716 }
3717 if ($mode == 'shippedtobill') {
3718 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3719 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3720 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3721 }
3722
3723 $response = new WorkboardResponse();
3724
3725 $response->warning_delay = $delay_warning;
3726 $response->label = $label;
3727 $response->labelShort = $labelShort;
3728 $response->url = $url;
3729 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3730 $response->img = img_object('', "order");
3731
3732 $generic_commande = new Commande($this->db);
3733
3734 while ($obj = $this->db->fetch_object($resql)) {
3735 $response->nbtodo++;
3736 $response->total += $obj->total_ht;
3737
3738 $generic_commande->status = $obj->fk_statut;
3739 $generic_commande->date = $this->db->jdate($obj->date_commande);
3740 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3741
3742 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3743 $response->nbtodolate++;
3744 }
3745 }
3746
3747 return $response;
3748 } else {
3749 $this->error = $this->db->error();
3750 return -1;
3751 }
3752 }
3753
3759 public function getLabelSource()
3760 {
3761 global $langs;
3762
3763 $label = $langs->trans('OrderSource'.$this->source);
3764
3765 if ($label == 'OrderSource') {
3766 return '';
3767 }
3768 return $label;
3769 }
3770
3777 public function getLibStatut($mode)
3778 {
3779 return $this->LibStatut($this->status, $this->billed, $mode);
3780 }
3781
3782 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3792 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3793 {
3794 // phpcs:enable
3795 global $langs, $hookmanager;
3796
3797 $billedtext = '';
3798 if (empty($donotshowbilled)) {
3799 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3800 }
3801 $billedtextlong = ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3802
3803 $labelTooltip = '';
3804
3805 $paramsBadge = array('badgeParams' => array('attr' => array(
3806 'data-status-element' => $this->element,
3807 'data-billed' => (int) $billed,
3808 'data-status' => (int) $status
3809 )));
3810
3811 if ($status == self::STATUS_CANCELED) {
3812 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3813 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3814 $statusType = 'status9';
3815 } elseif ($status == self::STATUS_DRAFT) {
3816 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3817 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3818 $statusType = 'status0';
3819 } elseif ($status == self::STATUS_VALIDATED) {
3820 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtextlong;
3821 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3822 $statusType = 'status1';
3823 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3824 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtextlong;
3825 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3826 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3827 if (!empty($this->delivery_date)) {
3828 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3829 }
3830 $statusType = 'status4';
3831 } elseif ($status == self::STATUS_CLOSED) {
3832 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtextlong;
3833 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3834 $statusType = 'status6';
3835 } else {
3836 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3837 $labelStatusShort = '';
3838 $statusType = '';
3839 $mode = 0;
3840 }
3841
3842 $paramsBadge['tooltip'] = $labelTooltip;
3843
3844 $parameters = array(
3845 'status' => $status,
3846 'mode' => $mode,
3847 'billed' => $billed,
3848 'donotshowbilled' => $donotshowbilled,
3849 'paramsBadge' => & $paramsBadge
3850 );
3851
3852 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3853
3854 if ($reshook > 0) {
3855 return $hookmanager->resPrint;
3856 }
3857
3858 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $paramsBadge);
3859 }
3860
3868 public function getTooltipContentArray($params)
3869 {
3870 global $conf, $langs, $user;
3871
3872 $langs->load('orders');
3873 $datas = [];
3874 $nofetch = !empty($params['nofetch']);
3875
3876 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3877 return ['optimize' => $langs->trans("Order")];
3878 }
3879
3880 if ($user->hasRight('commande', 'lire')) {
3881 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').'<u>'.$langs->trans("Order").'</u>';
3882 if (isset($this->status)) {
3883 $datas['status'] = ' '.$this->getLibStatut(5);
3884 }
3885 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3886 if (!$nofetch) {
3887 $langs->load('companies');
3888 if (empty($this->thirdparty)) {
3889 $this->fetch_thirdparty();
3890 }
3891 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3892 }
3893 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3894 if (!$nofetch) {
3895 $langs->load('project');
3896 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3897 $res = $this->fetchProject();
3898 if ($res > 0 && $this->project instanceof Project) {
3899 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, '1');
3900 }
3901 }
3902 }
3903 if (!empty($this->total_ht)) {
3904 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3905 }
3906 if (!empty($this->total_tva)) {
3907 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3908 }
3909 if (!empty($this->total_ttc)) {
3910 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3911 }
3912 if (!empty($this->date)) {
3913 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3914 }
3915 if (!empty($this->delivery_date)) {
3916 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3917 }
3918 }
3919
3920 return $datas;
3921 }
3922
3936 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3937 {
3938 global $conf, $langs, $user, $hookmanager;
3939
3940 if (!empty($conf->dol_no_mouse_hover)) {
3941 $notooltip = 1; // Force disable tooltips
3942 }
3943
3944 $result = '';
3945
3946 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3947 $baseurl = DOL_URL_ROOT . '/expedition/shipment.php';
3948 } else {
3949 $baseurl = DOL_URL_ROOT . '/commande/card.php';
3950 }
3951 $query = ['id' => $this->id];
3952 if (!$user->hasRight('commande', 'lire')) {
3953 $option = 'nolink';
3954 }
3955
3956 if ($option !== 'nolink') {
3957 // Add param to save lastsearch_values or not
3958 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3959 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3960 $add_save_lastsearch_values = 1;
3961 }
3962 if ($add_save_lastsearch_values) {
3963 $query = array_merge($query, ['save_lastsearch_values' => 1]);
3964 }
3965 }
3966 $url = dolBuildUrl($baseurl, $query);
3967
3968 if ($short) {
3969 return $url;
3970 }
3971 $params = [
3972 'id' => $this->id,
3973 'objecttype' => $this->element,
3974 'option' => $option,
3975 'nofetch' => 1,
3976 ];
3977 $classfortooltip = 'classfortooltip';
3978 $dataparams = '';
3979 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3980 $classfortooltip = 'classforajaxtooltip';
3981 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3982 $label = '';
3983 } else {
3984 $label = implode($this->getTooltipContentArray($params));
3985 }
3986
3987 $linkclose = '';
3988 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3989 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3990 $label = $langs->trans("Order");
3991 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
3992 }
3993 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
3994 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3995
3996 $target_value = array('_self', '_blank', '_parent', '_top');
3997 if (in_array($target, $target_value)) {
3998 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3999 }
4000 }
4001
4002 $linkstart = '<a href="'.$url.'"';
4003 $linkstart .= $linkclose.'>';
4004 $linkend = '</a>';
4005
4006 if ($option === 'nolink') {
4007 $linkstart = '';
4008 $linkend = '';
4009 }
4010
4011 $result .= $linkstart;
4012 if ($withpicto) {
4013 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
4014 }
4015 if ($withpicto != 2) {
4016 $result .= $this->ref;
4017 }
4018 $result .= $linkend;
4019
4020 if ($addlinktonotes) {
4021 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
4022 if ($txttoshow) {
4023 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
4024 $result .= ' <span class="note inline-block">';
4025 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
4026 $result .= img_picto('', 'note');
4027 $result .= '</a>';
4028 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
4029 //$result.='</a>';
4030 $result .= '</span>';
4031 }
4032 }
4033
4034 global $action;
4035 $hookmanager->initHooks(array($this->element . 'dao'));
4036 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
4037 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4038 if ($reshook > 0) {
4039 $result = $hookmanager->resPrint;
4040 } else {
4041 $result .= $hookmanager->resPrint;
4042 }
4043 return $result;
4044 }
4045
4046
4053 public function info($id)
4054 {
4055 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
4056 $sql .= ' date_valid as datev,';
4057 $sql .= ' date_cloture as datecloture,';
4058 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
4059 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
4060 $sql .= ' WHERE c.rowid = '.((int) $id);
4061 $result = $this->db->query($sql);
4062 if ($result) {
4063 if ($this->db->num_rows($result)) {
4064 $obj = $this->db->fetch_object($result);
4065 $this->id = $obj->rowid;
4066 if ($obj->fk_user_author) {
4067 $this->user_creation_id = $obj->fk_user_author;
4068 }
4069 if ($obj->fk_user_valid) {
4070 $this->user_validation_id = $obj->fk_user_valid;
4071 }
4072 if ($obj->fk_user_cloture) {
4073 $this->user_closing_id = $obj->fk_user_cloture;
4074 }
4075
4076 $this->date_creation = $this->db->jdate($obj->datec);
4077 $this->date_modification = $this->db->jdate($obj->datem);
4078 $this->date_validation = $this->db->jdate($obj->datev);
4079 $this->date_cloture = $this->db->jdate($obj->datecloture);
4080 }
4081
4082 $this->db->free($result);
4083 } else {
4084 dol_print_error($this->db);
4085 }
4086 }
4087
4088
4097 public function initAsSpecimen($param = array())
4098 {
4099 global $conf, $langs;
4100
4101 dol_syslog(get_class($this)."::initAsSpecimen");
4102
4103 // Load array of products prodids
4104 $num_prods = 0;
4105 $prodids = array();
4106 $sql = "SELECT rowid";
4107 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4108 $sql .= " WHERE entity IN (".getEntity('product').")";
4109 if (array_key_exists('tosell', $param)) {
4110 $sql .= " AND tosell = ".((int) $param['tosell']);
4111 }
4112 $sql .= $this->db->plimit(100);
4113
4114 $resql = $this->db->query($sql);
4115 if ($resql) {
4116 $num_prods = $this->db->num_rows($resql);
4117 $i = 0;
4118 while ($i < $num_prods) {
4119 $i++;
4120 $row = $this->db->fetch_row($resql);
4121 $prodids[$i] = $row[0];
4122 }
4123 }
4124
4125 // Initialise parameters
4126 $this->id = 0;
4127 $this->ref = 'SPECIMEN';
4128 $this->specimen = 1;
4129 $this->entity = $conf->entity;
4130 $this->socid = 1;
4131 $this->date = time();
4132 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4133 $this->cond_reglement_code = 'RECEP';
4134 $this->mode_reglement_code = 'CHQ';
4135 $this->availability_code = 'DSP';
4136 $this->demand_reason_code = 'SRC_00';
4137
4138 $this->note_public = 'This is a comment (public)';
4139 $this->note_private = 'This is a comment (private)';
4140
4141 $this->multicurrency_tx = 1;
4142 $this->multicurrency_code = $conf->currency;
4143
4144 $this->status = $this::STATUS_DRAFT;
4145
4146 // Lines
4147 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
4148 $xnbp = 0;
4149
4150 while ($xnbp < $nbp) {
4151 $line = new OrderLine($this->db);
4152
4153 $line->desc = $langs->trans("Description")." ".$xnbp;
4154 $line->qty = 1;
4155 $line->subprice = 100;
4156 $line->price = 100;
4157 $line->tva_tx = 20;
4158 if ($xnbp == 2) {
4159 $line->total_ht = 50;
4160 $line->total_ttc = 60;
4161 $line->total_tva = 10;
4162 $line->remise_percent = 50;
4163 } else {
4164 $line->total_ht = 100;
4165 $line->total_ttc = 120;
4166 $line->total_tva = 20;
4167 $line->remise_percent = 0;
4168 }
4169 if ($num_prods > 0) {
4170 $prodid = mt_rand(1, $num_prods);
4171 $line->fk_product = $prodids[$prodid];
4172 $line->product_ref = 'SPECIMEN';
4173 }
4174
4175 $this->lines[$xnbp] = $line;
4176
4177 $this->total_ht += $line->total_ht;
4178 $this->total_tva += $line->total_tva;
4179 $this->total_ttc += $line->total_ttc;
4180
4181 $xnbp++;
4182 }
4183
4184 return 1;
4185 }
4186
4187
4193 public function loadStateBoard()
4194 {
4195 global $user, $hookmanager;
4196
4197 $this->nb = array();
4198 $clause = "WHERE";
4199
4200 $sql = "SELECT count(c.rowid) as nb";
4201 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
4202 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
4203 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
4204 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4205 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4206 $clause = "AND";
4207 }
4208 $sql .= " ".$clause." c.entity IN (".getEntity('commande').")";
4209 // Add where from hooks
4210 $parameters = array();
4211 $hookmanager->executeHooks('printFieldListWhere', $parameters, $this); // Note that $action and $object may have been modified by hook
4212 $sql .= $hookmanager->resPrint;
4213 $resql = $this->db->query($sql);
4214 if ($resql) {
4215 while ($obj = $this->db->fetch_object($resql)) {
4216 $this->nb["orders"] = $obj->nb;
4217 }
4218 $this->db->free($resql);
4219 return 1;
4220 } else {
4221 dol_print_error($this->db);
4222 $this->error = $this->db->error();
4223 return -1;
4224 }
4225 }
4226
4232 public function getLinesArray()
4233 {
4234 return $this->fetch_lines();
4235 }
4236
4248 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4249 {
4250 global $langs;
4251
4252 $langs->load("orders");
4253 $outputlangs->load("products");
4254
4255 if (!dol_strlen($modele)) {
4256 $modele = 'einstein';
4257
4258 if (!empty($this->model_pdf)) {
4259 $modele = $this->model_pdf;
4260 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4261 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4262 }
4263 }
4264
4265 $modelpath = "core/modules/commande/doc/";
4266
4267 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4268 }
4269
4270
4279 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4280 {
4281 $tables = array(
4282 'commande'
4283 );
4284
4285 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4286 }
4287
4296 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4297 {
4298 $tables = array(
4299 'commandedet',
4300 );
4301
4302 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4303 }
4304
4310 public function hasDelay()
4311 {
4312 global $conf;
4313
4314 if (!($this->status > Commande::STATUS_DRAFT && $this->status < Commande::STATUS_CLOSED)) {
4315 return false; // Never late if not inside this status range
4316 }
4317
4318 $now = dol_now();
4319
4320 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4321 }
4322
4328 public function showDelay()
4329 {
4330 global $conf, $langs;
4331
4332 if (empty($this->delivery_date)) {
4333 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4334 } else {
4335 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4336 }
4337 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4338
4339 return $text;
4340 }
4341
4351 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4352 {
4353 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4354 }
4355
4363 public function getShippableInfos(array $options = array()): array
4364 {
4365 global $conf, $langs;
4366
4367 $langs->loadLangs(array('orders', 'sendings', 'stocks', 'products'));
4368
4369 $result = array(
4370 'has_product' => false,
4371 'shippable' => false,
4372 'texticon' => '',
4373 'textinfo' => '',
4374 'warning' => false,
4375 );
4376
4377 // Requested naming for statuses
4378 if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_CLOSED) {
4379 return $result;
4380 }
4381
4382
4383 $genericCommande = $this;
4384 $genericProduct = new Product($this->db);
4385
4386
4387 $productstatcache = array();
4388 $productstatcachevirtual = array();
4389
4390
4391 $genericCommande->getLinesArray(); // Load array ->lines
4392 $genericCommande->loadExpeditions(); // Load array ->expeditions
4393
4394 $notshippable = 0;
4395 $has_reliquat = 0;
4396 $warning = 0;
4397 $textinfo = '';
4398 $textwarning = '';
4399 $nbprod = 0;
4400
4401 $genericProduct = new Product($this->db);
4402
4403 $numlines = count($genericCommande->lines);
4404 for ($lig = 0; $lig < $numlines; $lig++) {
4405 $orderLine = $genericCommande->lines[$lig]; // @phan-var-force OrderLine $orderLine
4406
4407 if (isset($genericCommande->expeditions[$orderLine->id])) {
4408 $reliquat = $orderLine->qty - $genericCommande->expeditions[$orderLine->id];
4409 } else {
4410 $reliquat = $orderLine->qty;
4411 }
4412
4413 if ($orderLine->product_type == 0 && $orderLine->fk_product > 0) { // product, not service
4414 $nbprod = 1;
4415
4416 if (empty($productstatcache[$orderLine->fk_product])) {
4417 $genericProduct->fetch($orderLine->fk_product);
4418 $genericProduct->load_stock('nobatch,warehouseopen'); // loadvirtualstock included
4419
4420 $productstatcache[$orderLine->fk_product]['stockreel'] = $genericProduct->stock_reel;
4421 $productstatcachevirtual[$orderLine->fk_product]['stockreel'] = $genericProduct->stock_theorique;
4422 }
4423
4424 $genericProduct->stock_reel = $productstatcache[$orderLine->fk_product]['stockreel'];
4425 $genericProduct->stock_theorique = $productstatcachevirtual[$orderLine->fk_product]['stockreel'];
4426
4427 if ($reliquat > 0) {
4428 $has_reliquat = 1;
4429 if (!getDolGlobalString('SHIPPABLE_ORDER_ICON_IN_LIST')) {
4430 $textinfo .= $reliquat . ' x ' . $orderLine->product_ref . '&nbsp;' . dol_trunc($orderLine->product_label, 20);
4431 $textinfo .= ' - ' . $langs->trans("Stock") . ': <span class="' . ($genericProduct->stock_reel >= $reliquat ? 'ok' : 'error') . '">' . $genericProduct->stock_reel . '</span>';
4432 $textinfo .= ' - ' . $langs->trans("VirtualStock") . ': <span class="' . ($genericProduct->stock_theorique >= $reliquat ? 'ok' : 'error') . '">' . $genericProduct->stock_theorique . '</span>';
4433 if ($reliquat != $orderLine->qty) {
4434 $textinfo .= ' <span class="opacitymedium">' . $langs->trans("QtyInOtherShipments") . ' ' . ($orderLine->qty - $reliquat) . '</span>';
4435 }
4436 $textinfo .= '<br>';
4437 } else {
4438 // BUGGED CODE (kept for backward compatibility and hidden conf)
4439 $stockorder = 0;
4440 $stockordersupplier = 0;
4441
4442 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') || getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
4443 if (isModEnabled('order')) {
4444 if (empty($productstatcache[$orderLine->fk_product]['statsordercustomer'])) {
4445 $genericProduct->fetch($orderLine->fk_product);
4446 $genericProduct->load_stats_commande(0, '1,2');
4447 $productstatcache[$orderLine->fk_product]['statsordercustomer'] = $genericProduct->stats_commande['qty'];
4448 }
4449 $genericProduct->stats_commande['qty'] = $productstatcache[$orderLine->fk_product]['statsordercustomer'];
4450 $stockorder = $genericProduct->stats_commande['qty'];
4451
4452 if (isModEnabled('supplier_order')) {
4453 if (empty($productstatcache[$orderLine->fk_product]['statsordersupplier'])) {
4454 $genericProduct->load_stats_commande_fournisseur(0, '3');
4455 $productstatcache[$orderLine->fk_product]['statsordersupplier'] = $genericProduct->stats_commande_fournisseur['qty'];
4456 }
4457 $genericProduct->stats_commande_fournisseur['qty'] = $productstatcache[$orderLine->fk_product]['statsordersupplier'];
4458 $stockordersupplier = $genericProduct->stats_commande_fournisseur['qty'];
4459 }
4460 }
4461 }
4462
4463 $textinfo .= $reliquat . ' x ' . $orderLine->ref . '&nbsp;' . dol_trunc($orderLine->product_label, 20);
4464 $textinfo .= ' ' . $langs->trans("Available") . '&nbsp;&nbsp;' . $genericProduct->stock_reel . '..' . $stockorder;
4465
4466 if ($stockorder && $genericProduct->stock_reel < ($genericProduct->stock_reel - $stockorder + $reliquat)) {
4467 $warning++;
4468 $textwarning .= '<span class="warning">' . $langs->trans("Available") . '&nbsp;&nbsp;' . $genericProduct->stock_reel . '..' . $stockorder . '</span>';
4469 } else {
4470 if ($reliquat > $genericProduct->stock_reel) {
4471 $textinfo .= ' <span class="warning">' . $langs->trans("Available") . '&nbsp;&nbsp;' . $genericProduct->stock_reel . '</span>';
4472 } else {
4473 $textinfo .= ' <span class="ok">' . $langs->trans("Available") . '&nbsp;&nbsp;' . $genericProduct->stock_reel . '</span>';
4474 }
4475 }
4476
4477 if (isModEnabled('supplier_order')) {
4478 $textinfo .= '&nbsp;' . $langs->trans("SupplierOrder") . '&nbsp;&nbsp;' . $stockordersupplier;
4479 }
4480 if ($reliquat != $orderLine->qty) {
4481 $textinfo .= ' <span class="opacitymedium">' . $langs->trans("QtyInOtherShipments") . ' ' . ($orderLine->qty - $reliquat) . '</span>';
4482 }
4483 $textinfo .= '<br>';
4484 }
4485
4486 if ($reliquat > $genericProduct->stock_reel) {
4487 $notshippable++;
4488 }
4489 }
4490 }
4491 }
4492
4493 if ($nbprod) {
4494 if (!$has_reliquat) {
4495 $texticon = img_picto('', 'statut5', '', 0, 0, 0, '', 'paddingleft');
4496 $textinfo = $texticon . ' ' . $langs->trans("Shipped");
4497 $result['shippable'] = true;
4498 } elseif ($notshippable) {
4499 $texticon = img_picto('', 'dolly', '', 0, 0, 0, '', 'error paddingleft');
4500 $textinfo = $texticon . ' ' . $langs->trans("NonShippable") . '<br>' . $textinfo;
4501 $result['shippable'] = false;
4502 } else {
4503 $texticon = img_picto('', 'dolly', '', 0, 0, 0, '', 'green paddingleft');
4504 $textinfo = $texticon . ' ' . $langs->trans("Shippable") . '<br>' . $textinfo;
4505 $result['shippable'] = true;
4506 }
4507 $result['has_product'] = true;
4508 $result['texticon'] = $texticon;
4509 $result['textinfo'] = $textinfo;
4510 $result['warning'] = !empty($warning);
4511 }
4512
4513 return $result;
4514 }
4515}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
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 the order date.
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.
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.
initAsSpecimen($param=array())
Initialise an instance with random values.
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.
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 sale 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.
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.
setCategories($categories)
Sets object to given categories.
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)
Delete an order line.
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.
getShippableInfos(array $options=array())
Compute shippable status and tooltip/icon for the order.
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.
cancel($user, $idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
createFromClone(User $user, $socid=0, $forceentity=null)
Load an object from its id and create a new one in database.
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)
checkActiveProductInLines($status='onsale')
Check if all products have the right status (on sale, on buy) called during validation of propal,...
deleteExtraFields()
Delete all extra fields values for the current object.
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.
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 getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
static getIdFromCode($dbs, $code)
Get id 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 translations.
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:169
setSignedStatusCommon(User $user, int $status, int $notrigger=0, string $triggercode='')
Set signed status & call trigger with context message.
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_delete_preview($object)
Delete all preview files linked to object instance.
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
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 '.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
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.
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.
getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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 rate, 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...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
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, $notused, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:487