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