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