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