dolibarr 23.0.4
commande.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2011 Jean Heimburger <jean@tiaris.info>
8 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
9 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
10 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
11 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
12 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
13 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2021-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 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
40require_once DOL_DOCUMENT_ROOT.'/commande/class/orderline.class.php';
41require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
42require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
43require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
44require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
45require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
46
47
51class Commande extends CommonOrder
52{
53 use CommonSubtotal;
54
58 public $element = 'commande';
59
64 public $TRIGGER_PREFIX = 'ORDER';
65
69 public $table_element = 'commande';
70
74 public $table_element_line = 'commandedet';
75
79 public $class_element_line = 'OrderLine';
80
84 public $fk_element = 'fk_commande';
85
89 public $picto = 'order';
90
95 public $restrictiononfksoc = 1;
96
100 protected $table_ref_field = 'ref';
101
105 public $socid;
106
110 public $ref_client;
111
115 public $ref_customer;
116
120 public $contactid;
121
127 public $statut;
128
133 public $status;
134
138 public $billed;
139
143 public $date_lim_reglement;
147 public $cond_reglement_code;
148
152 public $cond_reglement_doc;
153
158 public $deposit_percent;
159
163 public $fk_account;
164
168 public $mode_reglement;
169
173 public $mode_reglement_id;
174
178 public $mode_reglement_code;
179
184 public $availability_id;
185
190 public $availability_code;
191
196 public $availability;
197
201 public $demand_reason_id;
202
206 public $demand_reason_code;
207
211 public $date;
212
218 public $date_commande;
219
223 public $delivery_date;
224
228 public $fk_remise_except;
229
234 public $remise_percent;
235
239 public $source;
240
245 public $signed_status = 0;
246
250 public $warehouse_id;
251
255 public $extraparams = array();
256
260 public $user_author_id;
261
265 public $line;
266
270 public $lines = array();
271
272
276 public $module_source;
280 public $pos_source;
281
285 public $expeditions;
286
290 public $online_payment_url;
291
292
293
319 // BEGIN MODULEBUILDER PROPERTIES
323 public $fields = array(
324 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
325 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
326 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 25),
327 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 26),
328 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 28),
329 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 20),
330 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 25),
331 'date_commande' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'csslist' => 'nowraponall'),
332 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 62, 'csslist' => 'nowraponall'),
333 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'csslist' => 'nowraponall'),
334 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 85),
335 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => -1, 'position' => 90),
336 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => -1, 'position' => 95),
337 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
338 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
339 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
340 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
341 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
342 'signed_status' => array('type' => 'smallint(6)', 'label' => 'SignedStatus', 'enabled' => 1, 'visible' => -1, 'position' => 146, 'arrayofkeyval' => array(0 => 'NoSignature', 1 => 'SignedSender', 2 => 'SignedReceiver', 9 => 'SignedAll')),
343 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 150),
344 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 155),
345 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 160),
346 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 170),
347 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 175),
348 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 180),
349 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 181),
350 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 185),
351 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'csslist' => 'nowraponall'),
352 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 195),
353 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'DefaultWarehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 200, 'nodepth' => 1),
354 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 205),
355 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 210),
356 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
357 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 225),
358 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 230),
359 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 235),
360 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
361 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245),
362 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
363 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
364 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 260, 'isameasure' => 1),
365 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 265, 'isameasure' => 1),
366 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 270),
367 'module_source' => array('type' => 'varchar(32)', 'label' => 'POSModule', 'enabled' => 1, 'visible' => -1, 'position' => 275),
368 'pos_source' => array('type' => 'varchar(32)', 'label' => 'POSTerminal', 'enabled' => 1, 'visible' => -1, 'position' => 280),
369 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 300),
370 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 302),
371 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 304, 'csslist' => 'nowraponall'),
372 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 306),
373 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 400),
374 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'position' => 500),
375 );
376 // END MODULEBUILDER PROPERTIES
377
382
386 const STATUS_CANCELED = -1;
390 const STATUS_DRAFT = 0;
398 const STATUS_SHIPMENTONPROCESS = 2; // We set this status when a shipment is validated
399
405
409 const STATUS_CLOSED = 3;
410
411 /*
412 * No signature
413 */
414 const STATUS_NO_SIGNATURE = 0;
415
416 /*
417 * Signed by sender
418 */
419 const STATUS_SIGNED_SENDER = 1;
420
421 /*
422 * Signed by receiver
423 */
424 const STATUS_SIGNED_RECEIVER = 2;
425
426 /*
427 * Signed by all
428 */
429 const STATUS_SIGNED_ALL = 9; // To handle future kind of signature (ex: tripartite contract)
430
431
437 public function __construct($db)
438 {
439 $this->db = $db;
440
441 $this->ismultientitymanaged = 1;
442 $this->isextrafieldmanaged = 1;
443
444 $this->fields['ref_ext']['visible'] = getDolGlobalInt('MAIN_LIST_SHOW_REF_EXT');
445 }
446
454 public function getNextNumRef($soc)
455 {
456 global $langs, $conf;
457 $langs->load("order");
458
459 if (getDolGlobalString('COMMANDE_ADDON')) {
460 $mybool = false;
461
462 $file = getDolGlobalString('COMMANDE_ADDON') . ".php";
463 $classname = getDolGlobalString('COMMANDE_ADDON');
464
465 // Include file with class
466 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
467 foreach ($dirmodels as $reldir) {
468 $dir = dol_buildpath($reldir."core/modules/commande/");
469
470 // Load file with numbering class (if found)
471 $mybool = ((bool) @include_once $dir.$file) || $mybool;
472 }
473
474 if (!$mybool) {
475 dol_print_error(null, "Failed to include file ".$file);
476 return '';
477 }
478
479 $obj = new $classname();
481 '@phan-var-force ModeleNumRefCommandes $obj';
482
483 $numref = $obj->getNextValue($soc, $this);
484
485 if ($numref != "") {
486 return $numref;
487 } else {
488 $this->error = $obj->error;
489 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
490 return "";
491 }
492 } else {
493 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
494 return "";
495 }
496 }
497
498
507 public function valid($user, $idwarehouse = 0, $notrigger = 0)
508 {
509 global $conf, $langs;
510
511 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
512
513 $error = 0;
514
515 // Protection
516 if ($this->status == self::STATUS_VALIDATED) {
517 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
518 return 0;
519 }
520
521 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
522 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
523 $this->error = 'NotEnoughPermissions';
524 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
525 return -1;
526 }
527 if (empty($this->socid)) {
528 $this->error = 'ErrorWrongParameters';
529 return -1;
530 }
531 if (!getDolGlobalBool('ORDER_NOCHECK_ONSALE_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines()) {
532 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
533 return -1;
534 }
535 $now = dol_now();
536
537 $this->db->begin();
538
539 // Definition du nom de module de numerotation de commande
540 $soc = new Societe($this->db);
541 $soc->fetch($this->socid);
542
543 // Class of company linked to order
544 $result = $soc->setAsCustomer();
545
546 // Define new ref
547 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
548 $num = $this->getNextNumRef($soc);
549 } else {
550 $num = (string) $this->ref;
551 }
552 $this->newref = dol_sanitizeFileName($num);
553
554 // Validate
555 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
556 $sql .= " SET ref = '".$this->db->escape($num)."',";
557 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
558 $sql .= " date_valid = '".$this->db->idate($now)."',";
559 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
560 $sql .= " fk_user_modif = ".((int) $user->id);
561 $sql .= " WHERE rowid = ".((int) $this->id);
562
563 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
564 $resql = $this->db->query($sql);
565 if (!$resql) {
566 dol_print_error($this->db);
567 $this->error = $this->db->lasterror();
568 $error++;
569 }
570
571 if (!$error) {
572 // If stock is incremented on validate order, we must increment it
573 if ($result >= 0 && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
574 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
575 $langs->load("agenda");
576
577 // Loop on each line
578 $cpt = count($this->lines);
579 for ($i = 0; $i < $cpt; $i++) {
580 if ($this->lines[$i]->fk_product > 0) {
581 $mouvP = new MouvementStock($this->db);
582 $mouvP->origin = &$this;
583 $mouvP->setOrigin($this->element, $this->id);
584 // We decrement stock of product (and sub-products)
585 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
586 if ($result < 0) {
587 $error++;
588 $this->setErrorsFromObject($mouvP);
589 }
590 }
591 if ($error) {
592 break;
593 }
594 }
595 }
596 }
597
598 if (!$error && !$notrigger) {
599 // Call trigger
600 $result = $this->call_trigger('ORDER_VALIDATE', $user);
601 if ($result < 0) {
602 $error++;
603 }
604 // End call triggers
605 }
606
607 if (!$error) {
608 $this->oldref = $this->ref;
609
610 // Rename directory if dir was a temporary ref
611 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
612 // Now we rename also files into index
613 $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)."'";
614 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
615 $resql = $this->db->query($sql);
616 if (!$resql) {
617 $error++;
618 $this->error = $this->db->lasterror();
619 }
620 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
621 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
622 $resql = $this->db->query($sql);
623 if (!$resql) {
624 $error++;
625 $this->error = $this->db->lasterror();
626 }
627
628 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
629 $oldref = dol_sanitizeFileName($this->ref);
630 $newref = dol_sanitizeFileName($num);
631 $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
632 $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
633 if (!$error && file_exists($dirsource)) {
634 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
635
636 if (@rename($dirsource, $dirdest)) {
637 dol_syslog("Rename ok");
638 // Rename docs starting with $oldref with $newref
639 $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
640 foreach ($listoffiles as $fileentry) {
641 $dirsource = $fileentry['name'];
642 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
643 $dirsource = $fileentry['path'].'/'.$dirsource;
644 $dirdest = $fileentry['path'].'/'.$dirdest;
645 @rename($dirsource, $dirdest);
646 }
647 }
648 }
649 }
650 }
651
652 // Set new ref and current status
653 if (!$error) {
654 $this->ref = $num;
655 $this->statut = self::STATUS_VALIDATED; // deprecated
657 }
658
659 if (!$error) {
660 $this->db->commit();
661 return 1;
662 } else {
663 $this->db->rollback();
664 return -1;
665 }
666 }
667
668 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
676 public function setDraft($user, $idwarehouse = -1)
677 {
678 //phpcs:enable
679 global $langs;
680
681 $error = 0;
682
683 // Protection
684 if ($this->status <= self::STATUS_DRAFT && !getDolGlobalInt('ORDER_REOPEN_TO_DRAFT')) {
685 return 0;
686 }
687
688 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
689 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
690 $this->error = 'Permission denied';
691 return -1;
692 }
693
694 dol_syslog(__METHOD__, LOG_DEBUG);
695
696 $this->db->begin();
697
698 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
699 $sql .= " SET fk_statut = ".((int) self::STATUS_DRAFT).",";
700 $sql .= " fk_user_modif = ".((int) $user->id);
701 $sql .= " WHERE rowid = ".((int) $this->id);
702
703 if ($this->db->query($sql)) {
704 $this->oldcopy = clone $this;
705
706 // If stock is decremented on validate order, we must reincrement it
707 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
708 $result = 0;
709
710 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
711 $langs->load("agenda");
712
713 $num = count($this->lines);
714 for ($i = 0; $i < $num; $i++) {
715 if ($this->lines[$i]->fk_product > 0) {
716 $mouvP = new MouvementStock($this->db);
717 $mouvP->origin = &$this;
718 $mouvP->setOrigin($this->element, $this->id);
719 // We increment stock of product (and sub-products)
720 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
721 if ($result < 0) {
722 $error++;
723 $this->setErrorsFromObject($mouvP);
724 break;
725 }
726 }
727 }
728 }
729
730 if (!$error) {
731 // Call trigger
732 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
733 if ($result < 0) {
734 $error++;
735 }
736 }
737
738 if (!$error) {
739 $this->statut = self::STATUS_DRAFT; // deprecated
740 $this->status = self::STATUS_DRAFT;
741 $this->db->commit();
742 return 1;
743 } else {
744 $this->db->rollback();
745 return -1;
746 }
747 } else {
748 $this->error = $this->db->error();
749 $this->db->rollback();
750 return -1;
751 }
752 }
753
754
755 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
763 public function set_reopen($user)
764 {
765 // phpcs:enable
766 $error = 0;
767
768 if ($this->status != self::STATUS_CANCELED && $this->status != self::STATUS_CLOSED) {
769 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
770 return 0;
771 }
772
773 $this->db->begin();
774
775 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
776 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
777 $sql .= " fk_user_modif = ".((int) $user->id);
778 $sql .= " WHERE rowid = ".((int) $this->id);
779
780 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
781 $resql = $this->db->query($sql);
782 if ($resql) {
783 // Call trigger
784 $result = $this->call_trigger('ORDER_REOPEN', $user);
785 if ($result < 0) {
786 $error++;
787 }
788 // End call triggers
789 } else {
790 $error++;
791 $this->error = $this->db->lasterror();
792 dol_print_error($this->db);
793 }
794
795 if (!$error) {
796 $this->statut = self::STATUS_VALIDATED; // deprecated
798 $this->billed = 0;
799
800 $this->db->commit();
801 return 1;
802 } else {
803 foreach ($this->errors as $errmsg) {
804 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
805 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
806 }
807 $this->db->rollback();
808 return -1 * $error;
809 }
810 }
811
819 public function cloture($user, $notrigger = 0)
820 {
821 $error = 0;
822
823 $usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
824 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'close')));
825
826 if ($usercanclose) {
827 if ($this->status == self::STATUS_CLOSED) {
828 return 0;
829 }
830 $this->db->begin();
831
832 $now = dol_now();
833
834 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
835 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
836 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
837 $sql .= " date_cloture = '".$this->db->idate($now)."',";
838 $sql .= " fk_user_modif = ".((int) $user->id);
839 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
840
841 if ($this->db->query($sql)) {
842 if (!$notrigger) {
843 // Call trigger
844 $result = $this->call_trigger('ORDER_CLOSE', $user);
845 if ($result < 0) {
846 $error++;
847 }
848 // End call triggers
849 }
850
851 if (!$error) {
852 $this->statut = self::STATUS_CLOSED; // deprecated
854
855 $this->db->commit();
856 return 1;
857 } else {
858 $this->db->rollback();
859 return -1;
860 }
861 } else {
862 $this->error = $this->db->lasterror();
863
864 $this->db->rollback();
865 return -1;
866 }
867 }
868 return 0;
869 }
870
882 public function setCategories($categories)
883 {
884 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
885 return parent::setCategoriesCommon($categories, Categorie::TYPE_ORDER);
886 }
887
896 public function cancel($user, $idwarehouse = -1)
897 {
898 global $user, $langs;
899
900 $error = 0;
901
902 $this->db->begin();
903
904 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
905 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
906 $sql .= " fk_user_modif = ".((int) $user->id);
907 $sql .= " WHERE rowid = ".((int) $this->id);
908 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
909
910 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
911 if ($this->db->query($sql)) {
912 // If stock is decremented on validate order, we must reincrement it
913 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
914 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
915 $langs->load("agenda");
916
917 $num = count($this->lines);
918 for ($i = 0; $i < $num; $i++) {
919 if ($this->lines[$i]->fk_product > 0) {
920 $mouvP = new MouvementStock($this->db);
921 $mouvP->setOrigin($this->element, $this->id);
922 // We increment stock of product (and sub-products)
923 $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
924 if ($result < 0) {
925 $error++;
926 $this->setErrorsFromObject($mouvP);
927 break;
928 }
929 }
930 }
931 }
932
933 if (!$error) {
934 // Call trigger
935 $result = $this->call_trigger('ORDER_CANCEL', $user);
936 if ($result < 0) {
937 $error++;
938 }
939 // End call triggers
940 }
941
942 if (!$error) {
943 $this->statut = self::STATUS_CANCELED; // deprecated
945 $this->db->commit();
946 return 1;
947 } else {
948 foreach ($this->errors as $errmsg) {
949 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
950 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
951 }
952 $this->db->rollback();
953 return -1 * $error;
954 }
955 } else {
956 $this->error = $this->db->error();
957 $this->db->rollback();
958 return -1;
959 }
960 }
961
970 public function create($user, $notrigger = 0)
971 {
972 global $conf, $langs, $mysoc;
973 $error = 0;
974
975 // Clean parameters
976
977 if (empty($this->socid)) {
978 $this->error = 'ErrorWrongParameters';
979 return -1;
980 }
981
982 // Set tmp vars
983 $date = ($this->date_commande ? $this->date_commande : $this->date);
984 $this->import_key = trim((string) $this->import_key);
985 $delivery_date = $this->delivery_date;
986
987 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
988 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
989 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
990 } else {
991 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
992 }
993 if (empty($this->fk_multicurrency)) {
994 $this->multicurrency_code = $conf->currency;
995 $this->fk_multicurrency = 0;
996 $this->multicurrency_tx = 1;
997 }
998 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
999 $this->entity = setEntity($this);
1000
1001 dol_syslog(get_class($this)."::create user=".$user->id);
1002
1003 // Check parameters
1004 if (!empty($this->ref)) { // We check that ref is not already used
1005 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1006 if ($result > 0) {
1007 $this->error = 'ErrorRefAlreadyExists';
1008 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1009 $this->db->rollback();
1010 return -1;
1011 }
1012 }
1013
1014 $soc = new Societe($this->db);
1015 $result = $soc->fetch($this->socid);
1016 if ($result < 0) {
1017 $this->error = "Failed to fetch company";
1018 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1019 return -2;
1020 }
1021 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
1022 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
1023 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1024 return -1;
1025 }
1026
1027 $this->date_creation = dol_now();
1028
1029 $this->db->begin();
1030
1031 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
1032 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
1033 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
1034 $sql .= ", fk_shipping_method";
1035 $sql .= ", fk_warehouse";
1036 $sql .= ", fk_incoterms, location_incoterms";
1037 $sql .= ", entity, module_source, pos_source";
1038 $sql .= ", fk_multicurrency";
1039 $sql .= ", multicurrency_code";
1040 $sql .= ", multicurrency_tx";
1041 $sql .= ", import_key";
1042 $sql .= ")";
1043 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($this->date_creation)."', ".($user->id > 0 ? ((int) $user->id) : "null");
1044 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1045 $sql .= ", '".$this->db->idate($date)."'";
1046 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape((string) $this->source) : 'null');
1047 $sql .= ", '".$this->db->escape($this->note_private)."'";
1048 $sql .= ", '".$this->db->escape($this->note_public)."'";
1049 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
1050 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
1051 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1052 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
1053 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
1054 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
1055 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1056 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
1057 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
1058 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1059 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
1060 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1061 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1062 $sql .= ", ".(int) $this->fk_incoterms;
1063 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1064 $sql .= ", ".(int) $this->entity;
1065 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1066 $sql .= ", ".((!is_null($this->pos_source) && $this->pos_source != '') ? "'".$this->db->escape($this->pos_source)."'" : "null"); // Can be null, '', '0', '1'
1067 $sql .= ", ".(int) $this->fk_multicurrency;
1068 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1069 $sql .= ", ".(float) $this->multicurrency_tx;
1070 $sql .= ", '".$this->db->escape($this->import_key)."'";
1071 $sql .= ")";
1072
1073 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1074 $resql = $this->db->query($sql);
1075 if ($resql) {
1076 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1077
1078 if ($this->id) {
1079 $fk_parent_line = 0;
1080 $num = count($this->lines);
1081
1082 /*
1083 * Insert products details into db
1084 */
1085 for ($i = 0; $i < $num; $i++) {
1086 $line = $this->lines[$i];
1087
1088 // Test and convert into OrderLine object this->lines[$i]. When coming from REST API, we may still have an array
1089 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1090 if (!is_object($line)) {
1091 $lineobj = new OrderLine($this->db);
1092 foreach ($line as $key => $val) {
1093 $lineobj->$key = $val;
1094 }
1095 $line = $lineobj;
1096 $this->lines[$i] = $line;
1097 }
1098
1099 // Reset fk_parent_line for no child products and special product
1100 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1101 $fk_parent_line = 0;
1102 }
1103
1104 // Complete vat rate with code
1105 $vatrate = $line->tva_tx;
1106 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1107 $vatrate .= ' ('.$line->vat_src_code.')';
1108 }
1109
1110 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1111 $originid = $line->origin_id;
1112 $origintype = empty($line->origin_type) ? $line->origin : $line->origin_type;
1113 } else {
1114 $originid = $line->id;
1115 $origintype = $this->element;
1116 }
1117
1118 // ref_ext
1119 if (empty($line->ref_ext)) {
1120 $line->ref_ext = '';
1121 }
1122
1123 $result = $this->addline(
1124 $line->desc,
1125 $line->subprice,
1126 $line->qty,
1127 $vatrate,
1128 $line->localtax1_tx,
1129 $line->localtax2_tx,
1130 $line->fk_product,
1131 $line->remise_percent,
1132 $line->info_bits,
1133 $line->fk_remise_except,
1134 'HT',
1135 0,
1136 $line->date_start,
1137 $line->date_end,
1138 $line->product_type,
1139 $line->rang,
1140 $line->special_code,
1141 $fk_parent_line,
1142 $line->fk_fournprice,
1143 $line->pa_ht,
1144 $line->label,
1145 $line->array_options,
1146 $line->fk_unit,
1147 (string) $origintype,
1148 $originid,
1149 0,
1150 $line->ref_ext,
1151 1
1152 );
1153
1154 if ($result < 0) {
1155 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1156 $this->error = $this->db->lasterror();
1157 $this->errors[] = $this->error;
1158 dol_print_error($this->db);
1159 }
1160 $this->db->rollback();
1161 return -1;
1162 }
1163 // Defined the new fk_parent_line
1164 if ($result > 0 && $line->product_type == 9) {
1165 $fk_parent_line = $result;
1166 }
1167 }
1168
1169 $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.
1170
1171 // update ref
1172 $initialref = '(PROV'.$this->id.')';
1173 if (!empty($this->ref)) {
1174 $initialref = $this->ref;
1175 }
1176
1177 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1178 if ($this->db->query($sql)) {
1179 $this->ref = $initialref;
1180
1181 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1182 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1183 }
1184
1185 // Add object linked
1186 if (!empty($this->linked_objects) && is_array($this->linked_objects)) {
1187 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1188 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, ...))
1189 foreach ($tmp_origin_id as $origin_id) {
1190 $ret = $this->add_object_linked($origin, $origin_id);
1191 if (!$ret) {
1192 $this->error = $this->db->lasterror();
1193 $error++;
1194 }
1195 }
1196 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1197 $origin_id = $tmp_origin_id;
1198 $ret = $this->add_object_linked($origin, $origin_id);
1199 if (!$ret) {
1200 $this->error = $this->db->lasterror();
1201 $error++;
1202 }
1203 }
1204 }
1205 }
1206
1207 if (!$error && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1208 $originforcontact = empty($this->origin_type) ? $this->origin : $this->origin_type;
1209 $originidforcontact = $this->origin_id;
1210 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1211 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1212 $exp = new Expedition($this->db);
1213 $exp->fetch($this->origin_id);
1214 $exp->fetchObjectLinked();
1215 if (count($exp->linkedObjectsIds['commande']) > 0) {
1216 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1217 $originforcontact = 'commande';
1218 if (is_object($value)) {
1219 $originidforcontact = $value->id;
1220 } else {
1221 $originidforcontact = $value;
1222 }
1223 break; // We take first one
1224 }
1225 }
1226 }
1227
1228 $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";
1229 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1230
1231 $resqlcontact = $this->db->query($sqlcontact);
1232 if ($resqlcontact) {
1233 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1234 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1235 $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
1236 }
1237 } else {
1238 dol_print_error($this->db);
1239 }
1240 }
1241
1242 if (!$error) {
1243 $result = $this->insertExtraFields();
1244 if ($result < 0) {
1245 $error++;
1246 }
1247 }
1248
1249 if (!$error && !$notrigger) {
1250 // Call trigger
1251 $result = $this->call_trigger('ORDER_CREATE', $user);
1252 if ($result < 0) {
1253 $error++;
1254 }
1255 // End call triggers
1256 }
1257
1258 if (!$error) {
1259 $this->db->commit();
1260 return $this->id;
1261 } else {
1262 $this->db->rollback();
1263 return -1 * $error;
1264 }
1265 } else {
1266 $this->error = $this->db->lasterror();
1267 $this->db->rollback();
1268 return -1;
1269 }
1270 }
1271
1272 return 0;
1273 } else {
1274 $this->error = $this->db->lasterror();
1275 $this->db->rollback();
1276 return -1;
1277 }
1278 }
1279
1280
1288 public function createFromClone(User $user, $socid = 0)
1289 {
1290 global $user, $hookmanager;
1291
1292 $error = 0;
1293
1294 $this->db->begin();
1295
1296 // get lines so they will be clone
1297 foreach ($this->lines as $line) {
1298 $line->fetch_optionals();
1299 }
1300
1301 // Load source object
1302 $objFrom = clone $this;
1303
1304 // Change socid if needed
1305 if (!empty($socid) && $socid != $this->socid) {
1306 $objsoc = new Societe($this->db);
1307
1308 if ($objsoc->fetch($socid) > 0) {
1309 $this->socid = $objsoc->id;
1310 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1311 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : '0');
1312 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1313 $this->fk_project = 0;
1314 $this->fk_delivery_address = 0;
1315 }
1316
1317 // TODO Change product price if multi-prices
1318 }
1319
1320 $this->id = 0;
1321 $this->ref = '';
1322 $this->statut = self::STATUS_DRAFT; // deprecated
1323 $this->status = self::STATUS_DRAFT;
1324
1325 // Clear fields
1326 $this->user_author_id = $user->id;
1327 $this->user_validation_id = 0;
1328 $this->date = dol_now();
1329 $this->date_commande = dol_now();
1330 $this->date_creation = '';
1331 $this->date_validation = '';
1332 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1333 $this->ref_client = '';
1334 $this->ref_customer = '';
1335 }
1336
1337 // Do not clone ref_ext
1338 $num = count($this->lines);
1339 for ($i = 0; $i < $num; $i++) {
1340 $this->lines[$i]->ref_ext = '';
1341 }
1342
1343 // Create clone
1344 $this->context['createfromclone'] = 'createfromclone';
1345 $result = $this->create($user);
1346 if ($result < 0) {
1347 $error++;
1348 }
1349
1350 if (!$error) {
1351 // copy internal contacts
1352 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1353 $error++;
1354 }
1355 }
1356
1357 if (!$error) {
1358 // copy external contacts if same company
1359 if ($this->socid == $objFrom->socid) {
1360 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1361 $error++;
1362 }
1363 }
1364 }
1365
1366 if (!$error) {
1367 // Hook of thirdparty module
1368 if (is_object($hookmanager)) {
1369 $parameters = array('objFrom' => $objFrom);
1370 $action = '';
1371 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1372 if ($reshook < 0) {
1373 $this->setErrorsFromObject($hookmanager);
1374 $error++;
1375 }
1376 }
1377 }
1378
1379 unset($this->context['createfromclone']);
1380
1381 // End
1382 if (!$error) {
1383 $this->db->commit();
1384 return $this->id;
1385 } else {
1386 $this->db->rollback();
1387 return -1;
1388 }
1389 }
1390
1391
1399 public function createFromProposal($object, User $user)
1400 {
1401 global $conf, $hookmanager;
1402
1403 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1404 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1405
1406 $error = 0;
1407
1408 $this->date_commande = dol_now();
1409 $this->date = dol_now();
1410 $this->source = 0;
1411
1412 $num = count($object->lines);
1413 for ($i = 0; $i < $num; $i++) {
1414 $line = new OrderLine($this->db);
1415
1416 $line->libelle = $object->lines[$i]->libelle;
1417 $line->label = $object->lines[$i]->label;
1418 $line->desc = $object->lines[$i]->desc;
1419 $line->price = $object->lines[$i]->price;
1420 $line->subprice = $object->lines[$i]->subprice;
1421 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1422 $line->tva_tx = $object->lines[$i]->tva_tx;
1423 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1424 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1425 $line->qty = $object->lines[$i]->qty;
1426 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1427 $line->remise_percent = $object->lines[$i]->remise_percent;
1428 $line->fk_product = $object->lines[$i]->fk_product;
1429 $line->info_bits = $object->lines[$i]->info_bits;
1430 $line->product_type = $object->lines[$i]->product_type;
1431 $line->rang = $object->lines[$i]->rang;
1432 $line->special_code = $object->lines[$i]->special_code;
1433 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1434 $line->fk_unit = $object->lines[$i]->fk_unit;
1435
1436 $line->date_start = $object->lines[$i]->date_start;
1437 $line->date_end = $object->lines[$i]->date_end;
1438
1439 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1440 $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);
1441 $line->pa_ht = $marginInfos[0];
1442 $line->marge_tx = $marginInfos[1];
1443 $line->marque_tx = $marginInfos[2];
1444
1445 $line->origin = $object->element;
1446 $line->origin_id = $object->lines[$i]->id;
1447
1448 // get extrafields from original line
1449 $object->lines[$i]->fetch_optionals();
1450 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1451 $line->array_options[$options_key] = $value;
1452 }
1453
1454 $this->lines[$i] = $line;
1455 }
1456
1457 $this->entity = $object->entity;
1458 $this->socid = $object->socid;
1459 $this->fk_project = $object->fk_project;
1460 $this->cond_reglement_id = $object->cond_reglement_id;
1461 $this->deposit_percent = $object->deposit_percent;
1462 $this->mode_reglement_id = $object->mode_reglement_id;
1463 $this->fk_account = $object->fk_account;
1464 $this->availability_id = $object->availability_id;
1465 $this->demand_reason_id = $object->demand_reason_id;
1466 $this->delivery_date = $object->delivery_date;
1467 $this->shipping_method_id = $object->shipping_method_id;
1468 $this->warehouse_id = $object->warehouse_id;
1469 $this->fk_delivery_address = $object->fk_delivery_address;
1470 $this->contact_id = $object->contact_id;
1471 $this->ref_client = $object->ref_client;
1472 $this->ref_customer = $object->ref_client;
1473
1474 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1475 $this->note_private = $object->note_private;
1476 $this->note_public = $object->note_public;
1477 }
1478
1479 $this->origin = $object->element;
1480 $this->origin_id = $object->id;
1481
1482 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1483 if (isModEnabled('multicurrency')) {
1484 if (!empty($object->multicurrency_code)) {
1485 $this->multicurrency_code = $object->multicurrency_code;
1486 }
1487 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1488 $this->multicurrency_tx = $object->multicurrency_tx;
1489 }
1490
1491 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1492 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1493 $this->fk_multicurrency = $tmparray[0];
1494 $this->multicurrency_tx = $tmparray[1];
1495 } else {
1496 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1497 }
1498 if (empty($this->fk_multicurrency)) {
1499 $this->multicurrency_code = $conf->currency;
1500 $this->fk_multicurrency = 0;
1501 $this->multicurrency_tx = 1;
1502 }
1503 }
1504
1505 // get extrafields from original line
1506 $object->fetch_optionals();
1507
1508 $e = new ExtraFields($this->db);
1509 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1510
1511 foreach ($object->array_options as $options_key => $value) {
1512 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1513 $this->array_options[$options_key] = $value;
1514 }
1515 }
1516 // Possibility to add external linked objects with hooks
1517 $this->linked_objects[$this->origin] = $this->origin_id;
1518 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1519 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1520 }
1521
1522 $ret = $this->create($user);
1523
1524 if ($ret > 0) {
1525 // Actions hooked (by external module)
1526 $hookmanager->initHooks(array('orderdao'));
1527
1528 $parameters = array('objFrom' => $object);
1529 $action = '';
1530 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1531 if ($reshook < 0) {
1532 $this->setErrorsFromObject($hookmanager);
1533 $error++;
1534 }
1535
1536 if (!$error) {
1537 // Validate immediately the order
1538 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1539 $this->fetch($ret);
1540 $this->valid($user);
1541 }
1542 return $ret;
1543 } else {
1544 return -1;
1545 }
1546 } else {
1547 return -1;
1548 }
1549 }
1550
1551
1592 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)
1593 {
1594 global $mysoc, $langs, $user;
1595
1596 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1597 $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";
1598 $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";
1599 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1600
1601 if ($this->status == self::STATUS_DRAFT) {
1602 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1603
1604 // Clean parameters
1605
1606 if (empty($remise_percent)) {
1607 $remise_percent = 0;
1608 }
1609 if (empty($qty)) {
1610 $qty = 0;
1611 }
1612 if (empty($info_bits)) {
1613 $info_bits = 0;
1614 }
1615 if (empty($rang)) {
1616 $rang = 0;
1617 }
1618 if (empty($txtva)) {
1619 $txtva = 0;
1620 }
1621 if (empty($txlocaltax1)) {
1622 $txlocaltax1 = 0;
1623 }
1624 if (empty($txlocaltax2)) {
1625 $txlocaltax2 = 0;
1626 }
1627 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1628 $fk_parent_line = 0;
1629 }
1630 if (empty($this->fk_multicurrency)) {
1631 $this->fk_multicurrency = 0;
1632 }
1633 if (empty($ref_ext)) {
1634 $ref_ext = '';
1635 }
1636
1637 $remise_percent = (float) price2num($remise_percent);
1638 $qty = (float) price2num($qty);
1639 $pu_ht = price2num($pu_ht);
1640 $pu_ht_devise = price2num($pu_ht_devise);
1641 $pu_ttc = price2num($pu_ttc);
1642 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
1643 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1644 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1645 }
1646 $txlocaltax1 = price2num($txlocaltax1);
1647 $txlocaltax2 = price2num($txlocaltax2);
1648 if ($price_base_type == 'HT') {
1649 $pu = $pu_ht;
1650 } else {
1651 $pu = $pu_ttc;
1652 }
1653 $label = trim($label);
1654 $desc = trim($desc);
1655
1656 // Check parameters
1657 if ($type < 0) {
1658 return -1;
1659 }
1660
1661 if ($date_start && $date_end && $date_start > $date_end) {
1662 $langs->load("errors");
1663 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1664 return -1;
1665 }
1666
1667 $this->db->begin();
1668
1669 $product_type = $type;
1670 if (!empty($fk_product) && $fk_product > 0) {
1671 $product = new Product($this->db);
1672 $result = $product->fetch($fk_product);
1673 $product_type = $product->type;
1674
1675 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product->isStockManaged()) {
1676 // get real stock
1677 $productChildrenNb = 0;
1678 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
1679 $productChildrenNb = $product->hasFatherOrChild(1);
1680 }
1681 if ($productChildrenNb > 0) {
1682 // compute real stock from each subcomponent
1683 $product_stock = null;
1684 $product->loadStockForVirtualProduct('warehouseopen', $qty);
1685 foreach ($product->stock_warehouse as $componentStockWarehouse) {
1686 if ($product_stock === null) {
1687 $product_stock = $componentStockWarehouse->real;
1688 } else {
1689 $product_stock = min($product_stock, $componentStockWarehouse->real);
1690 }
1691 }
1692 if ($product_stock === null) {
1693 $product_stock = 0;
1694 }
1695 } else {
1696 $product_stock = $product->stock_reel;
1697 }
1698
1699 if ($product_stock < $qty) {
1700 $langs->load("errors");
1701 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1702 $this->errors[] = $this->error;
1703 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1704 $this->db->rollback();
1706 }
1707 }
1708 }
1709 // Calcul du total TTC et de la TVA pour la ligne a partir de
1710 // qty, pu, remise_percent et txtva
1711 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1712 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1713
1714 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1715
1716 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1717 $tmpproduct = new Product($this->db);
1718 $result = $tmpproduct->fetch($fk_product);
1719 if (abs((float) $qty) < $tmpproduct->packaging) {
1720 $qty = (float) $tmpproduct->packaging;
1721 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'warnings');
1722 } else {
1723 if (!empty($tmpproduct->packaging) && (float) price2num(fmod((float) $qty, (float) $tmpproduct->packaging), 'MS')) {
1724 $coeff = intval(abs((float) $qty) / $tmpproduct->packaging) + 1;
1725 $qty = price2num((float) $tmpproduct->packaging * $coeff, 'MS');
1726 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'warnings');
1727 }
1728 }
1729 }
1730
1731 // Clean vat code
1732 $reg = array();
1733 $vat_src_code = '';
1734 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1735 $vat_src_code = $reg[1];
1736 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1737 }
1738
1739 $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);
1740
1741 /*var_dump($txlocaltax1);
1742 var_dump($txlocaltax2);
1743 var_dump($localtaxes_type);
1744 var_dump($tabprice);
1745 var_dump($tabprice[9]);
1746 var_dump($tabprice[10]);
1747 exit;*/
1748
1749 $total_ht = $tabprice[0];
1750 $total_tva = $tabprice[1];
1751 $total_ttc = $tabprice[2];
1752 $total_localtax1 = $tabprice[9];
1753 $total_localtax2 = $tabprice[10];
1754 $pu_ht = $tabprice[3];
1755
1756 // MultiCurrency
1757 $multicurrency_total_ht = $tabprice[16];
1758 $multicurrency_total_tva = $tabprice[17];
1759 $multicurrency_total_ttc = $tabprice[18];
1760 $pu_ht_devise = $tabprice[19];
1761
1762 // Rang to use
1763 $ranktouse = $rang;
1764
1765 if (empty($ranktouse) || $ranktouse == -1) {
1766 $rangmax = $this->line_max($fk_parent_line);
1767 $ranktouse = $rangmax + 1;
1768 }
1769
1770 // Insert line
1771 $this->line = new OrderLine($this->db);
1772
1773 $this->line->context = $this->context;
1774
1775 $this->line->fk_commande = $this->id;
1776 $this->line->label = $label;
1777 $this->line->desc = $desc;
1778 $this->line->qty = (float) $qty;
1779 $this->line->ref_ext = $ref_ext;
1780
1781 $this->line->vat_src_code = $vat_src_code;
1782 $this->line->tva_tx = $txtva;
1783 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1784 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1785 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1786 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1787 $this->line->fk_product = $fk_product;
1788 $this->line->product_type = $product_type;
1789 $this->line->fk_remise_except = $fk_remise_except;
1790 $this->line->remise_percent = $remise_percent;
1791 $this->line->subprice = (float) $pu_ht;
1792 $this->line->rang = $ranktouse;
1793 $this->line->info_bits = $info_bits;
1794 $this->line->total_ht = (float) $total_ht;
1795 $this->line->total_tva = (float) $total_tva;
1796 $this->line->total_localtax1 = (float) $total_localtax1;
1797 $this->line->total_localtax2 = (float) $total_localtax2;
1798 $this->line->total_ttc = (float) $total_ttc;
1799 $this->line->special_code = $special_code;
1800 if (empty($qty) && empty($special_code)) {
1801 $this->line->special_code = 3;
1802 }
1803 $this->line->origin = $origin;
1804 $this->line->origin_id = $origin_id;
1805 $this->line->fk_parent_line = $fk_parent_line;
1806 $this->line->fk_unit = $fk_unit;
1807
1808 $this->line->date_start = $date_start;
1809 $this->line->date_end = $date_end;
1810
1811 $this->line->fk_fournprice = $fk_fournprice;
1812 $this->line->pa_ht = $pa_ht; // Can be '' when not defined or 0 if defined to 0 or a price value
1813
1814 // Multicurrency
1815 $this->line->fk_multicurrency = $this->fk_multicurrency;
1816 $this->line->multicurrency_code = $this->multicurrency_code;
1817 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
1818 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
1819 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
1820 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
1821
1822 if (is_array($array_options) && count($array_options) > 0) {
1823 $this->line->array_options = $array_options;
1824 }
1825
1826 $result = $this->line->insert($user);
1827 if ($result > 0) {
1828 // Update denormalized fields at the order level
1829 if (empty($noupdateafterinsertline)) {
1830 $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.
1831 }
1832
1833 if ($result > 0) {
1834 if (!isset($this->context['createfromclone'])) {
1835 if (!empty($fk_parent_line)) {
1836 // Always reorder if child line
1837 $this->line_order(true, 'DESC');
1838 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1839 // Update all rank of all other lines starting from the same $ranktouse
1840 foreach ($this->lines as $tmpline) {
1841 if ($tmpline->rang >= $ranktouse) {
1842 if (!empty($tmpline->id)) {
1843 $this->updateRangOfLine($tmpline->id, $tmpline->rang + 1);
1844 }
1845 }
1846 }
1847 }
1848
1849 $this->lines[] = $this->line;
1850 } else {
1851 foreach ($this->lines as $line) {
1852 if ($line->id == $origin_id) {
1853 $this->line->extraparams = $line->extraparams;
1854 $this->line->setExtraParameters();
1855 }
1856 }
1857 }
1858
1859 $this->db->commit();
1860 return $this->line->id;
1861 } else {
1862 $this->db->rollback();
1863 return -1;
1864 }
1865 } else {
1866 $this->setErrorsFromObject($this->line);
1867 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1868 $this->db->rollback();
1869 return -2;
1870 }
1871 } else {
1872 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1873 return -3;
1874 }
1875 }
1876
1877
1878 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1892 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1893 {
1894 // phpcs:enable
1895 global $conf, $mysoc;
1896
1897 if (!$qty) {
1898 $qty = 1;
1899 }
1900
1901 if ($idproduct > 0) {
1902 $prod = new Product($this->db);
1903 $prod->fetch($idproduct);
1904
1905 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1906 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1907 if (empty($tva_tx)) {
1908 $tva_npr = 0;
1909 }
1910 $vat_src_code = ''; // May be defined into tva_tx
1911
1912 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1913 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1914
1915 // multiprix
1916 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
1917 $price = $prod->multiprices[$this->thirdparty->price_level];
1918 $price_ttc = $prod->multiprices_ttc[$this->thirdparty->price_level];
1919 } else {
1920 $price = $prod->price;
1921 $price_ttc = $prod->price_ttc;
1922 }
1923
1924 $line = new OrderLine($this->db);
1925
1926 $line->context = $this->context;
1927
1928 $line->fk_product = $idproduct;
1929 $line->desc = $prod->description;
1930 $line->qty = $qty;
1931 $line->subprice = $price;
1932 $line->subprice_ttc = $price_ttc;
1933 $line->remise_percent = $remise_percent;
1934 $line->vat_src_code = $vat_src_code;
1935 $line->tva_tx = $tva_tx;
1936 $line->localtax1_tx = $localtax1_tx;
1937 $line->localtax2_tx = $localtax2_tx;
1938
1939 $line->product_ref = $prod->ref;
1940 $line->product_label = $prod->label;
1941 $line->product_desc = $prod->description;
1942 $line->fk_unit = $prod->fk_unit;
1943
1944 // Save the start and end date of the line in the object
1945 if ($date_start) {
1946 $line->date_start = $date_start;
1947 }
1948 if ($date_end) {
1949 $line->date_end = $date_end;
1950 }
1951
1952 $this->lines[] = $line;
1953
1971 }
1972 }
1973
1974
1984 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1985 {
1986 // Check parameters
1987 if (empty($id) && empty($ref) && empty($ref_ext)) {
1988 return -1;
1989 }
1990
1991 $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';
1992 $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';
1993 $sql .= ', c.fk_account';
1994 $sql .= ', c.date_commande, c.date_valid, c.tms';
1995 $sql .= ', c.date_livraison as delivery_date';
1996 $sql .= ', c.fk_shipping_method';
1997 $sql .= ', c.fk_warehouse';
1998 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1999 $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';
2000 $sql .= ', c.fk_incoterms, c.location_incoterms';
2001 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
2002 $sql .= ", c.module_source, c.pos_source";
2003 $sql .= ", i.libelle as label_incoterms";
2004 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
2005 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
2006 $sql .= ', ca.code as availability_code, ca.label as availability_label';
2007 $sql .= ', dr.code as demand_reason_code';
2008 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
2009 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
2010 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
2011 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
2012 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
2013 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
2014
2015 if ($id) {
2016 $sql .= " WHERE c.rowid = ".((int) $id);
2017 } else {
2018 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
2019 }
2020
2021 if ($ref) {
2022 $sql .= " AND c.ref = '".$this->db->escape($ref)."'";
2023 }
2024 if ($ref_ext) {
2025 $sql .= " AND c.ref_ext = '".$this->db->escape($ref_ext)."'";
2026 }
2027
2028 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2029 $result = $this->db->query($sql);
2030 if ($result) {
2031 $obj = $this->db->fetch_object($result);
2032 if ($obj) {
2033 $this->id = $obj->rowid;
2034 $this->entity = $obj->entity;
2035
2036 $this->ref = $obj->ref;
2037 $this->ref_client = $obj->ref_client;
2038 $this->ref_customer = $obj->ref_client;
2039 $this->ref_ext = $obj->ref_ext;
2040
2041 $this->socid = $obj->fk_soc;
2042 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
2043
2044 $this->fk_project = $obj->fk_project;
2045 $this->project = null; // Clear if another value was already set by fetch_projet
2046
2047 $this->statut = $obj->status; // deprecated
2048 $this->status = $obj->status;
2049
2050 $this->user_author_id = $obj->fk_user_author;
2051 $this->user_creation_id = $obj->fk_user_author;
2052 $this->user_validation_id = $obj->fk_user_valid;
2053 $this->user_modification_id = $obj->fk_user_modif;
2054 $this->total_ht = $obj->total_ht;
2055 $this->total_tva = $obj->total_tva;
2056 $this->total_localtax1 = $obj->total_localtax1;
2057 $this->total_localtax2 = $obj->total_localtax2;
2058 $this->total_ttc = $obj->total_ttc;
2059 $this->date = $this->db->jdate($obj->date_commande);
2060 $this->date_commande = $this->db->jdate($obj->date_commande);
2061 $this->date_creation = $this->db->jdate($obj->date_creation);
2062 $this->date_validation = $this->db->jdate($obj->date_valid);
2063 $this->date_modification = $this->db->jdate($obj->tms);
2064 $this->source = $obj->source;
2065 $this->billed = $obj->billed;
2066 $this->note = $obj->note_private; // deprecated
2067 $this->note_private = $obj->note_private;
2068 $this->note_public = $obj->note_public;
2069 $this->model_pdf = $obj->model_pdf;
2070 $this->last_main_doc = $obj->last_main_doc;
2071 $this->mode_reglement_id = $obj->fk_mode_reglement;
2072 $this->mode_reglement_code = $obj->mode_reglement_code;
2073 $this->mode_reglement = $obj->mode_reglement_libelle;
2074 $this->cond_reglement_id = $obj->fk_cond_reglement;
2075 $this->cond_reglement_code = $obj->cond_reglement_code;
2076 $this->cond_reglement = $obj->cond_reglement_libelle;
2077 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
2078 $this->deposit_percent = $obj->deposit_percent;
2079 $this->fk_account = $obj->fk_account;
2080 $this->availability_id = $obj->fk_availability;
2081 $this->availability_code = $obj->availability_code;
2082 $this->availability = $obj->availability_label;
2083 $this->demand_reason_id = $obj->fk_input_reason;
2084 $this->demand_reason_code = $obj->demand_reason_code;
2085 $this->delivery_date = $this->db->jdate($obj->delivery_date);
2086 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
2087 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
2088 $this->fk_delivery_address = $obj->fk_delivery_address;
2089 $this->module_source = $obj->module_source;
2090 $this->pos_source = $obj->pos_source;
2091
2092 //Incoterms
2093 $this->fk_incoterms = $obj->fk_incoterms;
2094 $this->location_incoterms = $obj->location_incoterms;
2095 $this->label_incoterms = $obj->label_incoterms;
2096
2097 // Multicurrency
2098 $this->fk_multicurrency = $obj->fk_multicurrency;
2099 $this->multicurrency_code = $obj->multicurrency_code;
2100 $this->multicurrency_tx = $obj->multicurrency_tx;
2101 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
2102 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
2103 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2104
2105 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2106
2107 $this->lines = array();
2108
2109 // Retrieve all extrafield
2110 // fetch optionals attributes and labels
2111 $this->fetch_optionals();
2112
2113 $this->db->free($result);
2114
2115 // Lines
2116 $result = $this->fetch_lines();
2117 if ($result < 0) {
2118 return -3;
2119 }
2120 return 1;
2121 } else {
2122 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2123 return 0;
2124 }
2125 } else {
2126 $this->error = $this->db->error();
2127 return -1;
2128 }
2129 }
2130
2131
2132 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2139 public function insert_discount($idremise)
2140 {
2141 // phpcs:enable
2142 global $langs;
2143
2144 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2145 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2146
2147 $this->db->begin();
2148
2149 $remise = new DiscountAbsolute($this->db);
2150 $result = $remise->fetch($idremise);
2151
2152 if ($result > 0) {
2153 if ($remise->fk_facture) { // Protection against multiple submission
2154 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2155 $this->db->rollback();
2156 return -5;
2157 }
2158
2159 $line = new OrderLine($this->db);
2160
2161 $line->fk_commande = $this->id;
2162 $line->fk_remise_except = $remise->id;
2163 $line->desc = $remise->description; // Description ligne
2164 $line->vat_src_code = $remise->vat_src_code;
2165 $line->tva_tx = $remise->tva_tx;
2166 $line->subprice = -(float) $remise->amount_ht;
2167 $line->price = -(float) $remise->amount_ht;
2168 $line->fk_product = 0; // Id produit predefini
2169 $line->qty = 1;
2170 $line->remise_percent = 0;
2171 $line->rang = -1;
2172 $line->info_bits = 2;
2173
2174 $line->total_ht = -(float) $remise->amount_ht;
2175 $line->total_tva = -(float) $remise->amount_tva;
2176 $line->total_ttc = -(float) $remise->amount_ttc;
2177
2178 $result = $line->insert();
2179 if ($result > 0) {
2180 $result = $this->update_price(1);
2181 if ($result > 0) {
2182 $this->db->commit();
2183 return 1;
2184 } else {
2185 $this->db->rollback();
2186 return -1;
2187 }
2188 } else {
2189 $this->setErrorsFromObject($line);
2190 $this->db->rollback();
2191 return -2;
2192 }
2193 } else {
2194 $this->db->rollback();
2195 return -2;
2196 }
2197 }
2198
2199
2200 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2208 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2209 {
2210 // phpcs:enable
2211 $this->lines = array();
2212
2213 $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,';
2214 $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,';
2215 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2216 $sql .= ' l.fk_unit, l.extraparams,';
2217 $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,';
2218 $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,';
2219 $sql .= ' p.customcode, p.fk_country as country_id, c.code as country_code,';
2220 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units, p.packaging';
2221 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as l';
2222 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2223 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON c.rowid = p.fk_country';
2224 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2225 if ($only_product) {
2226 $sql .= ' AND p.fk_product_type = 0';
2227 }
2228 $sql .= ' ORDER BY l.rang, l.rowid';
2229
2230 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2231 $result = $this->db->query($sql);
2232 if ($result) {
2233 $num = $this->db->num_rows($result);
2234
2235 $i = 0;
2236 while ($i < $num) {
2237 $objp = $this->db->fetch_object($result);
2238
2239 $line = new OrderLine($this->db);
2240
2241 $line->rowid = $objp->rowid;
2242 $line->id = $objp->rowid;
2243 $line->fk_commande = $objp->fk_commande;
2244 $line->commande_id = $objp->fk_commande;
2245 $line->label = $objp->custom_label;
2246 $line->desc = $objp->description;
2247 $line->description = $objp->description; // Description line
2248 $line->product_type = $objp->product_type;
2249 $line->qty = $objp->qty;
2250 $line->ref_ext = $objp->ref_ext;
2251
2252 $line->vat_src_code = $objp->vat_src_code;
2253 $line->tva_tx = $objp->tva_tx;
2254 $line->localtax1_tx = $objp->localtax1_tx;
2255 $line->localtax2_tx = $objp->localtax2_tx;
2256 $line->localtax1_type = $objp->localtax1_type;
2257 $line->localtax2_type = $objp->localtax2_type;
2258 $line->total_ht = $objp->total_ht;
2259 $line->total_ttc = $objp->total_ttc;
2260 $line->total_tva = $objp->total_tva;
2261 $line->total_localtax1 = $objp->total_localtax1;
2262 $line->total_localtax2 = $objp->total_localtax2;
2263 $line->subprice = (float) $objp->subprice;
2264 $line->subprice_ttc = (float) $objp->subprice_ttc;
2265 $line->fk_remise_except = $objp->fk_remise_except;
2266 $line->remise_percent = $objp->remise_percent;
2267 $line->price = $objp->price;
2268 $line->fk_product = $objp->fk_product;
2269 $line->fk_fournprice = $objp->fk_fournprice;
2270 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2271 $line->pa_ht = $marginInfos[0];
2272 $line->marge_tx = $marginInfos[1];
2273 $line->marque_tx = $marginInfos[2];
2274 $line->rang = $objp->rang;
2275 $line->info_bits = $objp->info_bits;
2276 $line->special_code = $objp->special_code;
2277 $line->fk_parent_line = $objp->fk_parent_line;
2278
2279 $line->ref = $objp->product_ref;
2280 $line->libelle = $objp->product_label;
2281
2282 $line->product_ref = $objp->product_ref;
2283 $line->product_label = $objp->product_label;
2284 $line->product_tosell = $objp->product_tosell;
2285 $line->product_tobuy = $objp->product_tobuy;
2286 $line->product_desc = $objp->product_desc;
2287 $line->product_tobatch = $objp->product_tobatch;
2288 $line->product_barcode = $objp->product_barcode;
2289 $line->product_custom_code = $objp->customcode;
2290 $line->product_custom_country_id = $objp->country_id;
2291 $line->product_custom_country_code = $objp->country_code;
2292
2293 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2294 $line->fk_unit = $objp->fk_unit;
2295
2296 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
2297
2298 $line->weight = $objp->weight;
2299 $line->weight_units = $objp->weight_units;
2300 $line->volume = $objp->volume;
2301 $line->volume_units = $objp->volume_units;
2302 $line->packaging = $objp->packaging;
2303
2304 $line->date_start = $this->db->jdate($objp->date_start);
2305 $line->date_end = $this->db->jdate($objp->date_end);
2306
2307 // Multicurrency
2308 $line->fk_multicurrency = $objp->fk_multicurrency;
2309 $line->multicurrency_code = $objp->multicurrency_code;
2310 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2311 $line->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
2312 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2313 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2314 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2315
2316 $line->fetch_optionals();
2317
2318 // multilangs
2319 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2320 $tmpproduct = new Product($this->db);
2321 $tmpproduct->fetch($objp->fk_product);
2322 $tmpproduct->getMultiLangs();
2323
2324 $line->multilangs = $tmpproduct->multilangs;
2325 }
2326
2327 $this->lines[$i] = $line;
2328
2329 $i++;
2330 }
2331
2332 $this->db->free($result);
2333
2334 return 1;
2335 } else {
2336 $this->error = $this->db->error();
2337 return -3;
2338 }
2339 }
2340
2341
2347 public function getNbOfProductsLines()
2348 {
2349 $nb = 0;
2350 foreach ($this->lines as $line) {
2351 if ($line->product_type == 0) {
2352 $nb++;
2353 }
2354 }
2355 return $nb;
2356 }
2357
2363 public function getNbOfServicesLines()
2364 {
2365 $nb = 0;
2366 foreach ($this->lines as $line) {
2367 if ($line->product_type == 1) {
2368 $nb++;
2369 }
2370 }
2371 return $nb;
2372 }
2373
2379 public function getNbOfShipments()
2380 {
2381 $nb = 0;
2382
2383 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2384 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2385 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2386 $sql .= ' WHERE';
2387 $sql .= ' ed.fk_elementdet = cd.rowid';
2388 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2389 //print $sql;
2390
2391 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2392 $resql = $this->db->query($sql);
2393 if ($resql) {
2394 $obj = $this->db->fetch_object($resql);
2395 if ($obj) {
2396 $nb = (int) $obj->nb;
2397 }
2398
2399 $this->db->free($resql);
2400 return $nb;
2401 } else {
2402 $this->error = $this->db->lasterror();
2403 return -1;
2404 }
2405 }
2406
2415 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2416 {
2417 $this->expeditions = array();
2418
2419 $sql = 'SELECT cd.rowid, cd.fk_product,';
2420 $sql .= ' sum(ed.qty) as qty';
2421 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2422 if ($filtre_statut >= 0) {
2423 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2424 }
2425 $sql .= ' '.MAIN_DB_PREFIX.$this->table_element_line.' as cd';
2426 $sql .= ' WHERE';
2427 if ($filtre_statut >= 0) {
2428 $sql .= ' ed.fk_expedition = e.rowid AND';
2429 }
2430 $sql .= ' ed.fk_elementdet = cd.rowid';
2431 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2432 if ($fk_product > 0) {
2433 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2434 }
2435 if ($filtre_statut >= 0) {
2436 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2437 }
2438 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2439 //print $sql;
2440
2441 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2442 $resql = $this->db->query($sql);
2443 if ($resql) {
2444 $num = $this->db->num_rows($resql);
2445 $i = 0;
2446 while ($i < $num) {
2447 $obj = $this->db->fetch_object($resql);
2448 $this->expeditions[$obj->rowid] = $obj->qty;
2449 $i++;
2450 }
2451 $this->db->free($resql);
2452 return $num;
2453 } else {
2454 $this->error = $this->db->lasterror();
2455 return -1;
2456 }
2457 }
2458
2464 public function countNbOfShipments()
2465 {
2466 $sql = 'SELECT count(*)';
2467 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2468 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2469 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2470 $sql .= " AND el.sourcetype = 'commande'";
2471 $sql .= " AND el.fk_target = e.rowid";
2472 $sql .= " AND el.targettype = 'shipping'";
2473
2474 $resql = $this->db->query($sql);
2475 if ($resql) {
2476 $row = $this->db->fetch_row($resql);
2477 return $row[0];
2478 } else {
2479 dol_print_error($this->db);
2480 }
2481
2482 return 0;
2483 }
2484
2493 public function deleteLine($user = null, $lineid = 0, $id = 0)
2494 {
2495 if ($this->status == self::STATUS_DRAFT) {
2496 $this->db->begin();
2497
2498 // Delete line
2499 $line = new OrderLine($this->db);
2500
2501 $line->context = $this->context;
2502
2503 // Load data
2504 $line->fetch($lineid);
2505
2506 if ($id > 0 && $line->fk_commande != $id) {
2507 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2508 return -1;
2509 }
2510
2511 // Memorize previous line for triggers
2512 $staticline = clone $line;
2513 $line->oldline = $staticline;
2514
2515 if ($line->delete($user) > 0) {
2516 $result = $this->update_price(1);
2517
2518 if ($result > 0) {
2519 $this->db->commit();
2520 return 1;
2521 } else {
2522 $this->db->rollback();
2523 $this->error = $this->db->lasterror();
2524 return -1;
2525 }
2526 } else {
2527 $this->db->rollback();
2528 $this->setErrorsFromObject($line);
2529 return -1;
2530 }
2531 } else {
2532 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2533 return -1;
2534 }
2535 }
2536
2537 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2548 public function set_remise($user, $remise, $notrigger = 0)
2549 {
2550 // phpcs:enable
2551 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2552 // @phan-suppress-next-line PhanDeprecatedFunction
2553 return $this->setDiscount($user, $remise, $notrigger);
2554 }
2555
2564 public function setDiscount($user, $remise, $notrigger = 0)
2565 {
2566 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2567
2568 if ($user->hasRight('commande', 'creer')) {
2569 $error = 0;
2570
2571 $this->db->begin();
2572
2573 $remise = price2num($remise, 2);
2574
2575 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2576 $sql .= ' SET remise_percent = '.((float) $remise);
2577 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2578
2579 dol_syslog(__METHOD__, LOG_DEBUG);
2580 $resql = $this->db->query($sql);
2581 if (!$resql) {
2582 $this->errors[] = $this->db->error();
2583 $error++;
2584 }
2585
2586 if (!$error) {
2587 $this->oldcopy = clone $this;
2588 $this->remise_percent = $remise;
2589 $this->update_price(1);
2590 }
2591
2592 if (!$notrigger && empty($error)) {
2593 // Call trigger
2594 $result = $this->call_trigger('ORDER_MODIFY', $user);
2595 if ($result < 0) {
2596 $error++;
2597 }
2598 // End call triggers
2599 }
2600
2601 if (!$error) {
2602 $this->db->commit();
2603 return 1;
2604 } else {
2605 foreach ($this->errors as $errmsg) {
2606 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2607 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2608 }
2609 $this->db->rollback();
2610 return -1 * $error;
2611 }
2612 }
2613
2614 return 0;
2615 }
2616
2617
2618 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2627 public function set_date($user, $date, $notrigger = 0)
2628 {
2629 // phpcs:enable
2630 if ($user->hasRight('commande', 'creer')) {
2631 $error = 0;
2632
2633 $this->db->begin();
2634
2635 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2636 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2637 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2638
2639 dol_syslog(__METHOD__, LOG_DEBUG);
2640 $resql = $this->db->query($sql);
2641 if (!$resql) {
2642 $this->errors[] = $this->db->error();
2643 $error++;
2644 }
2645
2646 if (!$error) {
2647 $this->oldcopy = clone $this;
2648 $this->date = $date;
2649 }
2650
2651 if (!$notrigger && empty($error)) {
2652 // Call trigger
2653 $result = $this->call_trigger('ORDER_MODIFY', $user);
2654 if ($result < 0) {
2655 $error++;
2656 }
2657 // End call triggers
2658 }
2659
2660 if (!$error) {
2661 $this->db->commit();
2662 return 1;
2663 } else {
2664 foreach ($this->errors as $errmsg) {
2665 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2666 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2667 }
2668 $this->db->rollback();
2669 return -1 * $error;
2670 }
2671 } else {
2672 return -2;
2673 }
2674 }
2675
2676 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2686 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2687 {
2688 // phpcs:enable
2689 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2690 }
2691
2700 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2701 {
2702 if ($user->hasRight('commande', 'creer')) {
2703 $error = 0;
2704
2705 $this->db->begin();
2706
2707 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2708 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2709 $sql .= " WHERE rowid = ".((int) $this->id);
2710
2711 dol_syslog(__METHOD__, LOG_DEBUG);
2712 $resql = $this->db->query($sql);
2713 if (!$resql) {
2714 $this->errors[] = $this->db->error();
2715 $error++;
2716 }
2717
2718 if (!$error) {
2719 $this->oldcopy = clone $this;
2720 $this->delivery_date = $delivery_date;
2721 }
2722
2723 if (!$notrigger && empty($error)) {
2724 // Call trigger
2725 $result = $this->call_trigger('ORDER_MODIFY', $user);
2726 if ($result < 0) {
2727 $error++;
2728 }
2729 // End call triggers
2730 }
2731
2732 if (!$error) {
2733 $this->db->commit();
2734 return 1;
2735 } else {
2736 foreach ($this->errors as $errmsg) {
2737 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2738 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2739 }
2740 $this->db->rollback();
2741 return -1 * $error;
2742 }
2743 } else {
2744 return -2;
2745 }
2746 }
2747
2748 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2762 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2763 {
2764 // phpcs:enable
2765 global $user;
2766
2767 $ga = array();
2768
2769 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2770 $sql .= " c.rowid as cid, c.ref";
2771 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2772 $sql .= ", sc.fk_soc, sc.fk_user";
2773 }
2774 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX.$this->table_element." as c";
2775 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2776 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2777 }
2778 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2779 $sql .= " AND c.fk_soc = s.rowid";
2780 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2781 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2782 }
2783 if ($socid) {
2784 $sql .= " AND s.rowid = ".((int) $socid);
2785 }
2786 if ($draft) {
2787 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2788 }
2789 if (is_object($excluser)) {
2790 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2791 }
2792 $sql .= $this->db->order($sortfield, $sortorder);
2793 $sql .= $this->db->plimit($limit, $offset);
2794
2795 $result = $this->db->query($sql);
2796 if ($result) {
2797 $numc = $this->db->num_rows($result);
2798 if ($numc) {
2799 $i = 0;
2800 while ($i < $numc) {
2801 $obj = $this->db->fetch_object($result);
2802
2803 if ($shortlist == 1) {
2804 $ga[$obj->cid] = $obj->ref;
2805 } elseif ($shortlist == 2) {
2806 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2807 } else {
2808 $ga[$i]['id'] = $obj->cid;
2809 $ga[$i]['ref'] = $obj->ref;
2810 $ga[$i]['name'] = $obj->name;
2811 }
2812 $i++;
2813 }
2814 }
2815 return $ga;
2816 } else {
2817 dol_print_error($this->db);
2818 return -1;
2819 }
2820 }
2821
2829 public function availability($availability_id, $notrigger = 0)
2830 {
2831 global $user;
2832
2833 dol_syslog('Commande::availability('.$availability_id.')');
2834 if ($this->status >= self::STATUS_DRAFT) {
2835 $error = 0;
2836
2837 $this->db->begin();
2838
2839 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2840 $sql .= ' SET fk_availability = '.((int) $availability_id);
2841 $sql .= ' WHERE rowid='.((int) $this->id);
2842
2843 dol_syslog(__METHOD__, LOG_DEBUG);
2844 $resql = $this->db->query($sql);
2845 if (!$resql) {
2846 $this->errors[] = $this->db->error();
2847 $error++;
2848 }
2849
2850 if (!$error) {
2851 $this->oldcopy = clone $this;
2852 $this->availability_id = $availability_id;
2853 }
2854
2855 if (!$notrigger && empty($error)) {
2856 // Call trigger
2857 $result = $this->call_trigger('ORDER_MODIFY', $user);
2858 if ($result < 0) {
2859 $error++;
2860 }
2861 // End call triggers
2862 }
2863
2864 if (!$error) {
2865 $this->db->commit();
2866 return 1;
2867 } else {
2868 foreach ($this->errors as $errmsg) {
2869 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2870 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2871 }
2872 $this->db->rollback();
2873 return -1 * $error;
2874 }
2875 } else {
2876 $error_str = 'Command status do not meet requirement '.$this->status;
2877 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2878 $this->error = $error_str;
2879 $this->errors[] = $this->error;
2880 return -2;
2881 }
2882 }
2883
2884 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2892 public function demand_reason($demand_reason_id, $notrigger = 0)
2893 {
2894 // phpcs:enable
2895 global $user;
2896
2897 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2898 if ($this->status >= self::STATUS_DRAFT) {
2899 $error = 0;
2900
2901 $this->db->begin();
2902
2903 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2904 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2905 $sql .= ' WHERE rowid='.((int) $this->id);
2906
2907 dol_syslog(__METHOD__, LOG_DEBUG);
2908 $resql = $this->db->query($sql);
2909 if (!$resql) {
2910 $this->errors[] = $this->db->error();
2911 $error++;
2912 }
2913
2914 if (!$error) {
2915 $this->oldcopy = clone $this;
2916 $this->demand_reason_id = $demand_reason_id;
2917 }
2918
2919 if (!$notrigger && empty($error)) {
2920 // Call trigger
2921 $result = $this->call_trigger('ORDER_MODIFY', $user);
2922 if ($result < 0) {
2923 $error++;
2924 }
2925 // End call triggers
2926 }
2927
2928 if (!$error) {
2929 $this->db->commit();
2930 return 1;
2931 } else {
2932 foreach ($this->errors as $errmsg) {
2933 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2934 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2935 }
2936 $this->db->rollback();
2937 return -1 * $error;
2938 }
2939 } else {
2940 $error_str = 'order status do not meet requirement '.$this->status;
2941 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2942 $this->error = $error_str;
2943 $this->errors[] = $this->error;
2944 return -2;
2945 }
2946 }
2947
2948 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2957 public function set_ref_client($user, $ref_client, $notrigger = 0)
2958 {
2959 // phpcs:enable
2960 if ($user->hasRight('commande', 'creer')) {
2961 $error = 0;
2962
2963 $this->db->begin();
2964
2965 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2966 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2967 $sql .= ' WHERE rowid = '.((int) $this->id);
2968
2969 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2970 $resql = $this->db->query($sql);
2971 if (!$resql) {
2972 $this->errors[] = $this->db->error();
2973 $error++;
2974 }
2975
2976 if (!$error) {
2977 $this->oldcopy = clone $this;
2978 $this->ref_client = $ref_client;
2979 $this->ref_customer = $ref_client;
2980 }
2981
2982 if (!$notrigger && empty($error)) {
2983 // Call trigger
2984 $result = $this->call_trigger('ORDER_MODIFY', $user);
2985 if ($result < 0) {
2986 $error++;
2987 }
2988 // End call triggers
2989 }
2990 if (!$error) {
2991 $this->db->commit();
2992 return 1;
2993 } else {
2994 foreach ($this->errors as $errmsg) {
2995 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2996 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2997 }
2998 $this->db->rollback();
2999 return -1 * $error;
3000 }
3001 } else {
3002 return -1;
3003 }
3004 }
3005
3013 public function classifyBilled(User $user, $notrigger = 0)
3014 {
3015 $error = 0;
3016
3017 if ($this->billed) {
3018 return 0;
3019 }
3020
3021 $this->db->begin();
3022
3023 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 1';
3024 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3025
3026 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3027 if ($this->db->query($sql)) {
3028 $this->oldcopy = clone $this;
3029 $this->billed = 1;
3030
3031 if (!$notrigger && empty($error)) {
3032 // Call trigger
3033 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3034 if ($result < 0) {
3035 $error++;
3036 }
3037 // End call triggers
3038 }
3039
3040 if (!$error) {
3041 $this->db->commit();
3042 return 1;
3043 } else {
3044 foreach ($this->errors as $errmsg) {
3045 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3046 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3047 }
3048 $this->db->rollback();
3049 return -1 * $error;
3050 }
3051 } else {
3052 $this->error = $this->db->error();
3053 $this->db->rollback();
3054 return -1;
3055 }
3056 }
3057
3065 public function classifyUnBilled(User $user, $notrigger = 0)
3066 {
3067 $error = 0;
3068
3069 $this->db->begin();
3070
3071 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET facture = 0';
3072 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3073
3074 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3075 if ($this->db->query($sql)) {
3076 $this->oldcopy = clone $this;
3077 $this->billed = 1;
3078
3079 if (!$notrigger && empty($error)) {
3080 // Call trigger
3081 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3082 if ($result < 0) {
3083 $error++;
3084 }
3085 // End call triggers
3086 }
3087
3088 if (!$error) {
3089 $this->billed = 0;
3090
3091 $this->db->commit();
3092 return 1;
3093 } else {
3094 foreach ($this->errors as $errmsg) {
3095 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3096 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3097 }
3098 $this->db->rollback();
3099 return -1 * $error;
3100 }
3101 } else {
3102 $this->error = $this->db->error();
3103 $this->db->rollback();
3104 return -1;
3105 }
3106 }
3107
3108
3139 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)
3140 {
3141 global $mysoc, $langs, $user;
3142
3143 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");
3144 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3145
3146 if ($this->status == Commande::STATUS_DRAFT) {
3147 // Clean parameters
3148 if (empty($qty)) {
3149 $qty = 0;
3150 }
3151 if (empty($info_bits)) {
3152 $info_bits = 0;
3153 }
3154 if (empty($txtva)) {
3155 $txtva = 0;
3156 }
3157 if (empty($txlocaltax1)) {
3158 $txlocaltax1 = 0;
3159 }
3160 if (empty($txlocaltax2)) {
3161 $txlocaltax2 = 0;
3162 }
3163 if (empty($remise_percent)) {
3164 $remise_percent = 0;
3165 }
3166 if (empty($qty) && empty($special_code)) {
3167 $special_code = 3; // Set option tag
3168 }
3169 if (!empty($qty) && $special_code == 3) {
3170 $special_code = 0; // Remove option tag
3171 }
3172 if (empty($ref_ext)) {
3173 $ref_ext = '';
3174 }
3175
3176 if ($date_start && $date_end && $date_start > $date_end) {
3177 $langs->load("errors");
3178 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3179 return -1;
3180 }
3181
3182 $remise_percent = (float) price2num($remise_percent);
3183 $qty = (float) price2num($qty);
3184 $pu = price2num($pu);
3185 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
3186 $pu_ht_devise = price2num($pu_ht_devise);
3187 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3188 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3189 }
3190 $txlocaltax1 = (float) price2num($txlocaltax1);
3191 $txlocaltax2 = (float) price2num($txlocaltax2);
3192
3193 $this->db->begin();
3194
3195 // Calcul du total TTC et de la TVA pour la ligne a partir de
3196 // qty, pu, remise_percent et txtva
3197 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3198 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3199
3200 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3201
3202 // Clean vat code
3203 $vat_src_code = '';
3204 $reg = array();
3205 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3206 $vat_src_code = $reg[1];
3207 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3208 }
3209
3210 $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);
3211
3212 $total_ht = $tabprice[0];
3213 $total_tva = $tabprice[1];
3214 $total_ttc = $tabprice[2];
3215 $total_localtax1 = $tabprice[9];
3216 $total_localtax2 = $tabprice[10];
3217 $pu_ht = $tabprice[3];
3218 $pu_tva = $tabprice[4];
3219 $pu_ttc = $tabprice[5];
3220
3221 // MultiCurrency
3222 $multicurrency_total_ht = $tabprice[16];
3223 $multicurrency_total_tva = $tabprice[17];
3224 $multicurrency_total_ttc = $tabprice[18];
3225 $pu_ht_devise = $tabprice[19];
3226
3227 // Fetch current line from the database and then clone the object and set it in $oldline property
3228 $line = new OrderLine($this->db);
3229 $line->fetch($rowid);
3230 $line->fetch_optionals();
3231
3232 if (!empty($line->fk_product)) {
3233 $product = new Product($this->db);
3234 $result = $product->fetch($line->fk_product);
3235 $product_type = $product->type;
3236
3237 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product->isStockManaged()) {
3238 // get real stock
3239 $productChildrenNb = 0;
3240 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
3241 $productChildrenNb = $product->hasFatherOrChild(1);
3242 }
3243 if ($productChildrenNb > 0) {
3244 // compute real stock from each subcomponent
3245 $product_stock = null;
3246 $product->loadStockForVirtualProduct('warehouseopen', $qty);
3247 foreach ($product->stock_warehouse as $componentStockWarehouse) {
3248 if ($product_stock === null) {
3249 $product_stock = $componentStockWarehouse->real;
3250 } else {
3251 $product_stock = min($product_stock, $componentStockWarehouse->real);
3252 }
3253 }
3254 if ($product_stock === null) {
3255 $product_stock = 0;
3256 }
3257 } else {
3258 $product_stock = $product->stock_reel;
3259 }
3260
3261 if ($product_stock < $qty) {
3262 $langs->load("errors");
3263 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', (string) $product->ref);
3264 $this->errors[] = $this->error;
3265
3266 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3267
3268 $this->db->rollback();
3270 }
3271 }
3272 }
3273
3274 $staticline = clone $line;
3275
3276 $line->oldline = $staticline;
3277 $this->line = $line;
3278 $this->line->context = $this->context;
3279 $this->line->rang = $rang;
3280
3281 // Reorder if fk_parent_line change
3282 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3283 $rangmax = $this->line_max($fk_parent_line);
3284 $this->line->rang = $rangmax + 1;
3285 }
3286
3287 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
3288 if (abs((float) $qty) < $this->line->packaging) {
3289 $qty = $this->line->packaging;
3290 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'warnings');
3291 } else {
3292 if (!empty($this->line->packaging)
3293 && is_numeric($this->line->packaging)
3294 && (float) $this->line->packaging > 0
3295 && (float) price2num(fmod((float) $qty, (float) $this->line->packaging), 'MS')) {
3296 // Use abs() to keep the rounding consistent for negative qty,
3297 // matching what addline() at line 1725 already does (#38782 bug 5).
3298 $coeff = intval(abs((float) $qty) / $this->line->packaging) + 1;
3299 $qty = price2num((float) $this->line->packaging * $coeff, 'MS');
3300 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'warnings');
3301 }
3302 }
3303 }
3304
3305 $this->line->id = $rowid;
3306 $this->line->label = $label;
3307 $this->line->desc = $desc;
3308 $this->line->qty = $qty;
3309 $this->line->ref_ext = $ref_ext;
3310
3311 $this->line->vat_src_code = $vat_src_code;
3312 $this->line->tva_tx = $txtva;
3313 $this->line->localtax1_tx = $txlocaltax1;
3314 $this->line->localtax2_tx = $txlocaltax2;
3315 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3316 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3317 $this->line->remise_percent = $remise_percent;
3318 $this->line->subprice = (float) $pu_ht;
3319 $this->line->info_bits = $info_bits;
3320 $this->line->special_code = $special_code;
3321 $this->line->total_ht = (float) $total_ht;
3322 $this->line->total_tva = (float) $total_tva;
3323 $this->line->total_localtax1 = (float) $total_localtax1;
3324 $this->line->total_localtax2 = (float) $total_localtax2;
3325 $this->line->total_ttc = (float) $total_ttc;
3326 $this->line->date_start = $date_start;
3327 $this->line->date_end = $date_end;
3328 $this->line->product_type = $type;
3329 $this->line->fk_parent_line = $fk_parent_line;
3330 $this->line->skip_update_total = $skip_update_total;
3331 $this->line->fk_unit = $fk_unit;
3332
3333 $this->line->fk_fournprice = $fk_fournprice;
3334 $this->line->pa_ht = $pa_ht;
3335
3336 // Multicurrency
3337 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3338 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3339 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3340 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3341
3342 if (is_array($array_options) && count($array_options) > 0) {
3343 // We replace values in this->line->array_options only for entries defined into $array_options
3344 foreach ($array_options as $key => $value) {
3345 $this->line->array_options[$key] = $array_options[$key];
3346 }
3347 }
3348
3349 $result = $this->line->update($user, $notrigger);
3350 if ($result > 0) {
3351 // Reorder if child line
3352 if (!empty($fk_parent_line)) {
3353 $this->line_order(true, 'DESC');
3354 }
3355
3356 // Mise a jour info denormalisees
3357 $this->update_price(1, 'auto');
3358
3359 $this->db->commit();
3360 return $result;
3361 } else {
3362 $this->setErrorsFromObject($this->line);
3363
3364 $this->db->rollback();
3365 return -1;
3366 }
3367 } else {
3368 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3369 $this->errors = array('OrderStatusMakeOperationForbidden');
3370 return -2;
3371 }
3372 }
3373
3381 public function update(User $user, $notrigger = 0)
3382 {
3383 $error = 0;
3384
3385 // Clean parameters
3386 if (isset($this->ref)) {
3387 $this->ref = trim($this->ref);
3388 }
3389 if (isset($this->ref_client)) {
3390 $this->ref_client = trim($this->ref_client);
3391 }
3392 if (isset($this->ref_customer)) {
3393 $this->ref_customer = trim($this->ref_customer);
3394 }
3395 if (isset($this->note) || isset($this->note_private)) {
3396 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3397 }
3398 if (isset($this->note_public)) {
3399 $this->note_public = trim($this->note_public);
3400 }
3401 if (isset($this->model_pdf)) {
3402 $this->model_pdf = trim($this->model_pdf);
3403 }
3404 if (isset($this->import_key)) {
3405 $this->import_key = trim($this->import_key);
3406 }
3407 $delivery_date = $this->delivery_date;
3408
3409 // Check parameters
3410 // Put here code to add control on parameters values
3411
3412 // Update request
3413 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3414
3415 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3416 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3417 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3418 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3419 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3420 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3421 $sql .= " total_tva=".(isset($this->total_tva) ? (float) $this->total_tva : "null").",";
3422 $sql .= " localtax1=".(isset($this->total_localtax1) ? (float) $this->total_localtax1 : "null").",";
3423 $sql .= " localtax2=".(isset($this->total_localtax2) ? (float) $this->total_localtax2 : "null").",";
3424 $sql .= " total_ht=".(isset($this->total_ht) ? (float) $this->total_ht : "null").",";
3425 $sql .= " total_ttc=".(isset($this->total_ttc) ? (float) $this->total_ttc : "null").",";
3426 $sql .= " fk_statut=".(isset($this->status) ? (int) $this->status : "null").",";
3427 $sql .= " fk_user_modif=".(isset($user->id) ? (int) $user->id : "null").",";
3428 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? (int) $this->user_validation_id : "null").",";
3429 $sql .= " fk_projet=".(isset($this->fk_project) ? (int) $this->fk_project : "null").",";
3430 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? (int) $this->cond_reglement_id : "null").",";
3431 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
3432 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3433 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3434 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? (int) $this->shipping_method_id : "null").",";
3435 $sql .= " fk_account=".($this->fk_account > 0 ? (int) $this->fk_account : "null").",";
3436 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? (int) $this->demand_reason_id : "null").",";
3437 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3438 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3439 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3440 $sql .= " fk_warehouse=".($this->warehouse_id > 0 ? (int) $this->warehouse_id : "null").",";
3441 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").",";
3442 $sql .= " module_source = ".(isset($this->module_source) ? "'".$this->db->escape($this->module_source)."'" : "null").",";
3443 $sql .= " pos_source = ".(isset($this->pos_source) ? "'".$this->db->escape($this->pos_source)."'" : "null");
3444 $sql .= " WHERE rowid=".((int) $this->id);
3445
3446 $this->db->begin();
3447
3448 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3449 $resql = $this->db->query($sql);
3450 if (!$resql) {
3451 $error++;
3452 $this->errors[] = "Error ".$this->db->lasterror();
3453 }
3454
3455 if (!$error) {
3456 $result = $this->insertExtraFields();
3457 if ($result < 0) {
3458 $error++;
3459 }
3460 }
3461
3462 if (!$error && !$notrigger) {
3463 // Call trigger
3464 $result = $this->call_trigger('ORDER_MODIFY', $user);
3465 if ($result < 0) {
3466 $error++;
3467 }
3468 // End call triggers
3469 }
3470
3471 // Commit or rollback
3472 if ($error) {
3473 foreach ($this->errors as $errmsg) {
3474 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3475 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3476 }
3477 $this->db->rollback();
3478 return -1 * $error;
3479 } else {
3480 $this->db->commit();
3481 return 1;
3482 }
3483 }
3484
3492 public function delete($user, $notrigger = 0)
3493 {
3494 global $conf, $langs;
3495 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3496
3497 $error = 0;
3498
3499 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3500
3501 $this->db->begin();
3502
3503 if (!$notrigger) {
3504 // Call trigger
3505 $result = $this->call_trigger('ORDER_DELETE', $user);
3506 if ($result < 0) {
3507 $error++;
3508 }
3509 // End call triggers
3510 }
3511
3512 // Test we can delete
3513 if ($this->countNbOfShipments() != 0) {
3514 $this->errors[] = $langs->trans('SomeShipmentExists');
3515 $error++;
3516 }
3517
3518 // Remove linked categories.
3519 if (!$error) {
3520 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_order";
3521 $sql .= " WHERE fk_order = ".((int) $this->id);
3522
3523 $result = $this->db->query($sql);
3524 if (!$result) {
3525 $error++;
3526 $this->errors[] = $this->db->lasterror();
3527 }
3528 }
3529
3530 // Delete extrafields of lines and lines
3531 if (!$error && !empty($this->table_element_line)) {
3532 $tabletodelete = $this->table_element_line;
3533 $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).")";
3534 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3535 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3536 $error++;
3537 $this->error = $this->db->lasterror();
3538 $this->errors[] = $this->error;
3539 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3540 }
3541 }
3542
3543 if (!$error) {
3544 // Delete linked object
3545 $res = $this->deleteObjectLinked();
3546 if ($res < 0) {
3547 $error++;
3548 }
3549 }
3550
3551 if (!$error) {
3552 // Delete linked contacts
3553 $res = $this->delete_linked_contact();
3554 if ($res < 0) {
3555 $error++;
3556 }
3557 }
3558
3559 // Removed extrafields of object
3560 if (!$error) {
3561 $result = $this->deleteExtraFields();
3562 if ($result < 0) {
3563 $error++;
3564 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3565 }
3566 }
3567
3568 // Delete main record
3569 if (!$error) {
3570 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3571 $res = $this->db->query($sql);
3572 if (!$res) {
3573 $error++;
3574 $this->error = $this->db->lasterror();
3575 $this->errors[] = $this->error;
3576 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3577 }
3578 }
3579
3580 // Delete record into ECM index and physically
3581 if (!$error) {
3582 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive, old method.
3583 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3584 if (!$res) {
3585 $error++;
3586 }
3587 }
3588
3589 if (!$error) {
3590 // We remove directory
3591 $ref = dol_sanitizeFileName($this->ref);
3592 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3593 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3594 $file = $dir."/".$ref.".pdf";
3595 if (file_exists($file)) {
3596 dol_delete_preview($this);
3597
3598 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3599 $this->error = 'ErrorFailToDeleteFile';
3600 $this->errors[] = $this->error;
3601 $this->db->rollback();
3602 return 0;
3603 }
3604 }
3605 if (file_exists($dir)) {
3606 $res = @dol_delete_dir_recursive($dir);
3607 if (!$res) {
3608 $this->error = 'ErrorFailToDeleteDir';
3609 $this->errors[] = $this->error;
3610 $this->db->rollback();
3611 return 0;
3612 }
3613 }
3614 }
3615 }
3616
3617 if (!$error) {
3618 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3619 $this->db->commit();
3620 return 1;
3621 } else {
3622 $this->db->rollback();
3623 return -1;
3624 }
3625 }
3626
3627
3628 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3636 public function load_board($user, $mode)
3637 {
3638 // phpcs:enable
3639 global $conf, $langs, $hookmanager;
3640
3641 $clause = " WHERE";
3642
3643 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3644 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
3645 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3646 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3647 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3648 $clause = " AND";
3649 }
3650 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3651 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3652 if ($mode == 'toship') {
3653 // An order to ship is an open order (validated or in progress)
3654 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3655 }
3656 if ($mode == 'tobill') {
3657 // An order to bill is an order not already billed
3658 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3659 }
3660 if ($mode == 'shippedtobill') {
3661 // An order shipped and to bill is a delivered order not already billed
3662 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3663 }
3664 if ($user->socid) {
3665 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3666 }
3667 // Add where from hooks
3668 $parameters = array('socid' => $user->socid);
3669 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $this); // Note that $action and $object may have been modified by hook
3670 $sql .= $hookmanager->resPrint;
3671 $resql = $this->db->query($sql);
3672 if ($resql) {
3673 $delay_warning = 0;
3674 $label = $labelShort = $url = '';
3675 if ($mode == 'toship') {
3676 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3677 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3678 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3679 $labelShort = $langs->transnoentitiesnoconv("Opened");
3680 }
3681 if ($mode == 'tobill') {
3682 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3683 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3684 $labelShort = $langs->trans("ToBill");
3685 }
3686 if ($mode == 'shippedtobill') {
3687 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3688 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3689 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3690 }
3691
3692 $response = new WorkboardResponse();
3693
3694 $response->warning_delay = $delay_warning;
3695 $response->label = $label;
3696 $response->labelShort = $labelShort;
3697 $response->url = $url;
3698 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3699 $response->img = img_object('', "order");
3700
3701 $generic_commande = new Commande($this->db);
3702
3703 while ($obj = $this->db->fetch_object($resql)) {
3704 $response->nbtodo++;
3705 $response->total += $obj->total_ht;
3706
3707 $generic_commande->status = $obj->fk_statut;
3708 $generic_commande->date = $this->db->jdate($obj->date_commande);
3709 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3710
3711 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3712 $response->nbtodolate++;
3713 }
3714 }
3715
3716 return $response;
3717 } else {
3718 $this->error = $this->db->error();
3719 return -1;
3720 }
3721 }
3722
3728 public function getLabelSource()
3729 {
3730 global $langs;
3731
3732 $label = $langs->trans('OrderSource'.$this->source);
3733
3734 if ($label == 'OrderSource') {
3735 return '';
3736 }
3737 return $label;
3738 }
3739
3746 public function getLibStatut($mode)
3747 {
3748 return $this->LibStatut($this->status, $this->billed, $mode);
3749 }
3750
3751 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3761 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3762 {
3763 // phpcs:enable
3764 global $langs, $hookmanager;
3765
3766 $billedtext = '';
3767 if (empty($donotshowbilled)) {
3768 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3769 }
3770 $billedtextlong = ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3771
3772 $labelTooltip = '';
3773
3774 $paramsBadge = array('badgeParams' => array('attr' => array(
3775 'data-status-element' => $this->element,
3776 'data-billed' => (int) $billed,
3777 'data-status' => (int) $status
3778 )));
3779
3780 if ($status == self::STATUS_CANCELED) {
3781 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3782 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3783 $statusType = 'status9';
3784 } elseif ($status == self::STATUS_DRAFT) {
3785 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3786 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3787 $statusType = 'status0';
3788 } elseif ($status == self::STATUS_VALIDATED) {
3789 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtextlong;
3790 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3791 $statusType = 'status1';
3792 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3793 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtextlong;
3794 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3795 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3796 if (!empty($this->delivery_date)) {
3797 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3798 }
3799 $statusType = 'status4';
3800 } elseif ($status == self::STATUS_CLOSED) {
3801 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtextlong;
3802 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3803 $statusType = 'status6';
3804 } else {
3805 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3806 $labelStatusShort = '';
3807 $statusType = '';
3808 $mode = 0;
3809 }
3810
3811 $paramsBadge['tooltip'] = $labelTooltip;
3812
3813 $parameters = array(
3814 'status' => $status,
3815 'mode' => $mode,
3816 'billed' => $billed,
3817 'donotshowbilled' => $donotshowbilled,
3818 'paramsBadge' => & $paramsBadge
3819 );
3820
3821 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3822
3823 if ($reshook > 0) {
3824 return $hookmanager->resPrint;
3825 }
3826
3827 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $paramsBadge);
3828 }
3829
3837 public function getTooltipContentArray($params)
3838 {
3839 global $conf, $langs, $user;
3840
3841 $langs->load('orders');
3842 $datas = [];
3843 $nofetch = !empty($params['nofetch']);
3844
3845 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3846 return ['optimize' => $langs->trans("Order")];
3847 }
3848
3849 if ($user->hasRight('commande', 'lire')) {
3850 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').'<u>'.$langs->trans("Order").'</u>';
3851 if (isset($this->status)) {
3852 $datas['status'] = ' '.$this->getLibStatut(5);
3853 }
3854 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3855 if (!$nofetch) {
3856 $langs->load('companies');
3857 if (empty($this->thirdparty)) {
3858 $this->fetch_thirdparty();
3859 }
3860 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3861 }
3862 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3863 if (!$nofetch) {
3864 $langs->load('project');
3865 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3866 $res = $this->fetchProject();
3867 if ($res > 0 && $this->project instanceof Project) {
3868 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, '1');
3869 }
3870 }
3871 }
3872 if (!empty($this->total_ht)) {
3873 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3874 }
3875 if (!empty($this->total_tva)) {
3876 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3877 }
3878 if (!empty($this->total_ttc)) {
3879 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3880 }
3881 if (!empty($this->date)) {
3882 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3883 }
3884 if (!empty($this->delivery_date)) {
3885 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3886 }
3887 }
3888
3889 return $datas;
3890 }
3891
3905 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3906 {
3907 global $conf, $langs, $user, $hookmanager;
3908
3909 if (!empty($conf->dol_no_mouse_hover)) {
3910 $notooltip = 1; // Force disable tooltips
3911 }
3912
3913 $result = '';
3914
3915 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3916 $baseurl = DOL_URL_ROOT . '/expedition/shipment.php';
3917 } else {
3918 $baseurl = DOL_URL_ROOT . '/commande/card.php';
3919 }
3920 $query = ['id' => $this->id];
3921 if (!$user->hasRight('commande', 'lire')) {
3922 $option = 'nolink';
3923 }
3924
3925 if ($option !== 'nolink') {
3926 // Add param to save lastsearch_values or not
3927 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3928 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3929 $add_save_lastsearch_values = 1;
3930 }
3931 if ($add_save_lastsearch_values) {
3932 $query = array_merge($query, ['save_lastsearch_values' => 1]);
3933 }
3934 }
3935 $url = dolBuildUrl($baseurl, $query);
3936
3937 if ($short) {
3938 return $url;
3939 }
3940 $params = [
3941 'id' => $this->id,
3942 'objecttype' => $this->element,
3943 'option' => $option,
3944 'nofetch' => 1,
3945 ];
3946 $classfortooltip = 'classfortooltip';
3947 $dataparams = '';
3948 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3949 $classfortooltip = 'classforajaxtooltip';
3950 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3951 $label = '';
3952 } else {
3953 $label = implode($this->getTooltipContentArray($params));
3954 }
3955
3956 $linkclose = '';
3957 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3958 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3959 $label = $langs->trans("Order");
3960 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
3961 }
3962 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
3963 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3964
3965 $target_value = array('_self', '_blank', '_parent', '_top');
3966 if (in_array($target, $target_value)) {
3967 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3968 }
3969 }
3970
3971 $linkstart = '<a href="'.$url.'"';
3972 $linkstart .= $linkclose.'>';
3973 $linkend = '</a>';
3974
3975 if ($option === 'nolink') {
3976 $linkstart = '';
3977 $linkend = '';
3978 }
3979
3980 $result .= $linkstart;
3981 if ($withpicto) {
3982 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3983 }
3984 if ($withpicto != 2) {
3985 $result .= $this->ref;
3986 }
3987 $result .= $linkend;
3988
3989 if ($addlinktonotes) {
3990 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3991 if ($txttoshow) {
3992 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3993 $result .= ' <span class="note inline-block">';
3994 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3995 $result .= img_picto('', 'note');
3996 $result .= '</a>';
3997 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3998 //$result.='</a>';
3999 $result .= '</span>';
4000 }
4001 }
4002
4003 global $action;
4004 $hookmanager->initHooks(array($this->element . 'dao'));
4005 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
4006 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4007 if ($reshook > 0) {
4008 $result = $hookmanager->resPrint;
4009 } else {
4010 $result .= $hookmanager->resPrint;
4011 }
4012 return $result;
4013 }
4014
4015
4022 public function info($id)
4023 {
4024 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
4025 $sql .= ' date_valid as datev,';
4026 $sql .= ' date_cloture as datecloture,';
4027 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
4028 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as c';
4029 $sql .= ' WHERE c.rowid = '.((int) $id);
4030 $result = $this->db->query($sql);
4031 if ($result) {
4032 if ($this->db->num_rows($result)) {
4033 $obj = $this->db->fetch_object($result);
4034 $this->id = $obj->rowid;
4035 if ($obj->fk_user_author) {
4036 $this->user_creation_id = $obj->fk_user_author;
4037 }
4038 if ($obj->fk_user_valid) {
4039 $this->user_validation_id = $obj->fk_user_valid;
4040 }
4041 if ($obj->fk_user_cloture) {
4042 $this->user_closing_id = $obj->fk_user_cloture;
4043 }
4044
4045 $this->date_creation = $this->db->jdate($obj->datec);
4046 $this->date_modification = $this->db->jdate($obj->datem);
4047 $this->date_validation = $this->db->jdate($obj->datev);
4048 $this->date_cloture = $this->db->jdate($obj->datecloture);
4049 }
4050
4051 $this->db->free($result);
4052 } else {
4053 dol_print_error($this->db);
4054 }
4055 }
4056
4057
4066 public function initAsSpecimen($param = array())
4067 {
4068 global $conf, $langs;
4069
4070 dol_syslog(get_class($this)."::initAsSpecimen");
4071
4072 // Load array of products prodids
4073 $num_prods = 0;
4074 $prodids = array();
4075 $sql = "SELECT rowid";
4076 $sql .= " FROM ".MAIN_DB_PREFIX."product";
4077 $sql .= " WHERE entity IN (".getEntity('product').")";
4078 if (array_key_exists('tosell', $param)) {
4079 $sql .= " AND tosell = ".((int) $param['tosell']);
4080 }
4081 $sql .= $this->db->plimit(100);
4082
4083 $resql = $this->db->query($sql);
4084 if ($resql) {
4085 $num_prods = $this->db->num_rows($resql);
4086 $i = 0;
4087 while ($i < $num_prods) {
4088 $i++;
4089 $row = $this->db->fetch_row($resql);
4090 $prodids[$i] = $row[0];
4091 }
4092 }
4093
4094 // Initialise parameters
4095 $this->id = 0;
4096 $this->ref = 'SPECIMEN';
4097 $this->specimen = 1;
4098 $this->entity = $conf->entity;
4099 $this->socid = 1;
4100 $this->date = time();
4101 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4102 $this->cond_reglement_code = 'RECEP';
4103 $this->mode_reglement_code = 'CHQ';
4104 $this->availability_code = 'DSP';
4105 $this->demand_reason_code = 'SRC_00';
4106
4107 $this->note_public = 'This is a comment (public)';
4108 $this->note_private = 'This is a comment (private)';
4109
4110 $this->multicurrency_tx = 1;
4111 $this->multicurrency_code = $conf->currency;
4112
4113 $this->status = $this::STATUS_DRAFT;
4114
4115 // Lines
4116 $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)
4117 $xnbp = 0;
4118
4119 while ($xnbp < $nbp) {
4120 $line = new OrderLine($this->db);
4121
4122 $line->desc = $langs->trans("Description")." ".$xnbp;
4123 $line->qty = 1;
4124 $line->subprice = 100;
4125 $line->price = 100;
4126 $line->tva_tx = 20;
4127 if ($xnbp == 2) {
4128 $line->total_ht = 50;
4129 $line->total_ttc = 60;
4130 $line->total_tva = 10;
4131 $line->remise_percent = 50;
4132 } else {
4133 $line->total_ht = 100;
4134 $line->total_ttc = 120;
4135 $line->total_tva = 20;
4136 $line->remise_percent = 0;
4137 }
4138 if ($num_prods > 0) {
4139 $prodid = mt_rand(1, $num_prods);
4140 $line->fk_product = $prodids[$prodid];
4141 $line->product_ref = 'SPECIMEN';
4142 }
4143
4144 $this->lines[$xnbp] = $line;
4145
4146 $this->total_ht += $line->total_ht;
4147 $this->total_tva += $line->total_tva;
4148 $this->total_ttc += $line->total_ttc;
4149
4150 $xnbp++;
4151 }
4152
4153 return 1;
4154 }
4155
4156
4162 public function loadStateBoard()
4163 {
4164 global $user, $hookmanager;
4165
4166 $this->nb = array();
4167 $clause = "WHERE";
4168
4169 $sql = "SELECT count(c.rowid) as nb";
4170 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
4171 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
4172 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
4173 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4174 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4175 $clause = "AND";
4176 }
4177 $sql .= " ".$clause." c.entity IN (".getEntity('commande').")";
4178 // Add where from hooks
4179 $parameters = array();
4180 $hookmanager->executeHooks('printFieldListWhere', $parameters, $this); // Note that $action and $object may have been modified by hook
4181 $sql .= $hookmanager->resPrint;
4182 $resql = $this->db->query($sql);
4183 if ($resql) {
4184 while ($obj = $this->db->fetch_object($resql)) {
4185 $this->nb["orders"] = $obj->nb;
4186 }
4187 $this->db->free($resql);
4188 return 1;
4189 } else {
4190 dol_print_error($this->db);
4191 $this->error = $this->db->error();
4192 return -1;
4193 }
4194 }
4195
4201 public function getLinesArray()
4202 {
4203 return $this->fetch_lines();
4204 }
4205
4217 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4218 {
4219 global $langs;
4220
4221 $langs->load("orders");
4222 $outputlangs->load("products");
4223
4224 if (!dol_strlen($modele)) {
4225 $modele = 'einstein';
4226
4227 if (!empty($this->model_pdf)) {
4228 $modele = $this->model_pdf;
4229 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4230 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4231 }
4232 }
4233
4234 $modelpath = "core/modules/commande/doc/";
4235
4236 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4237 }
4238
4239
4248 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4249 {
4250 $tables = array(
4251 'commande'
4252 );
4253
4254 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4255 }
4256
4265 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4266 {
4267 $tables = array(
4268 'commandedet',
4269 );
4270
4271 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4272 }
4273
4279 public function hasDelay()
4280 {
4281 global $conf;
4282
4283 if (!($this->status > Commande::STATUS_DRAFT && $this->status < Commande::STATUS_CLOSED)) {
4284 return false; // Never late if not inside this status range
4285 }
4286
4287 $now = dol_now();
4288
4289 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4290 }
4291
4297 public function showDelay()
4298 {
4299 global $conf, $langs;
4300
4301 if (empty($this->delivery_date)) {
4302 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4303 } else {
4304 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4305 }
4306 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4307
4308 return $text;
4309 }
4310
4320 public function setSignedStatus(User $user, int $status = 0, int $notrigger = 0, $triggercode = ''): int
4321 {
4322 return $this->setSignedStatusCommon($user, $status, $notrigger, $triggercode);
4323 }
4324}
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.
createFromClone(User $user, $socid=0)
Load an object from its id and create a new one in database.
const STATUS_SHIPMENTONPROCESS
Shipment on process.
LibStatut($status, $billed, $mode, $donotshowbilled=0)
Return label of status.
getLibStatut($mode)
Return status label of Order.
hasDelay()
Is the sales order delayed?
const STATUS_CLOSED
Closed (Sent, billed or not)
valid($user, $idwarehouse=0, $notrigger=0)
Validate order.
getLabelSource()
Return source label of order.
set_remise($user, $remise, $notrigger=0)
Applique une remise relative.
initAsSpecimen($param=array())
Initialise an instance with random values.
const STATUS_CANCELED
Canceled status.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0, $target='')
Return clickable link of object (with eventually picto)
loadExpeditions($filtre_statut=-1, $fk_product=0)
Load array this->expeditions of lines of shipments with nb of products sent for each order line Note:...
__construct($db)
Constructor.
availability($availability_id, $notrigger=0)
Update delivery delay.
set_reopen($user)
Tag the order as validated (opened) Function used when order is reopend after being closed.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer ref.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $info_bits=0, $fk_remise_except=0, $price_base_type='HT', $pu_ttc=0, $date_start='', $date_end='', $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=array(), $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0, $ref_ext='', $noupdateafterinsertline=0)
Add an order line into database (linked to product/service or not)
getTooltipContentArray($params)
getTooltipContentArray
create($user, $notrigger=0)
Create sale order Note that this->ref can be set or empty.
demand_reason($demand_reason_id, $notrigger=0)
Update order demand_reason.
const STATUS_DRAFT
Draft status.
const STOCK_NOT_ENOUGH_FOR_ORDER
ERR Not enough stock.
insert_discount($idremise)
Add a discount line into a sale order (as a sale order line) using an existing absolute discount (Con...
getNbOfProductsLines()
Return number of line with type product.
update(User $user, $notrigger=0)
Update database.
classifyUnBilled(User $user, $notrigger=0)
Classify the order as not invoiced.
setDiscount($user, $remise, $notrigger=0)
Set a percentage discount.
cloture($user, $notrigger=0)
Close order.
setCategories($categories)
Sets object to given categories.
const STATUS_ACCEPTED
For backward compatibility.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=array(), $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $ref_ext='', $rang=0)
Update a line in database.
classifyBilled(User $user, $notrigger=0)
Classify the order as invoiced.
info($id)
Charge les information d'ordre info dans l'objet commande.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
const STATUS_VALIDATED
Validated status.
deleteLine($user=null, $lineid=0, $id=0)
Delete an order line.
countNbOfShipments()
Returns an array with expeditions lines number.
liste_array($shortlist=0, $draft=0, $excluser=null, $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
Return list of orders (eventuelly filtered on a user) into an array.
fetch($id, $ref='', $ref_ext='', $notused='')
Get object from database.
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.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetchProject()
Load the project with id $this->fk_project into this->project.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
checkActiveProductInLines($status='onsale')
Check if all products have the right status (on sale, on buy) called during validation of propal,...
deleteExtraFields()
Delete all extra fields values for the current object.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
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 Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
setSignedStatusCommon(User $user, int $status, int $notrigger=0, string $triggercode='')
Set signed status & call trigger with context message.
global $mysoc
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.
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)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_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...
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...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller=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
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left 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 PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:464