dolibarr 20.0.0
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-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 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
39include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
40require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.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';
45
46
50class Commande extends CommonOrder
51{
55 public $element = 'commande';
56
60 public $table_element = 'commande';
61
65 public $table_element_line = 'commandedet';
66
70 public $class_element_line = 'OrderLine';
71
75 public $fk_element = 'fk_commande';
76
80 public $picto = 'order';
81
86 public $restrictiononfksoc = 1;
87
91 protected $table_ref_field = 'ref';
92
96 public $socid;
97
101 public $ref_client;
102
106 public $ref_customer;
107
111 public $contactid;
112
117 public $statut;
118
122 public $billed;
123
127 public $date_lim_reglement;
131 public $cond_reglement_code;
132
136 public $cond_reglement_doc;
137
143 public $deposit_percent;
144
148 public $fk_account;
149
153 public $mode_reglement;
154
158 public $mode_reglement_id;
159
163 public $mode_reglement_code;
164
169 public $availability_id;
170
175 public $availability_code;
176
181 public $availability;
182
186 public $demand_reason_id;
187
191 public $demand_reason_code;
192
196 public $date;
197
203 public $date_commande;
204
208 public $delivery_date;
209
213 public $fk_remise_except;
214
219
220 public $source; // Order mode. How we received order (by phone, by email, ...)
221
225 public $warehouse_id;
226
227 public $extraparams = array();
228
229 public $linked_objects = array();
230
234 public $user_author_id;
235
239 public $line;
240
244 public $lines = array();
245
246
251
255 public $expeditions;
256
260 public $online_payment_url;
261
262
263
288 // BEGIN MODULEBUILDER PROPERTIES
292 public $fields = array(
293 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
294 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
295 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 25),
296 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 26),
297 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 28),
298 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 20),
299 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 25),
300 'date_commande' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'csslist' => 'nowraponall'),
301 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 62, 'csslist' => 'nowraponall'),
302 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'csslist' => 'nowraponall'),
303 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 85),
304 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => -1, 'position' => 90),
305 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => -1, 'position' => 95),
306 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
307 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
308 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
309 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
310 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
311 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 150),
312 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 155),
313 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 160),
314 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 170),
315 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 175),
316 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 180),
317 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 181),
318 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 185),
319 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'csslist' => 'nowraponall'),
320 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 195),
321 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 200),
322 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 205),
323 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 210),
324 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
325 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 225),
326 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 230),
327 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 235),
328 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
329 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245),
330 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
331 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
332 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 260, 'isameasure' => 1),
333 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 265, 'isameasure' => 1),
334 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 270),
335 'module_source' => array('type' => 'varchar(32)', 'label' => 'POSModule', 'enabled' => 1, 'visible' => -1, 'position' => 275),
336 'pos_source' => array('type' => 'varchar(32)', 'label' => 'POSTerminal', 'enabled' => 1, 'visible' => -1, 'position' => 280),
337 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 300),
338 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 302),
339 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 304, 'csslist' => 'nowraponall'),
340 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 306),
341 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 400),
342 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'position' => 500),
343 );
344 // END MODULEBUILDER PROPERTIES
345
350
354 const STATUS_CANCELED = -1;
358 const STATUS_DRAFT = 0;
366 const STATUS_SHIPMENTONPROCESS = 2; // We set this status when a shipment is validated
367 const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
368
372 const STATUS_CLOSED = 3;
373
374
380 public function __construct($db)
381 {
382 $this->db = $db;
383
384 $this->ismultientitymanaged = 1;
385 $this->isextrafieldmanaged = 1;
386 }
387
395 public function getNextNumRef($soc)
396 {
397 global $langs, $conf;
398 $langs->load("order");
399
400 if (getDolGlobalString('COMMANDE_ADDON')) {
401 $mybool = false;
402
403 $file = getDolGlobalString('COMMANDE_ADDON') . ".php";
404 $classname = getDolGlobalString('COMMANDE_ADDON');
405
406 // Include file with class
407 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
408 foreach ($dirmodels as $reldir) {
409 $dir = dol_buildpath($reldir."core/modules/commande/");
410
411 // Load file with numbering class (if found)
412 $mybool = ((bool) @include_once $dir.$file) || $mybool;
413 }
414
415 if ($mybool === false) {
416 dol_print_error(null, "Failed to include file ".$file);
417 return '';
418 }
419
420 $obj = new $classname();
421 $numref = $obj->getNextValue($soc, $this);
422
423 if ($numref != "") {
424 return $numref;
425 } else {
426 $this->error = $obj->error;
427 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
428 return "";
429 }
430 } else {
431 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
432 return "";
433 }
434 }
435
436
445 public function valid($user, $idwarehouse = 0, $notrigger = 0)
446 {
447 global $conf, $langs;
448
449 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
450
451 $error = 0;
452
453 // Protection
454 if ($this->statut == self::STATUS_VALIDATED) {
455 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
456 return 0;
457 }
458
459 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
460 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
461 $this->error = 'NotEnoughPermissions';
462 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
463 return -1;
464 }
465
466 $now = dol_now();
467
468 $this->db->begin();
469
470 // Definition du nom de module de numerotation de commande
471 $soc = new Societe($this->db);
472 $soc->fetch($this->socid);
473
474 // Class of company linked to order
475 $result = $soc->setAsCustomer();
476
477 // Define new ref
478 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
479 $num = $this->getNextNumRef($soc);
480 } else {
481 $num = $this->ref;
482 }
483 $this->newref = dol_sanitizeFileName($num);
484
485 // Validate
486 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
487 $sql .= " SET ref = '".$this->db->escape($num)."',";
488 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
489 $sql .= " date_valid='".$this->db->idate($now)."',";
490 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
491 $sql .= " fk_user_modif = ".((int) $user->id);
492 $sql .= " WHERE rowid = ".((int) $this->id);
493
494 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
495 $resql = $this->db->query($sql);
496 if (!$resql) {
497 dol_print_error($this->db);
498 $this->error = $this->db->lasterror();
499 $error++;
500 }
501
502 if (!$error) {
503 // If stock is incremented on validate order, we must increment it
504 if ($result >= 0 && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
505 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
506 $langs->load("agenda");
507
508 // Loop on each line
509 $cpt = count($this->lines);
510 for ($i = 0; $i < $cpt; $i++) {
511 if ($this->lines[$i]->fk_product > 0) {
512 $mouvP = new MouvementStock($this->db);
513 $mouvP->origin = &$this;
514 $mouvP->setOrigin($this->element, $this->id);
515 // We decrement stock of product (and sub-products)
516 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
517 if ($result < 0) {
518 $error++;
519 $this->error = $mouvP->error;
520 }
521 }
522 if ($error) {
523 break;
524 }
525 }
526 }
527 }
528
529 if (!$error && !$notrigger) {
530 // Call trigger
531 $result = $this->call_trigger('ORDER_VALIDATE', $user);
532 if ($result < 0) {
533 $error++;
534 }
535 // End call triggers
536 }
537
538 if (!$error) {
539 $this->oldref = $this->ref;
540
541 // Rename directory if dir was a temporary ref
542 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
543 // Now we rename also files into index
544 $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)."'";
545 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
546 $resql = $this->db->query($sql);
547 if (!$resql) {
548 $error++;
549 $this->error = $this->db->lasterror();
550 }
551 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
552 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
553 $resql = $this->db->query($sql);
554 if (!$resql) {
555 $error++;
556 $this->error = $this->db->lasterror();
557 }
558
559 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
560 $oldref = dol_sanitizeFileName($this->ref);
561 $newref = dol_sanitizeFileName($num);
562 $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
563 $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
564 if (!$error && file_exists($dirsource)) {
565 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
566
567 if (@rename($dirsource, $dirdest)) {
568 dol_syslog("Rename ok");
569 // Rename docs starting with $oldref with $newref
570 $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
571 foreach ($listoffiles as $fileentry) {
572 $dirsource = $fileentry['name'];
573 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
574 $dirsource = $fileentry['path'].'/'.$dirsource;
575 $dirdest = $fileentry['path'].'/'.$dirdest;
576 @rename($dirsource, $dirdest);
577 }
578 }
579 }
580 }
581 }
582
583 // Set new ref and current status
584 if (!$error) {
585 $this->ref = $num;
586 $this->statut = self::STATUS_VALIDATED; // deprecated
588 }
589
590 if (!$error) {
591 $this->db->commit();
592 return 1;
593 } else {
594 $this->db->rollback();
595 return -1;
596 }
597 }
598
599 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
607 public function setDraft($user, $idwarehouse = -1)
608 {
609 //phpcs:enable
610 global $conf, $langs;
611
612 $error = 0;
613
614 // Protection
615 if ($this->statut <= self::STATUS_DRAFT) {
616 return 0;
617 }
618
619 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
620 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'validate')))) {
621 $this->error = 'Permission denied';
622 return -1;
623 }
624
625 dol_syslog(__METHOD__, LOG_DEBUG);
626
627 $this->db->begin();
628
629 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
630 $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
631 $sql .= " fk_user_modif = ".((int) $user->id);
632 $sql .= " WHERE rowid = ".((int) $this->id);
633
634 if ($this->db->query($sql)) {
635 if (!$error) {
636 $this->oldcopy = clone $this;
637 }
638
639 // If stock is decremented on validate order, we must reincrement it
640 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
641 $result = 0;
642
643 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
644 $langs->load("agenda");
645
646 $num = count($this->lines);
647 for ($i = 0; $i < $num; $i++) {
648 if ($this->lines[$i]->fk_product > 0) {
649 $mouvP = new MouvementStock($this->db);
650 $mouvP->origin = &$this;
651 $mouvP->setOrigin($this->element, $this->id);
652 // We increment stock of product (and sub-products)
653 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
654 if ($result < 0) {
655 $error++;
656 $this->error = $mouvP->error;
657 break;
658 }
659 }
660 }
661 }
662
663 if (!$error) {
664 // Call trigger
665 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
666 if ($result < 0) {
667 $error++;
668 }
669 }
670
671 if (!$error) {
672 $this->statut = self::STATUS_DRAFT;
673 $this->db->commit();
674 return 1;
675 } else {
676 $this->db->rollback();
677 return -1;
678 }
679 } else {
680 $this->error = $this->db->error();
681 $this->db->rollback();
682 return -1;
683 }
684 }
685
686
687 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
695 public function set_reopen($user)
696 {
697 // phpcs:enable
698 $error = 0;
699
700 if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
701 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
702 return 0;
703 }
704
705 $this->db->begin();
706
707 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
708 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
709 $sql .= " fk_user_modif = ".((int) $user->id);
710 $sql .= " WHERE rowid = ".((int) $this->id);
711
712 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
713 $resql = $this->db->query($sql);
714 if ($resql) {
715 // Call trigger
716 $result = $this->call_trigger('ORDER_REOPEN', $user);
717 if ($result < 0) {
718 $error++;
719 }
720 // End call triggers
721 } else {
722 $error++;
723 $this->error = $this->db->lasterror();
724 dol_print_error($this->db);
725 }
726
727 if (!$error) {
729 $this->billed = 0;
730
731 $this->db->commit();
732 return 1;
733 } else {
734 foreach ($this->errors as $errmsg) {
735 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
736 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
737 }
738 $this->db->rollback();
739 return -1 * $error;
740 }
741 }
742
750 public function cloture($user, $notrigger = 0)
751 {
752 global $conf;
753
754 $error = 0;
755
756 $usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'creer'))
757 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('commande', 'order_advance', 'close')));
758
759 if ($usercanclose) {
760 if ($this->statut == self::STATUS_CLOSED) {
761 return 0;
762 }
763 $this->db->begin();
764
765 $now = dol_now();
766
767 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
768 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
769 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
770 $sql .= " date_cloture = '".$this->db->idate($now)."',";
771 $sql .= " fk_user_modif = ".((int) $user->id);
772 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
773
774 if ($this->db->query($sql)) {
775 if (!$notrigger) {
776 // Call trigger
777 $result = $this->call_trigger('ORDER_CLOSE', $user);
778 if ($result < 0) {
779 $error++;
780 }
781 // End call triggers
782 }
783
784 if (!$error) {
786
787 $this->db->commit();
788 return 1;
789 } else {
790 $this->db->rollback();
791 return -1;
792 }
793 } else {
794 $this->error = $this->db->lasterror();
795
796 $this->db->rollback();
797 return -1;
798 }
799 }
800 return 0;
801 }
802
810 public function cancel($idwarehouse = -1)
811 {
812 global $conf, $user, $langs;
813
814 $error = 0;
815
816 $this->db->begin();
817
818 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
819 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
820 $sql .= " fk_user_modif = ".((int) $user->id);
821 $sql .= " WHERE rowid = ".((int) $this->id);
822 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
823
824 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
825 if ($this->db->query($sql)) {
826 // If stock is decremented on validate order, we must reincrement it
827 if (isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_VALIDATE_ORDER') == 1) {
828 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
829 $langs->load("agenda");
830
831 $num = count($this->lines);
832 for ($i = 0; $i < $num; $i++) {
833 if ($this->lines[$i]->fk_product > 0) {
834 $mouvP = new MouvementStock($this->db);
835 $mouvP->setOrigin($this->element, $this->id);
836 // We increment stock of product (and sub-products)
837 $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
838 if ($result < 0) {
839 $error++;
840 $this->error = $mouvP->error;
841 break;
842 }
843 }
844 }
845 }
846
847 if (!$error) {
848 // Call trigger
849 $result = $this->call_trigger('ORDER_CANCEL', $user);
850 if ($result < 0) {
851 $error++;
852 }
853 // End call triggers
854 }
855
856 if (!$error) {
858 $this->db->commit();
859 return 1;
860 } else {
861 foreach ($this->errors as $errmsg) {
862 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
863 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
864 }
865 $this->db->rollback();
866 return -1 * $error;
867 }
868 } else {
869 $this->error = $this->db->error();
870 $this->db->rollback();
871 return -1;
872 }
873 }
874
883 public function create($user, $notrigger = 0)
884 {
885 global $conf, $langs, $mysoc;
886 $error = 0;
887
888 // Clean parameters
889
890 // Set tmp vars
891 $date = ($this->date_commande ? $this->date_commande : $this->date);
892 $delivery_date = $this->delivery_date;
893
894 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
895 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
896 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
897 } else {
898 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
899 }
900 if (empty($this->fk_multicurrency)) {
901 $this->multicurrency_code = $conf->currency;
902 $this->fk_multicurrency = 0;
903 $this->multicurrency_tx = 1;
904 }
905
906 dol_syslog(get_class($this)."::create user=".$user->id);
907
908 // Check parameters
909 if (!empty($this->ref)) { // We check that ref is not already used
910 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
911 if ($result > 0) {
912 $this->error = 'ErrorRefAlreadyExists';
913 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
914 $this->db->rollback();
915 return -1;
916 }
917 }
918
919 $soc = new Societe($this->db);
920 $result = $soc->fetch($this->socid);
921 if ($result < 0) {
922 $this->error = "Failed to fetch company";
923 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
924 return -2;
925 }
926 if (getDolGlobalString('ORDER_REQUIRE_SOURCE') && $this->source < 0) {
927 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
928 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
929 return -1;
930 }
931
932 $now = dol_now();
933
934 $this->db->begin();
935
936 $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
937 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
938 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
939 $sql .= ", fk_shipping_method";
940 $sql .= ", fk_warehouse";
941 $sql .= ", fk_incoterms, location_incoterms";
942 $sql .= ", entity, module_source, pos_source";
943 $sql .= ", fk_multicurrency";
944 $sql .= ", multicurrency_code";
945 $sql .= ", multicurrency_tx";
946 $sql .= ")";
947 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
948 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
949 $sql .= ", '".$this->db->idate($date)."'";
950 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
951 $sql .= ", '".$this->db->escape($this->note_private)."'";
952 $sql .= ", '".$this->db->escape($this->note_public)."'";
953 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
954 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
955 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
956 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
957 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
958 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
959 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
960 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
961 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
962 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
963 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
964 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
965 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
966 $sql .= ", ".(int) $this->fk_incoterms;
967 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
968 $sql .= ", ".setEntity($this);
969 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
970 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
971 $sql .= ", ".(int) $this->fk_multicurrency;
972 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
973 $sql .= ", ".(float) $this->multicurrency_tx;
974 $sql .= ")";
975
976 dol_syslog(get_class($this)."::create", LOG_DEBUG);
977 $resql = $this->db->query($sql);
978 if ($resql) {
979 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
980
981 if ($this->id) {
982 $fk_parent_line = 0;
983 $num = count($this->lines);
984
985 /*
986 * Insert products details into db
987 */
988 for ($i = 0; $i < $num; $i++) {
989 $line = $this->lines[$i];
990
991 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
992 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
993 if (!is_object($line)) {
994 $line = (object) $line;
995 }
996
997 // Reset fk_parent_line for no child products and special product
998 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
999 $fk_parent_line = 0;
1000 }
1001
1002 // Complete vat rate with code
1003 $vatrate = $line->tva_tx;
1004 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', (string) $vatrate)) {
1005 $vatrate .= ' ('.$line->vat_src_code.')';
1006 }
1007
1008 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1009 $originid = $line->origin_id;
1010 $origintype = $line->origin;
1011 } else {
1012 $originid = $line->id;
1013 $origintype = $this->element;
1014 }
1015
1016 // ref_ext
1017 if (empty($line->ref_ext)) {
1018 $line->ref_ext = '';
1019 }
1020
1021 $result = $this->addline(
1022 $line->desc,
1023 $line->subprice,
1024 $line->qty,
1025 $vatrate,
1026 $line->localtax1_tx,
1027 $line->localtax2_tx,
1028 $line->fk_product,
1029 $line->remise_percent,
1030 $line->info_bits,
1031 $line->fk_remise_except,
1032 'HT',
1033 0,
1034 $line->date_start,
1035 $line->date_end,
1036 $line->product_type,
1037 $line->rang,
1038 $line->special_code,
1039 $fk_parent_line,
1040 $line->fk_fournprice,
1041 $line->pa_ht,
1042 $line->label,
1043 $line->array_options,
1044 $line->fk_unit,
1045 $origintype,
1046 $originid,
1047 0,
1048 $line->ref_ext,
1049 1
1050 );
1051 if ($result < 0) {
1052 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1053 $this->error = $this->db->lasterror();
1054 $this->errors[] = $this->error;
1055 dol_print_error($this->db);
1056 }
1057 $this->db->rollback();
1058 return -1;
1059 }
1060 // Defined the new fk_parent_line
1061 if ($result > 0 && $line->product_type == 9) {
1062 $fk_parent_line = $result;
1063 }
1064 }
1065
1066 $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.
1067
1068 // update ref
1069 $initialref = '(PROV'.$this->id.')';
1070 if (!empty($this->ref)) {
1071 $initialref = $this->ref;
1072 }
1073
1074 $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1075 if ($this->db->query($sql)) {
1076 $this->ref = $initialref;
1077
1078 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1079 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1080 }
1081
1082 // Add object linked
1083 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1084 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1085 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, ...))
1086 foreach ($tmp_origin_id as $origin_id) {
1087 $ret = $this->add_object_linked($origin, $origin_id);
1088 if (!$ret) {
1089 $this->error = $this->db->lasterror();
1090 $error++;
1091 }
1092 }
1093 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1094 $origin_id = $tmp_origin_id;
1095 $ret = $this->add_object_linked($origin, $origin_id);
1096 if (!$ret) {
1097 $this->error = $this->db->lasterror();
1098 $error++;
1099 }
1100 }
1101 }
1102 }
1103
1104 if (!$error && $this->id && getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN') && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1105 $originforcontact = $this->origin;
1106 $originidforcontact = $this->origin_id;
1107 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1108 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1109 $exp = new Expedition($this->db);
1110 $exp->fetch($this->origin_id);
1111 $exp->fetchObjectLinked();
1112 if (count($exp->linkedObjectsIds['commande']) > 0) {
1113 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1114 $originforcontact = 'commande';
1115 if (is_object($value)) {
1116 $originidforcontact = $value->id;
1117 } else {
1118 $originidforcontact = $value;
1119 }
1120 break; // We take first one
1121 }
1122 }
1123 }
1124
1125 $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";
1126 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1127
1128 $resqlcontact = $this->db->query($sqlcontact);
1129 if ($resqlcontact) {
1130 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1131 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1132 $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
1133 }
1134 } else {
1135 dol_print_error($resqlcontact);
1136 }
1137 }
1138
1139 if (!$error) {
1140 $result = $this->insertExtraFields();
1141 if ($result < 0) {
1142 $error++;
1143 }
1144 }
1145
1146 if (!$error && !$notrigger) {
1147 // Call trigger
1148 $result = $this->call_trigger('ORDER_CREATE', $user);
1149 if ($result < 0) {
1150 $error++;
1151 }
1152 // End call triggers
1153 }
1154
1155 if (!$error) {
1156 $this->db->commit();
1157 return $this->id;
1158 } else {
1159 $this->db->rollback();
1160 return -1 * $error;
1161 }
1162 } else {
1163 $this->error = $this->db->lasterror();
1164 $this->db->rollback();
1165 return -1;
1166 }
1167 }
1168
1169 return 0;
1170 } else {
1171 $this->error = $this->db->lasterror();
1172 $this->db->rollback();
1173 return -1;
1174 }
1175 }
1176
1177
1185 public function createFromClone(User $user, $socid = 0)
1186 {
1187 global $conf, $user, $hookmanager;
1188
1189 $error = 0;
1190
1191 $this->db->begin();
1192
1193 // get lines so they will be clone
1194 foreach ($this->lines as $line) {
1195 $line->fetch_optionals();
1196 }
1197
1198 // Load source object
1199 $objFrom = clone $this;
1200
1201 // Change socid if needed
1202 if (!empty($socid) && $socid != $this->socid) {
1203 $objsoc = new Societe($this->db);
1204
1205 if ($objsoc->fetch($socid) > 0) {
1206 $this->socid = $objsoc->id;
1207 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1208 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1209 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1210 $this->fk_project = 0;
1211 $this->fk_delivery_address = 0;
1212 }
1213
1214 // TODO Change product price if multi-prices
1215 }
1216
1217 $this->id = 0;
1218 $this->ref = '';
1219 $this->statut = self::STATUS_DRAFT;
1220
1221 // Clear fields
1222 $this->user_author_id = $user->id;
1223 $this->user_validation_id = 0;
1224 $this->date = dol_now();
1225 $this->date_commande = dol_now();
1226 $this->date_creation = '';
1227 $this->date_validation = '';
1228 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1229 $this->ref_client = '';
1230 $this->ref_customer = '';
1231 }
1232
1233 // Do not clone ref_ext
1234 $num = count($this->lines);
1235 for ($i = 0; $i < $num; $i++) {
1236 $this->lines[$i]->ref_ext = '';
1237 }
1238
1239 // Create clone
1240 $this->context['createfromclone'] = 'createfromclone';
1241 $result = $this->create($user);
1242 if ($result < 0) {
1243 $error++;
1244 }
1245
1246 if (!$error) {
1247 // copy internal contacts
1248 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1249 $error++;
1250 }
1251 }
1252
1253 if (!$error) {
1254 // copy external contacts if same company
1255 if ($this->socid == $objFrom->socid) {
1256 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1257 $error++;
1258 }
1259 }
1260 }
1261
1262 if (!$error) {
1263 // Hook of thirdparty module
1264 if (is_object($hookmanager)) {
1265 $parameters = array('objFrom' => $objFrom);
1266 $action = '';
1267 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1268 if ($reshook < 0) {
1269 $this->setErrorsFromObject($hookmanager);
1270 $error++;
1271 }
1272 }
1273 }
1274
1275 unset($this->context['createfromclone']);
1276
1277 // End
1278 if (!$error) {
1279 $this->db->commit();
1280 return $this->id;
1281 } else {
1282 $this->db->rollback();
1283 return -1;
1284 }
1285 }
1286
1287
1295 public function createFromProposal($object, User $user)
1296 {
1297 global $conf, $hookmanager;
1298
1299 require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1300 require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
1301
1302 $error = 0;
1303
1304 $this->date_commande = dol_now();
1305 $this->date = dol_now();
1306 $this->source = 0;
1307
1308 $num = count($object->lines);
1309 for ($i = 0; $i < $num; $i++) {
1310 $line = new OrderLine($this->db);
1311
1312 $line->libelle = $object->lines[$i]->libelle;
1313 $line->label = $object->lines[$i]->label;
1314 $line->desc = $object->lines[$i]->desc;
1315 $line->price = $object->lines[$i]->price;
1316 $line->subprice = $object->lines[$i]->subprice;
1317 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1318 $line->tva_tx = $object->lines[$i]->tva_tx;
1319 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1320 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1321 $line->qty = $object->lines[$i]->qty;
1322 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1323 $line->remise_percent = $object->lines[$i]->remise_percent;
1324 $line->fk_product = $object->lines[$i]->fk_product;
1325 $line->info_bits = $object->lines[$i]->info_bits;
1326 $line->product_type = $object->lines[$i]->product_type;
1327 $line->rang = $object->lines[$i]->rang;
1328 $line->special_code = $object->lines[$i]->special_code;
1329 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1330 $line->fk_unit = $object->lines[$i]->fk_unit;
1331
1332 $line->date_start = $object->lines[$i]->date_start;
1333 $line->date_end = $object->lines[$i]->date_end;
1334
1335 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1336 $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);
1337 $line->pa_ht = $marginInfos[0];
1338 $line->marge_tx = $marginInfos[1];
1339 $line->marque_tx = $marginInfos[2];
1340
1341 $line->origin = $object->element;
1342 $line->origin_id = $object->lines[$i]->id;
1343
1344 // get extrafields from original line
1345 $object->lines[$i]->fetch_optionals();
1346 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1347 $line->array_options[$options_key] = $value;
1348 }
1349
1350 $this->lines[$i] = $line;
1351 }
1352
1353 $this->entity = $object->entity;
1354 $this->socid = $object->socid;
1355 $this->fk_project = $object->fk_project;
1356 $this->cond_reglement_id = $object->cond_reglement_id;
1357 $this->deposit_percent = $object->deposit_percent;
1358 $this->mode_reglement_id = $object->mode_reglement_id;
1359 $this->fk_account = $object->fk_account;
1360 $this->availability_id = $object->availability_id;
1361 $this->demand_reason_id = $object->demand_reason_id;
1362 $this->delivery_date = $object->delivery_date;
1363 $this->shipping_method_id = $object->shipping_method_id;
1364 $this->warehouse_id = $object->warehouse_id;
1365 $this->fk_delivery_address = $object->fk_delivery_address;
1366 $this->contact_id = $object->contact_id;
1367 $this->ref_client = $object->ref_client;
1368 $this->ref_customer = $object->ref_client;
1369
1370 if (!getDolGlobalString('MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN')) {
1371 $this->note_private = $object->note_private;
1372 $this->note_public = $object->note_public;
1373 }
1374
1375 $this->origin = $object->element;
1376 $this->origin_id = $object->id;
1377
1378 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1379 if (!empty($conf->multicurrency->enabled)) {
1380 if (!empty($object->multicurrency_code)) {
1381 $this->multicurrency_code = $object->multicurrency_code;
1382 }
1383 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($object->multicurrency_tx)) {
1384 $this->multicurrency_tx = $object->multicurrency_tx;
1385 }
1386
1387 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1388 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1389 $this->fk_multicurrency = $tmparray[0];
1390 $this->multicurrency_tx = $tmparray[1];
1391 } else {
1392 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1393 }
1394 if (empty($this->fk_multicurrency)) {
1395 $this->multicurrency_code = $conf->currency;
1396 $this->fk_multicurrency = 0;
1397 $this->multicurrency_tx = 1;
1398 }
1399 }
1400
1401 // get extrafields from original line
1402 $object->fetch_optionals();
1403
1404 $e = new ExtraFields($this->db);
1405 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1406
1407 foreach ($object->array_options as $options_key => $value) {
1408 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1409 $this->array_options[$options_key] = $value;
1410 }
1411 }
1412 // Possibility to add external linked objects with hooks
1413 $this->linked_objects[$this->origin] = $this->origin_id;
1414 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1415 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1416 }
1417
1418 $ret = $this->create($user);
1419
1420 if ($ret > 0) {
1421 // Actions hooked (by external module)
1422 $hookmanager->initHooks(array('orderdao'));
1423
1424 $parameters = array('objFrom' => $object);
1425 $action = '';
1426 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1427 if ($reshook < 0) {
1428 $this->setErrorsFromObject($hookmanager);
1429 $error++;
1430 }
1431
1432 if (!$error) {
1433 // Validate immediately the order
1434 if (getDolGlobalString('ORDER_VALID_AFTER_CLOSE_PROPAL')) {
1435 $this->fetch($ret);
1436 $this->valid($user);
1437 }
1438 return $ret;
1439 } else {
1440 return -1;
1441 }
1442 } else {
1443 return -1;
1444 }
1445 }
1446
1447
1488 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)
1489 {
1490 global $mysoc, $conf, $langs, $user;
1491
1492 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1493 $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";
1494 $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";
1495 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1496
1497 if ($this->statut == self::STATUS_DRAFT) {
1498 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1499
1500 // Clean parameters
1501
1502 if (empty($remise_percent)) {
1503 $remise_percent = 0;
1504 }
1505 if (empty($qty)) {
1506 $qty = 0;
1507 }
1508 if (empty($info_bits)) {
1509 $info_bits = 0;
1510 }
1511 if (empty($rang)) {
1512 $rang = 0;
1513 }
1514 if (empty($txtva)) {
1515 $txtva = 0;
1516 }
1517 if (empty($txlocaltax1)) {
1518 $txlocaltax1 = 0;
1519 }
1520 if (empty($txlocaltax2)) {
1521 $txlocaltax2 = 0;
1522 }
1523 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1524 $fk_parent_line = 0;
1525 }
1526 if (empty($this->fk_multicurrency)) {
1527 $this->fk_multicurrency = 0;
1528 }
1529 if (empty($ref_ext)) {
1530 $ref_ext = '';
1531 }
1532
1534 $qty = (float) price2num($qty);
1535 $pu_ht = price2num($pu_ht);
1536 $pu_ht_devise = price2num($pu_ht_devise);
1537 $pu_ttc = price2num($pu_ttc);
1538 $pa_ht = (float) price2num($pa_ht);
1539 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1540 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1541 }
1542 $txlocaltax1 = price2num($txlocaltax1);
1543 $txlocaltax2 = price2num($txlocaltax2);
1544 if ($price_base_type == 'HT') {
1545 $pu = $pu_ht;
1546 } else {
1547 $pu = $pu_ttc;
1548 }
1549 $label = trim($label);
1550 $desc = trim($desc);
1551
1552 // Check parameters
1553 if ($type < 0) {
1554 return -1;
1555 }
1556
1557 if ($date_start && $date_end && $date_start > $date_end) {
1558 $langs->load("errors");
1559 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1560 return -1;
1561 }
1562
1563 $this->db->begin();
1564
1565 $product_type = $type;
1566 if (!empty($fk_product) && $fk_product > 0) {
1567 $product = new Product($this->db);
1568 $result = $product->fetch($fk_product);
1569 $product_type = $product->type;
1570
1571 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
1572 $langs->load("errors");
1573 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1574 $this->errors[] = $this->error;
1575 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1576 $this->db->rollback();
1578 }
1579 }
1580 // Calcul du total TTC et de la TVA pour la ligne a partir de
1581 // qty, pu, remise_percent et txtva
1582 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1583 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1584
1585 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1586
1587 // Clean vat code
1588 $reg = array();
1589 $vat_src_code = '';
1590 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1591 $vat_src_code = $reg[1];
1592 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1593 }
1594
1595 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1596
1597 /*var_dump($txlocaltax1);
1598 var_dump($txlocaltax2);
1599 var_dump($localtaxes_type);
1600 var_dump($tabprice);
1601 var_dump($tabprice[9]);
1602 var_dump($tabprice[10]);
1603 exit;*/
1604
1605 $total_ht = $tabprice[0];
1606 $total_tva = $tabprice[1];
1607 $total_ttc = $tabprice[2];
1608 $total_localtax1 = $tabprice[9];
1609 $total_localtax2 = $tabprice[10];
1610 $pu_ht = $tabprice[3];
1611
1612 // MultiCurrency
1613 $multicurrency_total_ht = $tabprice[16];
1614 $multicurrency_total_tva = $tabprice[17];
1615 $multicurrency_total_ttc = $tabprice[18];
1616 $pu_ht_devise = $tabprice[19];
1617
1618 // Rang to use
1619 $ranktouse = $rang;
1620 if ($ranktouse == -1) {
1621 $rangmax = $this->line_max($fk_parent_line);
1622 $ranktouse = $rangmax + 1;
1623 }
1624
1625 // TODO A virer
1626 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1627 $price = $pu;
1628 $remise = 0;
1629 if ($remise_percent > 0) {
1630 $remise = round(((float) $pu * $remise_percent / 100), 2);
1631 $price = (float) $pu - $remise;
1632 }
1633
1634 // Insert line
1635 $this->line = new OrderLine($this->db);
1636
1637 $this->line->context = $this->context;
1638
1639 $this->line->fk_commande = $this->id;
1640 $this->line->label = $label;
1641 $this->line->desc = $desc;
1642 $this->line->qty = $qty;
1643 $this->line->ref_ext = $ref_ext;
1644
1645 $this->line->vat_src_code = $vat_src_code;
1646 $this->line->tva_tx = $txtva;
1647 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1648 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1649 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1650 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1651 $this->line->fk_product = $fk_product;
1652 $this->line->product_type = $product_type;
1653 $this->line->fk_remise_except = $fk_remise_except;
1654 $this->line->remise_percent = $remise_percent;
1655 $this->line->subprice = $pu_ht;
1656 $this->line->rang = $ranktouse;
1657 $this->line->info_bits = $info_bits;
1658 $this->line->total_ht = $total_ht;
1659 $this->line->total_tva = $total_tva;
1660 $this->line->total_localtax1 = $total_localtax1;
1661 $this->line->total_localtax2 = $total_localtax2;
1662 $this->line->total_ttc = $total_ttc;
1663 $this->line->special_code = $special_code;
1664 $this->line->origin = $origin;
1665 $this->line->origin_id = $origin_id;
1666 $this->line->fk_parent_line = $fk_parent_line;
1667 $this->line->fk_unit = $fk_unit;
1668
1669 $this->line->date_start = $date_start;
1670 $this->line->date_end = $date_end;
1671
1672 $this->line->fk_fournprice = $fk_fournprice;
1673 $this->line->pa_ht = $pa_ht;
1674
1675 // Multicurrency
1676 $this->line->fk_multicurrency = $this->fk_multicurrency;
1677 $this->line->multicurrency_code = $this->multicurrency_code;
1678 $this->line->multicurrency_subprice = $pu_ht_devise;
1679 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1680 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1681 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1682
1683 // TODO Ne plus utiliser
1684 $this->line->price = $price;
1685
1686 if (is_array($array_options) && count($array_options) > 0) {
1687 $this->line->array_options = $array_options;
1688 }
1689
1690 $result = $this->line->insert($user);
1691 if ($result > 0) {
1692 // Reorder if child line
1693 if (!empty($fk_parent_line)) {
1694 $this->line_order(true, 'DESC');
1695 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1696 $linecount = count($this->lines);
1697 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1698 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1699 }
1700 }
1701
1702 // Mise a jour information denormalisees au niveau de la commande meme
1703 if (empty($noupdateafterinsertline)) {
1704 $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.
1705 }
1706
1707 if ($result > 0) {
1708 $this->db->commit();
1709 $this->lines[] = $this->line;
1710 return $this->line->id;
1711 } else {
1712 $this->db->rollback();
1713 return -1;
1714 }
1715 } else {
1716 $this->error = $this->line->error;
1717 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1718 $this->db->rollback();
1719 return -2;
1720 }
1721 } else {
1722 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1723 return -3;
1724 }
1725 }
1726
1727
1728 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1742 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1743 {
1744 // phpcs:enable
1745 global $conf, $mysoc;
1746
1747 if (!$qty) {
1748 $qty = 1;
1749 }
1750
1751 if ($idproduct > 0) {
1752 $prod = new Product($this->db);
1753 $prod->fetch($idproduct);
1754
1755 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1756 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1757 if (empty($tva_tx)) {
1758 $tva_npr = 0;
1759 }
1760 $vat_src_code = ''; // May be defined into tva_tx
1761
1762 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1763 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1764
1765 // multiprix
1766 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1767 $price = $prod->multiprices[$this->thirdparty->price_level];
1768 } else {
1769 $price = $prod->price;
1770 }
1771
1772 $line = new OrderLine($this->db);
1773
1774 $line->context = $this->context;
1775
1776 $line->fk_product = $idproduct;
1777 $line->desc = $prod->description;
1778 $line->qty = $qty;
1779 $line->subprice = $price;
1780 $line->remise_percent = $remise_percent;
1781 $line->vat_src_code = $vat_src_code;
1782 $line->tva_tx = $tva_tx;
1783 $line->localtax1_tx = $localtax1_tx;
1784 $line->localtax2_tx = $localtax2_tx;
1785
1786 $line->product_ref = $prod->ref;
1787 $line->product_label = $prod->label;
1788 $line->product_desc = $prod->description;
1789 $line->fk_unit = $prod->fk_unit;
1790
1791 // Save the start and end date of the line in the object
1792 if ($date_start) {
1793 $line->date_start = $date_start;
1794 }
1795 if ($date_end) {
1796 $line->date_end = $date_end;
1797 }
1798
1799 $this->lines[] = $line;
1800
1819 }
1820 }
1821
1822
1832 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1833 {
1834 // Check parameters
1835 if (empty($id) && empty($ref) && empty($ref_ext)) {
1836 return -1;
1837 }
1838
1839 $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';
1840 $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';
1841 $sql .= ', c.fk_account';
1842 $sql .= ', c.date_commande, c.date_valid, c.tms';
1843 $sql .= ', c.date_livraison as delivery_date';
1844 $sql .= ', c.fk_shipping_method';
1845 $sql .= ', c.fk_warehouse';
1846 $sql .= ', c.fk_projet as fk_project, c.source, c.facture as billed';
1847 $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';
1848 $sql .= ', c.fk_incoterms, c.location_incoterms';
1849 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1850 $sql .= ", c.module_source, c.pos_source";
1851 $sql .= ", i.libelle as label_incoterms";
1852 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1853 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1854 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1855 $sql .= ', dr.code as demand_reason_code';
1856 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1857 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1858 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1859 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1860 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1861 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1862
1863 if ($id) {
1864 $sql .= " WHERE c.rowid=".((int) $id);
1865 } else {
1866 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Don't use entity if you use rowid
1867 }
1868
1869 if ($ref) {
1870 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1871 }
1872 if ($ref_ext) {
1873 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1874 }
1875
1876 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1877 $result = $this->db->query($sql);
1878 if ($result) {
1879 $obj = $this->db->fetch_object($result);
1880 if ($obj) {
1881 $this->id = $obj->rowid;
1882 $this->entity = $obj->entity;
1883
1884 $this->ref = $obj->ref;
1885 $this->ref_client = $obj->ref_client;
1886 $this->ref_customer = $obj->ref_client;
1887 $this->ref_ext = $obj->ref_ext;
1888
1889 $this->socid = $obj->fk_soc;
1890 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1891
1892 $this->fk_project = $obj->fk_project;
1893 $this->project = null; // Clear if another value was already set by fetch_projet
1894
1895 $this->statut = $obj->fk_statut;
1896 $this->status = $obj->fk_statut;
1897
1898 $this->user_author_id = $obj->fk_user_author;
1899 $this->user_creation_id = $obj->fk_user_author;
1900 $this->user_validation_id = $obj->fk_user_valid;
1901 $this->user_modification_id = $obj->fk_user_modif;
1902 $this->total_ht = $obj->total_ht;
1903 $this->total_tva = $obj->total_tva;
1904 $this->total_localtax1 = $obj->total_localtax1;
1905 $this->total_localtax2 = $obj->total_localtax2;
1906 $this->total_ttc = $obj->total_ttc;
1907 $this->date = $this->db->jdate($obj->date_commande);
1908 $this->date_commande = $this->db->jdate($obj->date_commande);
1909 $this->date_creation = $this->db->jdate($obj->date_creation);
1910 $this->date_validation = $this->db->jdate($obj->date_valid);
1911 $this->date_modification = $this->db->jdate($obj->tms);
1912 $this->source = $obj->source;
1913 $this->billed = $obj->billed;
1914 $this->note = $obj->note_private; // deprecated
1915 $this->note_private = $obj->note_private;
1916 $this->note_public = $obj->note_public;
1917 $this->model_pdf = $obj->model_pdf;
1918 $this->last_main_doc = $obj->last_main_doc;
1919 $this->mode_reglement_id = $obj->fk_mode_reglement;
1920 $this->mode_reglement_code = $obj->mode_reglement_code;
1921 $this->mode_reglement = $obj->mode_reglement_libelle;
1922 $this->cond_reglement_id = $obj->fk_cond_reglement;
1923 $this->cond_reglement_code = $obj->cond_reglement_code;
1924 $this->cond_reglement = $obj->cond_reglement_libelle;
1925 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1926 $this->deposit_percent = $obj->deposit_percent;
1927 $this->fk_account = $obj->fk_account;
1928 $this->availability_id = $obj->fk_availability;
1929 $this->availability_code = $obj->availability_code;
1930 $this->availability = $obj->availability_label;
1931 $this->demand_reason_id = $obj->fk_input_reason;
1932 $this->demand_reason_code = $obj->demand_reason_code;
1933 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1934 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1935 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1936 $this->fk_delivery_address = $obj->fk_delivery_address;
1937 $this->module_source = $obj->module_source;
1938 $this->pos_source = $obj->pos_source;
1939
1940 //Incoterms
1941 $this->fk_incoterms = $obj->fk_incoterms;
1942 $this->location_incoterms = $obj->location_incoterms;
1943 $this->label_incoterms = $obj->label_incoterms;
1944
1945 // Multicurrency
1946 $this->fk_multicurrency = $obj->fk_multicurrency;
1947 $this->multicurrency_code = $obj->multicurrency_code;
1948 $this->multicurrency_tx = $obj->multicurrency_tx;
1949 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1950 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1951 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1952
1953 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1954
1955 $this->lines = array();
1956
1957 // Retrieve all extrafield
1958 // fetch optionals attributes and labels
1959 $this->fetch_optionals();
1960
1961 $this->db->free($result);
1962
1963 // Lines
1964 $result = $this->fetch_lines();
1965 if ($result < 0) {
1966 return -3;
1967 }
1968 return 1;
1969 } else {
1970 $this->error = 'Order with id '.$id.' not found sql='.$sql;
1971 return 0;
1972 }
1973 } else {
1974 $this->error = $this->db->error();
1975 return -1;
1976 }
1977 }
1978
1979
1980 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1987 public function insert_discount($idremise)
1988 {
1989 // phpcs:enable
1990 global $langs;
1991
1992 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1993 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1994
1995 $this->db->begin();
1996
1997 $remise = new DiscountAbsolute($this->db);
1998 $result = $remise->fetch($idremise);
1999
2000 if ($result > 0) {
2001 if ($remise->fk_facture) { // Protection against multiple submission
2002 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2003 $this->db->rollback();
2004 return -5;
2005 }
2006
2007 $line = new OrderLine($this->db);
2008
2009 $line->fk_commande = $this->id;
2010 $line->fk_remise_except = $remise->id;
2011 $line->desc = $remise->description; // Description ligne
2012 $line->vat_src_code = $remise->vat_src_code;
2013 $line->tva_tx = $remise->tva_tx;
2014 $line->subprice = -$remise->amount_ht;
2015 $line->price = -$remise->amount_ht;
2016 $line->fk_product = 0; // Id produit predefini
2017 $line->qty = 1;
2018 $line->remise_percent = 0;
2019 $line->rang = -1;
2020 $line->info_bits = 2;
2021
2022 $line->total_ht = -$remise->amount_ht;
2023 $line->total_tva = -$remise->amount_tva;
2024 $line->total_ttc = -$remise->amount_ttc;
2025
2026 $result = $line->insert();
2027 if ($result > 0) {
2028 $result = $this->update_price(1);
2029 if ($result > 0) {
2030 $this->db->commit();
2031 return 1;
2032 } else {
2033 $this->db->rollback();
2034 return -1;
2035 }
2036 } else {
2037 $this->error = $line->error;
2038 $this->errors = $line->errors;
2039 $this->db->rollback();
2040 return -2;
2041 }
2042 } else {
2043 $this->db->rollback();
2044 return -2;
2045 }
2046 }
2047
2048
2049 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2057 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2058 {
2059 // phpcs:enable
2060 global $langs, $conf;
2061
2062 $this->lines = array();
2063
2064 $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,';
2065 $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,';
2066 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2067 $sql .= ' l.fk_unit,';
2068 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2069 $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,';
2070 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2071 $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2072 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2073 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2074 if ($only_product) {
2075 $sql .= ' AND p.fk_product_type = 0';
2076 }
2077 $sql .= ' ORDER BY l.rang, l.rowid';
2078
2079 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2080 $result = $this->db->query($sql);
2081 if ($result) {
2082 $num = $this->db->num_rows($result);
2083
2084 $i = 0;
2085 while ($i < $num) {
2086 $objp = $this->db->fetch_object($result);
2087
2088 $line = new OrderLine($this->db);
2089
2090 $line->rowid = $objp->rowid;
2091 $line->id = $objp->rowid;
2092 $line->fk_commande = $objp->fk_commande;
2093 $line->commande_id = $objp->fk_commande;
2094 $line->label = $objp->custom_label;
2095 $line->desc = $objp->description;
2096 $line->description = $objp->description; // Description line
2097 $line->product_type = $objp->product_type;
2098 $line->qty = $objp->qty;
2099 $line->ref_ext = $objp->ref_ext;
2100
2101 $line->vat_src_code = $objp->vat_src_code;
2102 $line->tva_tx = $objp->tva_tx;
2103 $line->localtax1_tx = $objp->localtax1_tx;
2104 $line->localtax2_tx = $objp->localtax2_tx;
2105 $line->localtax1_type = $objp->localtax1_type;
2106 $line->localtax2_type = $objp->localtax2_type;
2107 $line->total_ht = $objp->total_ht;
2108 $line->total_ttc = $objp->total_ttc;
2109 $line->total_tva = $objp->total_tva;
2110 $line->total_localtax1 = $objp->total_localtax1;
2111 $line->total_localtax2 = $objp->total_localtax2;
2112 $line->subprice = $objp->subprice;
2113 $line->fk_remise_except = $objp->fk_remise_except;
2114 $line->remise_percent = $objp->remise_percent;
2115 $line->price = $objp->price;
2116 $line->fk_product = $objp->fk_product;
2117 $line->fk_fournprice = $objp->fk_fournprice;
2118 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2119 $line->pa_ht = $marginInfos[0];
2120 $line->marge_tx = $marginInfos[1];
2121 $line->marque_tx = $marginInfos[2];
2122 $line->rang = $objp->rang;
2123 $line->info_bits = $objp->info_bits;
2124 $line->special_code = $objp->special_code;
2125 $line->fk_parent_line = $objp->fk_parent_line;
2126
2127 $line->ref = $objp->product_ref;
2128 $line->libelle = $objp->product_label;
2129
2130 $line->product_ref = $objp->product_ref;
2131 $line->product_label = $objp->product_label;
2132 $line->product_tosell = $objp->product_tosell;
2133 $line->product_tobuy = $objp->product_tobuy;
2134 $line->product_desc = $objp->product_desc;
2135 $line->product_tobatch = $objp->product_tobatch;
2136 $line->product_barcode = $objp->product_barcode;
2137
2138 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2139 $line->fk_unit = $objp->fk_unit;
2140
2141 $line->weight = $objp->weight;
2142 $line->weight_units = $objp->weight_units;
2143 $line->volume = $objp->volume;
2144 $line->volume_units = $objp->volume_units;
2145
2146 $line->date_start = $this->db->jdate($objp->date_start);
2147 $line->date_end = $this->db->jdate($objp->date_end);
2148
2149 // Multicurrency
2150 $line->fk_multicurrency = $objp->fk_multicurrency;
2151 $line->multicurrency_code = $objp->multicurrency_code;
2152 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2153 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2154 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2155 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2156
2157 $line->fetch_optionals();
2158
2159 // multilangs
2160 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2161 $tmpproduct = new Product($this->db);
2162 $tmpproduct->fetch($objp->fk_product);
2163 $tmpproduct->getMultiLangs();
2164
2165 $line->multilangs = $tmpproduct->multilangs;
2166 }
2167
2168 $this->lines[$i] = $line;
2169
2170 $i++;
2171 }
2172
2173 $this->db->free($result);
2174
2175 return 1;
2176 } else {
2177 $this->error = $this->db->error();
2178 return -3;
2179 }
2180 }
2181
2182
2188 public function getNbOfProductsLines()
2189 {
2190 $nb = 0;
2191 foreach ($this->lines as $line) {
2192 if ($line->product_type == 0) {
2193 $nb++;
2194 }
2195 }
2196 return $nb;
2197 }
2198
2204 public function getNbOfServicesLines()
2205 {
2206 $nb = 0;
2207 foreach ($this->lines as $line) {
2208 if ($line->product_type == 1) {
2209 $nb++;
2210 }
2211 }
2212 return $nb;
2213 }
2214
2220 public function getNbOfShipments()
2221 {
2222 $nb = 0;
2223
2224 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2225 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2226 $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2227 $sql .= ' WHERE';
2228 $sql .= ' ed.fk_elementdet = cd.rowid';
2229 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2230 //print $sql;
2231
2232 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2233 $resql = $this->db->query($sql);
2234 if ($resql) {
2235 $obj = $this->db->fetch_object($resql);
2236 if ($obj) {
2237 $nb = $obj->nb;
2238 }
2239
2240 $this->db->free($resql);
2241 return $nb;
2242 } else {
2243 $this->error = $this->db->lasterror();
2244 return -1;
2245 }
2246 }
2247
2256 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2257 {
2258 $this->expeditions = array();
2259
2260 $sql = 'SELECT cd.rowid, cd.fk_product,';
2261 $sql .= ' sum(ed.qty) as qty';
2262 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2263 if ($filtre_statut >= 0) {
2264 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2265 }
2266 $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2267 $sql .= ' WHERE';
2268 if ($filtre_statut >= 0) {
2269 $sql .= ' ed.fk_expedition = e.rowid AND';
2270 }
2271 $sql .= ' ed.fk_elementdet = cd.rowid';
2272 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2273 if ($fk_product > 0) {
2274 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2275 }
2276 if ($filtre_statut >= 0) {
2277 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2278 }
2279 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2280 //print $sql;
2281
2282 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2283 $resql = $this->db->query($sql);
2284 if ($resql) {
2285 $num = $this->db->num_rows($resql);
2286 $i = 0;
2287 while ($i < $num) {
2288 $obj = $this->db->fetch_object($resql);
2289 $this->expeditions[$obj->rowid] = $obj->qty;
2290 $i++;
2291 }
2292 $this->db->free($resql);
2293 return $num;
2294 } else {
2295 $this->error = $this->db->lasterror();
2296 return -1;
2297 }
2298 }
2299
2305 public function countNbOfShipments()
2306 {
2307 $sql = 'SELECT count(*)';
2308 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2309 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2310 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2311 $sql .= " AND el.sourcetype = 'commande'";
2312 $sql .= " AND el.fk_target = e.rowid";
2313 $sql .= " AND el.targettype = 'shipping'";
2314
2315 $resql = $this->db->query($sql);
2316 if ($resql) {
2317 $row = $this->db->fetch_row($resql);
2318 return $row[0];
2319 } else {
2320 dol_print_error($this->db);
2321 }
2322
2323 return 0;
2324 }
2325
2326 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2335 /*public function stock_array($filtre_statut = self::STATUS_CANCELED)
2336 {
2337 // phpcs:enable
2338 $this->stocks = array();
2339
2340 // Tableau des id de produit de la commande
2341 $array_of_product = array();
2342
2343 // Recherche total en stock pour chaque produit
2344 // TODO $array_of_product est défini vide juste au dessus !!
2345 if (count($array_of_product)) {
2346 $sql = "SELECT fk_product, sum(ps.reel) as total";
2347 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2348 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2349 $sql .= ' GROUP BY fk_product';
2350 $resql = $this->db->query($sql);
2351 if ($resql) {
2352 $num = $this->db->num_rows($resql);
2353 $i = 0;
2354 while ($i < $num) {
2355 $obj = $this->db->fetch_object($resql);
2356 $this->stocks[$obj->fk_product] = $obj->total;
2357 $i++;
2358 }
2359 $this->db->free($resql);
2360 }
2361 }
2362 return 0;
2363 }*/
2364
2373 public function deleteLine($user = null, $lineid = 0, $id = 0)
2374 {
2375 if ($this->statut == self::STATUS_DRAFT) {
2376 $this->db->begin();
2377
2378 // Delete line
2379 $line = new OrderLine($this->db);
2380
2381 $line->context = $this->context;
2382
2383 // Load data
2384 $line->fetch($lineid);
2385
2386 if ($id > 0 && $line->fk_commande != $id) {
2387 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2388 return -1;
2389 }
2390
2391 // Memorize previous line for triggers
2392 $staticline = clone $line;
2393 $line->oldline = $staticline;
2394
2395 if ($line->delete($user) > 0) {
2396 $result = $this->update_price(1);
2397
2398 if ($result > 0) {
2399 $this->db->commit();
2400 return 1;
2401 } else {
2402 $this->db->rollback();
2403 $this->error = $this->db->lasterror();
2404 return -1;
2405 }
2406 } else {
2407 $this->db->rollback();
2408 $this->error = $line->error;
2409 return -1;
2410 }
2411 } else {
2412 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2413 return -1;
2414 }
2415 }
2416
2417 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2428 public function set_remise($user, $remise, $notrigger = 0)
2429 {
2430 // phpcs:enable
2431 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2432 // @phan-suppress-next-line PhanDeprecatedFunction
2433 return $this->setDiscount($user, $remise, $notrigger);
2434 }
2435
2444 public function setDiscount($user, $remise, $notrigger = 0)
2445 {
2446 $remise = trim((string) $remise) ? trim((string) $remise) : 0;
2447
2448 if ($user->hasRight('commande', 'creer')) {
2449 $error = 0;
2450
2451 $this->db->begin();
2452
2453 $remise = price2num($remise, 2);
2454
2455 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2456 $sql .= ' SET remise_percent = '.((float) $remise);
2457 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2458
2459 dol_syslog(__METHOD__, LOG_DEBUG);
2460 $resql = $this->db->query($sql);
2461 if (!$resql) {
2462 $this->errors[] = $this->db->error();
2463 $error++;
2464 }
2465
2466 if (!$error) {
2467 $this->oldcopy = clone $this;
2468 $this->remise_percent = $remise;
2469 $this->update_price(1);
2470 }
2471
2472 if (!$notrigger && empty($error)) {
2473 // Call trigger
2474 $result = $this->call_trigger('ORDER_MODIFY', $user);
2475 if ($result < 0) {
2476 $error++;
2477 }
2478 // End call triggers
2479 }
2480
2481 if (!$error) {
2482 $this->db->commit();
2483 return 1;
2484 } else {
2485 foreach ($this->errors as $errmsg) {
2486 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2487 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2488 }
2489 $this->db->rollback();
2490 return -1 * $error;
2491 }
2492 }
2493
2494 return 0;
2495 }
2496
2497
2498 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2507 /*
2508 public function set_remise_absolue($user, $remise, $notrigger = 0)
2509 {
2510 // phpcs:enable
2511 if (empty($remise)) {
2512 $remise = 0;
2513 }
2514
2515 $remise = price2num($remise);
2516
2517 if ($user->hasRight('commande', 'creer')) {
2518 $error = 0;
2519
2520 $this->db->begin();
2521
2522 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2523 $sql .= ' SET remise_absolue = '.((float) $remise);
2524 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2525
2526 dol_syslog(__METHOD__, LOG_DEBUG);
2527 $resql = $this->db->query($sql);
2528 if (!$resql) {
2529 $this->errors[] = $this->db->error();
2530 $error++;
2531 }
2532
2533 if (!$error) {
2534 $this->oldcopy = clone $this;
2535 $this->remise_absolue = $remise;
2536 $this->update_price(1);
2537 }
2538
2539 if (!$notrigger && empty($error)) {
2540 // Call trigger
2541 $result = $this->call_trigger('ORDER_MODIFY', $user);
2542 if ($result < 0) {
2543 $error++;
2544 }
2545 // End call triggers
2546 }
2547
2548 if (!$error) {
2549 $this->db->commit();
2550 return 1;
2551 } else {
2552 foreach ($this->errors as $errmsg) {
2553 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2554 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2555 }
2556 $this->db->rollback();
2557 return -1 * $error;
2558 }
2559 }
2560
2561 return 0;
2562 }
2563 */
2564
2565 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2574 public function set_date($user, $date, $notrigger = 0)
2575 {
2576 // phpcs:enable
2577 if ($user->hasRight('commande', 'creer')) {
2578 $error = 0;
2579
2580 $this->db->begin();
2581
2582 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2583 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2584 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2585
2586 dol_syslog(__METHOD__, LOG_DEBUG);
2587 $resql = $this->db->query($sql);
2588 if (!$resql) {
2589 $this->errors[] = $this->db->error();
2590 $error++;
2591 }
2592
2593 if (!$error) {
2594 $this->oldcopy = clone $this;
2595 $this->date = $date;
2596 }
2597
2598 if (!$notrigger && empty($error)) {
2599 // Call trigger
2600 $result = $this->call_trigger('ORDER_MODIFY', $user);
2601 if ($result < 0) {
2602 $error++;
2603 }
2604 // End call triggers
2605 }
2606
2607 if (!$error) {
2608 $this->db->commit();
2609 return 1;
2610 } else {
2611 foreach ($this->errors as $errmsg) {
2612 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2613 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2614 }
2615 $this->db->rollback();
2616 return -1 * $error;
2617 }
2618 } else {
2619 return -2;
2620 }
2621 }
2622
2623 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2633 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2634 {
2635 // phpcs:enable
2636 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2637 }
2638
2647 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2648 {
2649 if ($user->hasRight('commande', 'creer')) {
2650 $error = 0;
2651
2652 $this->db->begin();
2653
2654 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2655 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2656 $sql .= " WHERE rowid = ".((int) $this->id);
2657
2658 dol_syslog(__METHOD__, LOG_DEBUG);
2659 $resql = $this->db->query($sql);
2660 if (!$resql) {
2661 $this->errors[] = $this->db->error();
2662 $error++;
2663 }
2664
2665 if (!$error) {
2666 $this->oldcopy = clone $this;
2667 $this->delivery_date = $delivery_date;
2668 }
2669
2670 if (!$notrigger && empty($error)) {
2671 // Call trigger
2672 $result = $this->call_trigger('ORDER_MODIFY', $user);
2673 if ($result < 0) {
2674 $error++;
2675 }
2676 // End call triggers
2677 }
2678
2679 if (!$error) {
2680 $this->db->commit();
2681 return 1;
2682 } else {
2683 foreach ($this->errors as $errmsg) {
2684 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2685 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2686 }
2687 $this->db->rollback();
2688 return -1 * $error;
2689 }
2690 } else {
2691 return -2;
2692 }
2693 }
2694
2695 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2709 public function liste_array($shortlist = 0, $draft = 0, $excluser = null, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2710 {
2711 // phpcs:enable
2712 global $user;
2713
2714 $ga = array();
2715
2716 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2717 $sql .= " c.rowid as cid, c.ref";
2718 if (!$user->hasRight('societe', 'client', 'voir')) {
2719 $sql .= ", sc.fk_soc, sc.fk_user";
2720 }
2721 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2722 if (!$user->hasRight('societe', 'client', 'voir')) {
2723 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2724 }
2725 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2726 $sql .= " AND c.fk_soc = s.rowid";
2727 if (!$user->hasRight('societe', 'client', 'voir')) {
2728 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2729 }
2730 if ($socid) {
2731 $sql .= " AND s.rowid = ".((int) $socid);
2732 }
2733 if ($draft) {
2734 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2735 }
2736 if (is_object($excluser)) {
2737 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2738 }
2739 $sql .= $this->db->order($sortfield, $sortorder);
2740 $sql .= $this->db->plimit($limit, $offset);
2741
2742 $result = $this->db->query($sql);
2743 if ($result) {
2744 $numc = $this->db->num_rows($result);
2745 if ($numc) {
2746 $i = 0;
2747 while ($i < $numc) {
2748 $obj = $this->db->fetch_object($result);
2749
2750 if ($shortlist == 1) {
2751 $ga[$obj->cid] = $obj->ref;
2752 } elseif ($shortlist == 2) {
2753 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2754 } else {
2755 $ga[$i]['id'] = $obj->cid;
2756 $ga[$i]['ref'] = $obj->ref;
2757 $ga[$i]['name'] = $obj->name;
2758 }
2759 $i++;
2760 }
2761 }
2762 return $ga;
2763 } else {
2764 dol_print_error($this->db);
2765 return -1;
2766 }
2767 }
2768
2776 public function availability($availability_id, $notrigger = 0)
2777 {
2778 global $user;
2779
2780 dol_syslog('Commande::availability('.$availability_id.')');
2781 if ($this->statut >= self::STATUS_DRAFT) {
2782 $error = 0;
2783
2784 $this->db->begin();
2785
2786 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2787 $sql .= ' SET fk_availability = '.((int) $availability_id);
2788 $sql .= ' WHERE rowid='.((int) $this->id);
2789
2790 dol_syslog(__METHOD__, LOG_DEBUG);
2791 $resql = $this->db->query($sql);
2792 if (!$resql) {
2793 $this->errors[] = $this->db->error();
2794 $error++;
2795 }
2796
2797 if (!$error) {
2798 $this->oldcopy = clone $this;
2799 $this->availability_id = $availability_id;
2800 }
2801
2802 if (!$notrigger && empty($error)) {
2803 // Call trigger
2804 $result = $this->call_trigger('ORDER_MODIFY', $user);
2805 if ($result < 0) {
2806 $error++;
2807 }
2808 // End call triggers
2809 }
2810
2811 if (!$error) {
2812 $this->db->commit();
2813 return 1;
2814 } else {
2815 foreach ($this->errors as $errmsg) {
2816 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2817 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2818 }
2819 $this->db->rollback();
2820 return -1 * $error;
2821 }
2822 } else {
2823 $error_str = 'Command status do not meet requirement '.$this->statut;
2824 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2825 $this->error = $error_str;
2826 $this->errors[] = $this->error;
2827 return -2;
2828 }
2829 }
2830
2831 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2839 public function demand_reason($demand_reason_id, $notrigger = 0)
2840 {
2841 // phpcs:enable
2842 global $user;
2843
2844 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2845 if ($this->statut >= self::STATUS_DRAFT) {
2846 $error = 0;
2847
2848 $this->db->begin();
2849
2850 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2851 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2852 $sql .= ' WHERE rowid='.((int) $this->id);
2853
2854 dol_syslog(__METHOD__, LOG_DEBUG);
2855 $resql = $this->db->query($sql);
2856 if (!$resql) {
2857 $this->errors[] = $this->db->error();
2858 $error++;
2859 }
2860
2861 if (!$error) {
2862 $this->oldcopy = clone $this;
2863 $this->demand_reason_id = $demand_reason_id;
2864 }
2865
2866 if (!$notrigger && empty($error)) {
2867 // Call trigger
2868 $result = $this->call_trigger('ORDER_MODIFY', $user);
2869 if ($result < 0) {
2870 $error++;
2871 }
2872 // End call triggers
2873 }
2874
2875 if (!$error) {
2876 $this->db->commit();
2877 return 1;
2878 } else {
2879 foreach ($this->errors as $errmsg) {
2880 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2881 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2882 }
2883 $this->db->rollback();
2884 return -1 * $error;
2885 }
2886 } else {
2887 $error_str = 'order status do not meet requirement '.$this->statut;
2888 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2889 $this->error = $error_str;
2890 $this->errors[] = $this->error;
2891 return -2;
2892 }
2893 }
2894
2895 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2904 public function set_ref_client($user, $ref_client, $notrigger = 0)
2905 {
2906 // phpcs:enable
2907 if ($user->hasRight('commande', 'creer')) {
2908 $error = 0;
2909
2910 $this->db->begin();
2911
2912 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2913 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2914 $sql .= ' WHERE rowid = '.((int) $this->id);
2915
2916 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2917 $resql = $this->db->query($sql);
2918 if (!$resql) {
2919 $this->errors[] = $this->db->error();
2920 $error++;
2921 }
2922
2923 if (!$error) {
2924 $this->oldcopy = clone $this;
2925 $this->ref_client = $ref_client;
2926 $this->ref_customer = $ref_client;
2927 }
2928
2929 if (!$notrigger && empty($error)) {
2930 // Call trigger
2931 $result = $this->call_trigger('ORDER_MODIFY', $user);
2932 if ($result < 0) {
2933 $error++;
2934 }
2935 // End call triggers
2936 }
2937 if (!$error) {
2938 $this->db->commit();
2939 return 1;
2940 } else {
2941 foreach ($this->errors as $errmsg) {
2942 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2943 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2944 }
2945 $this->db->rollback();
2946 return -1 * $error;
2947 }
2948 } else {
2949 return -1;
2950 }
2951 }
2952
2960 public function classifyBilled(User $user, $notrigger = 0)
2961 {
2962 $error = 0;
2963
2964 if ($this->billed) {
2965 return 0;
2966 }
2967
2968 $this->db->begin();
2969
2970 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2971 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
2972
2973 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2974 if ($this->db->query($sql)) {
2975 if (!$error) {
2976 $this->oldcopy = clone $this;
2977 $this->billed = 1;
2978 }
2979
2980 if (!$notrigger && empty($error)) {
2981 // Call trigger
2982 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
2983 if ($result < 0) {
2984 $error++;
2985 }
2986 // End call triggers
2987 }
2988
2989 if (!$error) {
2990 $this->db->commit();
2991 return 1;
2992 } else {
2993 foreach ($this->errors as $errmsg) {
2994 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
2995 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2996 }
2997 $this->db->rollback();
2998 return -1 * $error;
2999 }
3000 } else {
3001 $this->error = $this->db->error();
3002 $this->db->rollback();
3003 return -1;
3004 }
3005 }
3006
3014 public function classifyUnBilled(User $user, $notrigger = 0)
3015 {
3016 $error = 0;
3017
3018 $this->db->begin();
3019
3020 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3021 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3022
3023 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3024 if ($this->db->query($sql)) {
3025 if (!$error) {
3026 $this->oldcopy = clone $this;
3027 $this->billed = 1;
3028 }
3029
3030 if (!$notrigger && empty($error)) {
3031 // Call trigger
3032 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3033 if ($result < 0) {
3034 $error++;
3035 }
3036 // End call triggers
3037 }
3038
3039 if (!$error) {
3040 $this->billed = 0;
3041
3042 $this->db->commit();
3043 return 1;
3044 } else {
3045 foreach ($this->errors as $errmsg) {
3046 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3047 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3048 }
3049 $this->db->rollback();
3050 return -1 * $error;
3051 }
3052 } else {
3053 $this->error = $this->db->error();
3054 $this->db->rollback();
3055 return -1;
3056 }
3057 }
3058
3059
3090 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)
3091 {
3092 global $conf, $mysoc, $langs, $user;
3093
3094 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");
3095 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3096
3097 if ($this->statut == Commande::STATUS_DRAFT) {
3098 // Clean parameters
3099 if (empty($qty)) {
3100 $qty = 0;
3101 }
3102 if (empty($info_bits)) {
3103 $info_bits = 0;
3104 }
3105 if (empty($txtva)) {
3106 $txtva = 0;
3107 }
3108 if (empty($txlocaltax1)) {
3109 $txlocaltax1 = 0;
3110 }
3111 if (empty($txlocaltax2)) {
3112 $txlocaltax2 = 0;
3113 }
3114 if (empty($remise_percent)) {
3115 $remise_percent = 0;
3116 }
3117 if (empty($special_code) || $special_code == 3) {
3118 $special_code = 0;
3119 }
3120 if (empty($ref_ext)) {
3121 $ref_ext = '';
3122 }
3123
3124 if ($date_start && $date_end && $date_start > $date_end) {
3125 $langs->load("errors");
3126 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3127 return -1;
3128 }
3129
3131 $qty = (float) price2num($qty);
3132 $pu = price2num($pu);
3133 $pa_ht = (float) price2num($pa_ht);
3134 $pu_ht_devise = price2num($pu_ht_devise);
3135 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3136 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3137 }
3138 $txlocaltax1 = (float) price2num($txlocaltax1);
3139 $txlocaltax2 = (float) price2num($txlocaltax2);
3140
3141 $this->db->begin();
3142
3143 // Calcul du total TTC et de la TVA pour la ligne a partir de
3144 // qty, pu, remise_percent et txtva
3145 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3146 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3147
3148 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3149
3150 // Clean vat code
3151 $vat_src_code = '';
3152 $reg = array();
3153 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3154 $vat_src_code = $reg[1];
3155 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3156 }
3157
3158 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
3159
3160 $total_ht = $tabprice[0];
3161 $total_tva = $tabprice[1];
3162 $total_ttc = $tabprice[2];
3163 $total_localtax1 = $tabprice[9];
3164 $total_localtax2 = $tabprice[10];
3165 $pu_ht = $tabprice[3];
3166 $pu_tva = $tabprice[4];
3167 $pu_ttc = $tabprice[5];
3168
3169 // MultiCurrency
3170 $multicurrency_total_ht = $tabprice[16];
3171 $multicurrency_total_tva = $tabprice[17];
3172 $multicurrency_total_ttc = $tabprice[18];
3173 $pu_ht_devise = $tabprice[19];
3174
3175 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3176 $price = $pu_ht;
3177 if ($price_base_type == 'TTC') {
3178 $subprice = $pu_ttc;
3179 } else {
3180 $subprice = $pu_ht;
3181 }
3182 $remise = 0;
3183 if ($remise_percent > 0) {
3184 $remise = round(((float) $pu * $remise_percent / 100), 2);
3185 $price = ((float) $pu - $remise);
3186 }
3187
3188 //Fetch current line from the database and then clone the object and set it in $oldline property
3189 $line = new OrderLine($this->db);
3190 $line->fetch($rowid);
3191 $line->fetch_optionals();
3192
3193 if (!empty($line->fk_product)) {
3194 $product = new Product($this->db);
3195 $result = $product->fetch($line->fk_product);
3196 $product_type = $product->type;
3197
3198 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
3199 $langs->load("errors");
3200 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3201 $this->errors[] = $this->error;
3202
3203 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3204
3205 $this->db->rollback();
3207 }
3208 }
3209
3210 $staticline = clone $line;
3211
3212 $line->oldline = $staticline;
3213 $this->line = $line;
3214 $this->line->context = $this->context;
3215 $this->line->rang = $rang;
3216
3217 // Reorder if fk_parent_line change
3218 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3219 $rangmax = $this->line_max($fk_parent_line);
3220 $this->line->rang = $rangmax + 1;
3221 }
3222
3223 $this->line->id = $rowid;
3224 $this->line->label = $label;
3225 $this->line->desc = $desc;
3226 $this->line->qty = $qty;
3227 $this->line->ref_ext = $ref_ext;
3228
3229 $this->line->vat_src_code = $vat_src_code;
3230 $this->line->tva_tx = $txtva;
3231 $this->line->localtax1_tx = $txlocaltax1;
3232 $this->line->localtax2_tx = $txlocaltax2;
3233 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3234 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3235 $this->line->remise_percent = $remise_percent;
3236 $this->line->subprice = $pu_ht;
3237 $this->line->info_bits = $info_bits;
3238 $this->line->special_code = $special_code;
3239 $this->line->total_ht = $total_ht;
3240 $this->line->total_tva = $total_tva;
3241 $this->line->total_localtax1 = $total_localtax1;
3242 $this->line->total_localtax2 = $total_localtax2;
3243 $this->line->total_ttc = $total_ttc;
3244 $this->line->date_start = $date_start;
3245 $this->line->date_end = $date_end;
3246 $this->line->product_type = $type;
3247 $this->line->fk_parent_line = $fk_parent_line;
3248 $this->line->skip_update_total = $skip_update_total;
3249 $this->line->fk_unit = $fk_unit;
3250
3251 $this->line->fk_fournprice = $fk_fournprice;
3252 $this->line->pa_ht = $pa_ht;
3253
3254 // Multicurrency
3255 $this->line->multicurrency_subprice = $pu_ht_devise;
3256 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3257 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3258 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3259
3260 // TODO deprecated
3261 $this->line->price = $price;
3262
3263 if (is_array($array_options) && count($array_options) > 0) {
3264 // We replace values in this->line->array_options only for entries defined into $array_options
3265 foreach ($array_options as $key => $value) {
3266 $this->line->array_options[$key] = $array_options[$key];
3267 }
3268 }
3269
3270 $result = $this->line->update($user, $notrigger);
3271 if ($result > 0) {
3272 // Reorder if child line
3273 if (!empty($fk_parent_line)) {
3274 $this->line_order(true, 'DESC');
3275 }
3276
3277 // Mise a jour info denormalisees
3278 $this->update_price(1, 'auto');
3279
3280 $this->db->commit();
3281 return $result;
3282 } else {
3283 $this->error = $this->line->error;
3284
3285 $this->db->rollback();
3286 return -1;
3287 }
3288 } else {
3289 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3290 $this->errors = array('OrderStatusMakeOperationForbidden');
3291 return -2;
3292 }
3293 }
3294
3302 public function update(User $user, $notrigger = 0)
3303 {
3304 global $conf;
3305
3306 $error = 0;
3307
3308 // Clean parameters
3309 if (isset($this->ref)) {
3310 $this->ref = trim($this->ref);
3311 }
3312 if (isset($this->ref_client)) {
3313 $this->ref_client = trim($this->ref_client);
3314 }
3315 if (isset($this->ref_customer)) {
3316 $this->ref_customer = trim($this->ref_customer);
3317 }
3318 if (isset($this->note) || isset($this->note_private)) {
3319 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3320 }
3321 if (isset($this->note_public)) {
3322 $this->note_public = trim($this->note_public);
3323 }
3324 if (isset($this->model_pdf)) {
3325 $this->model_pdf = trim($this->model_pdf);
3326 }
3327 if (isset($this->import_key)) {
3328 $this->import_key = trim($this->import_key);
3329 }
3330 $delivery_date = $this->delivery_date;
3331
3332 // Check parameters
3333 // Put here code to add control on parameters values
3334
3335 // Update request
3336 $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3337
3338 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3339 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3340 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3341 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3342 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3343 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3344 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3345 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3346 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3347 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3348 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3349 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3350 $sql .= " fk_user_modif=".(isset($user->id) ? $user->id : "null").",";
3351 $sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? $this->user_validation_id : "null").",";
3352 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3353 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3354 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3355 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3356 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3357 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3358 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3359 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3360 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3361 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3362 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3363 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3364
3365 $sql .= " WHERE rowid=".((int) $this->id);
3366
3367 $this->db->begin();
3368
3369 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3370 $resql = $this->db->query($sql);
3371 if (!$resql) {
3372 $error++;
3373 $this->errors[] = "Error ".$this->db->lasterror();
3374 }
3375
3376 if (!$error) {
3377 $result = $this->insertExtraFields();
3378 if ($result < 0) {
3379 $error++;
3380 }
3381 }
3382
3383 if (!$error && !$notrigger) {
3384 // Call trigger
3385 $result = $this->call_trigger('ORDER_MODIFY', $user);
3386 if ($result < 0) {
3387 $error++;
3388 }
3389 // End call triggers
3390 }
3391
3392 // Commit or rollback
3393 if ($error) {
3394 foreach ($this->errors as $errmsg) {
3395 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3396 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3397 }
3398 $this->db->rollback();
3399 return -1 * $error;
3400 } else {
3401 $this->db->commit();
3402 return 1;
3403 }
3404 }
3405
3413 public function delete($user, $notrigger = 0)
3414 {
3415 global $conf, $langs;
3416 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3417
3418 $error = 0;
3419
3420 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3421
3422 $this->db->begin();
3423
3424 if (!$notrigger) {
3425 // Call trigger
3426 $result = $this->call_trigger('ORDER_DELETE', $user);
3427 if ($result < 0) {
3428 $error++;
3429 }
3430 // End call triggers
3431 }
3432
3433 // Test we can delete
3434 if ($this->countNbOfShipments() != 0) {
3435 $this->errors[] = $langs->trans('SomeShipmentExists');
3436 $error++;
3437 }
3438
3439 // Delete extrafields of lines and lines
3440 if (!$error && !empty($this->table_element_line)) {
3441 $tabletodelete = $this->table_element_line;
3442 $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).")";
3443 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3444 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3445 $error++;
3446 $this->error = $this->db->lasterror();
3447 $this->errors[] = $this->error;
3448 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3449 }
3450 }
3451
3452 if (!$error) {
3453 // Delete linked object
3454 $res = $this->deleteObjectLinked();
3455 if ($res < 0) {
3456 $error++;
3457 }
3458 }
3459
3460 if (!$error) {
3461 // Delete linked contacts
3462 $res = $this->delete_linked_contact();
3463 if ($res < 0) {
3464 $error++;
3465 }
3466 }
3467
3468 // Removed extrafields of object
3469 if (!$error) {
3470 $result = $this->deleteExtraFields();
3471 if ($result < 0) {
3472 $error++;
3473 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3474 }
3475 }
3476
3477 // Delete main record
3478 if (!$error) {
3479 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3480 $res = $this->db->query($sql);
3481 if (!$res) {
3482 $error++;
3483 $this->error = $this->db->lasterror();
3484 $this->errors[] = $this->error;
3485 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3486 }
3487 }
3488
3489 // Delete record into ECM index and physically
3490 if (!$error) {
3491 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3492 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3493 if (!$res) {
3494 $error++;
3495 }
3496 }
3497
3498 if (!$error) {
3499 // We remove directory
3500 $ref = dol_sanitizeFileName($this->ref);
3501 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3502 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3503 $file = $dir."/".$ref.".pdf";
3504 if (file_exists($file)) {
3505 dol_delete_preview($this);
3506
3507 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3508 $this->error = 'ErrorFailToDeleteFile';
3509 $this->errors[] = $this->error;
3510 $this->db->rollback();
3511 return 0;
3512 }
3513 }
3514 if (file_exists($dir)) {
3515 $res = @dol_delete_dir_recursive($dir);
3516 if (!$res) {
3517 $this->error = 'ErrorFailToDeleteDir';
3518 $this->errors[] = $this->error;
3519 $this->db->rollback();
3520 return 0;
3521 }
3522 }
3523 }
3524 }
3525
3526 if (!$error) {
3527 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3528 $this->db->commit();
3529 return 1;
3530 } else {
3531 $this->db->rollback();
3532 return -1;
3533 }
3534 }
3535
3536
3537 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3545 public function load_board($user, $mode)
3546 {
3547 // phpcs:enable
3548 global $conf, $langs;
3549
3550 $clause = " WHERE";
3551
3552 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3553 $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3554 if (!$user->hasRight('societe', 'client', 'voir')) {
3555 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3556 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3557 $clause = " AND";
3558 }
3559 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3560 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3561 if ($mode == 'toship') {
3562 // An order to ship is an open order (validated or in progress)
3563 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ")";
3564 }
3565 if ($mode == 'tobill') {
3566 // An order to bill is an order not already billed
3567 $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . "," . self::STATUS_SHIPMENTONPROCESS . ", " . self::STATUS_CLOSED . ") AND c.facture = 0";
3568 }
3569 if ($mode == 'shippedtobill') {
3570 // An order shipped and to bill is a delivered order not already billed
3571 $sql .= " AND c.fk_statut IN (" . self::STATUS_CLOSED . ") AND c.facture = 0";
3572 }
3573 if ($user->socid) {
3574 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3575 }
3576
3577 $resql = $this->db->query($sql);
3578 if ($resql) {
3579 $delay_warning = 0;
3580 $label = $labelShort = $url = '';
3581 if ($mode == 'toship') {
3582 $delay_warning = $conf->commande->client->warning_delay / 60 / 60 / 24;
3583 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-2&mainmenu=commercial&leftmenu=orders';
3584 $label = $langs->transnoentitiesnoconv("OrdersToProcess");
3585 $labelShort = $langs->transnoentitiesnoconv("Opened");
3586 }
3587 if ($mode == 'tobill') {
3588 $url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3589 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3590 $labelShort = $langs->trans("ToBill");
3591 }
3592 if ($mode == 'shippedtobill') {
3593 $url = DOL_URL_ROOT.'/commande/list.php?search_status=3&search_billed=0&mainmenu=commercial&leftmenu=orders';
3594 $label = $langs->trans("OrdersToBill"); // We set here bill but may be billed or ordered
3595 $labelShort = $langs->trans("StatusOrderDelivered").' '.$langs->trans("and").' '.$langs->trans("ToBill");
3596 }
3597
3598 $response = new WorkboardResponse();
3599
3600 $response->warning_delay = $delay_warning;
3601 $response->label = $label;
3602 $response->labelShort = $labelShort;
3603 $response->url = $url;
3604 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3605 $response->img = img_object('', "order");
3606
3607 $generic_commande = new Commande($this->db);
3608
3609 while ($obj = $this->db->fetch_object($resql)) {
3610 $response->nbtodo++;
3611 $response->total += $obj->total_ht;
3612
3613 $generic_commande->statut = $obj->fk_statut;
3614 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3615 $generic_commande->date = $this->db->jdate($obj->date_commande);
3616 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3617
3618 if ($mode == 'toship' && $generic_commande->hasDelay()) {
3619 $response->nbtodolate++;
3620 }
3621 }
3622
3623 return $response;
3624 } else {
3625 $this->error = $this->db->error();
3626 return -1;
3627 }
3628 }
3629
3635 public function getLabelSource()
3636 {
3637 global $langs;
3638
3639 $label = $langs->trans('OrderSource'.$this->source);
3640
3641 if ($label == 'OrderSource') {
3642 return '';
3643 }
3644 return $label;
3645 }
3646
3653 public function getLibStatut($mode)
3654 {
3655 return $this->LibStatut($this->statut, $this->billed, $mode);
3656 }
3657
3658 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3668 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3669 {
3670 // phpcs:enable
3671 global $langs, $hookmanager;
3672
3673 $billedtext = '';
3674 if (empty($donotshowbilled)) {
3675 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3676 }
3677
3678 $labelTooltip = '';
3679
3680 if ($status == self::STATUS_CANCELED) {
3681 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3682 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3683 $statusType = 'status9';
3684 } elseif ($status == self::STATUS_DRAFT) {
3685 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3686 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3687 $statusType = 'status0';
3688 } elseif ($status == self::STATUS_VALIDATED) {
3689 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3690 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3691 $statusType = 'status1';
3692 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3693 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3694 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3695 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3696 if (!empty($this->delivery_date)) {
3697 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3698 }
3699 $statusType = 'status4';
3700 } elseif ($status == self::STATUS_CLOSED) {
3701 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered').$billedtext;
3702 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort').$billedtext;
3703 $statusType = 'status6';
3704 } else {
3705 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3706 $labelStatusShort = '';
3707 $statusType = '';
3708 $mode = 0;
3709 }
3710
3711 $parameters = array(
3712 'status' => $status,
3713 'mode' => $mode,
3714 'billed' => $billed,
3715 'donotshowbilled' => $donotshowbilled
3716 );
3717
3718 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3719
3720 if ($reshook > 0) {
3721 return $hookmanager->resPrint;
3722 }
3723
3724 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3725 }
3726
3733 public function getTooltipContentArray($params)
3734 {
3735 global $conf, $langs, $user;
3736
3737 $langs->load('orders');
3738 $datas = [];
3739 $nofetch = !empty($params['nofetch']);
3740
3741 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3742 return ['optimize' => $langs->trans("Order")];
3743 }
3744
3745 if ($user->hasRight('commande', 'lire')) {
3746 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3747 if (isset($this->statut)) {
3748 $datas['status'] = ' '.$this->getLibStatut(5);
3749 }
3750 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3751 if (!$nofetch) {
3752 $langs->load('companies');
3753 if (empty($this->thirdparty)) {
3754 $this->fetch_thirdparty();
3755 }
3756 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3757 }
3758 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3759 if (!$nofetch) {
3760 $langs->load('project');
3761 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3762 $res = $this->fetch_project();
3763 if ($res > 0 && $this->project instanceof Project) {
3764 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3765 }
3766 }
3767 }
3768 if (!empty($this->total_ht)) {
3769 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3770 }
3771 if (!empty($this->total_tva)) {
3772 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3773 }
3774 if (!empty($this->total_ttc)) {
3775 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3776 }
3777 if (!empty($this->date)) {
3778 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3779 }
3780 if (!empty($this->delivery_date)) {
3781 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3782 }
3783 }
3784
3785 return $datas;
3786 }
3787
3801 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3802 {
3803 global $conf, $langs, $user, $hookmanager;
3804
3805 if (!empty($conf->dol_no_mouse_hover)) {
3806 $notooltip = 1; // Force disable tooltips
3807 }
3808
3809 $result = '';
3810
3811 if (isModEnabled("shipping") && ($option == '1' || $option == '2')) {
3812 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3813 } else {
3814 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3815 }
3816
3817 if (!$user->hasRight('commande', 'lire')) {
3818 $option = 'nolink';
3819 }
3820
3821 if ($option !== 'nolink') {
3822 // Add param to save lastsearch_values or not
3823 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3824 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3825 $add_save_lastsearch_values = 1;
3826 }
3827 if ($add_save_lastsearch_values) {
3828 $url .= '&save_lastsearch_values=1';
3829 }
3830 }
3831
3832 if ($short) {
3833 return $url;
3834 }
3835 $params = [
3836 'id' => $this->id,
3837 'objecttype' => $this->element,
3838 'option' => $option,
3839 'nofetch' => 1,
3840 ];
3841 $classfortooltip = 'classfortooltip';
3842 $dataparams = '';
3843 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3844 $classfortooltip = 'classforajaxtooltip';
3845 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3846 $label = '';
3847 } else {
3848 $label = implode($this->getTooltipContentArray($params));
3849 }
3850
3851 $linkclose = '';
3852 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3853 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3854 $label = $langs->trans("Order");
3855 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3856 }
3857 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3858 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3859
3860 $target_value = array('_self', '_blank', '_parent', '_top');
3861 if (in_array($target, $target_value)) {
3862 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3863 }
3864 }
3865
3866 $linkstart = '<a href="'.$url.'"';
3867 $linkstart .= $linkclose.'>';
3868 $linkend = '</a>';
3869
3870 if ($option === 'nolink') {
3871 $linkstart = '';
3872 $linkend = '';
3873 }
3874
3875 $result .= $linkstart;
3876 if ($withpicto) {
3877 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3878 }
3879 if ($withpicto != 2) {
3880 $result .= $this->ref;
3881 }
3882 $result .= $linkend;
3883
3884 if ($addlinktonotes) {
3885 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3886 if ($txttoshow) {
3887 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3888 $result .= ' <span class="note inline-block">';
3889 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3890 $result .= img_picto('', 'note');
3891 $result .= '</a>';
3892 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3893 //$result.='</a>';
3894 $result .= '</span>';
3895 }
3896 }
3897
3898 global $action;
3899 $hookmanager->initHooks(array($this->element . 'dao'));
3900 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3901 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3902 if ($reshook > 0) {
3903 $result = $hookmanager->resPrint;
3904 } else {
3905 $result .= $hookmanager->resPrint;
3906 }
3907 return $result;
3908 }
3909
3910
3917 public function info($id)
3918 {
3919 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3920 $sql .= ' date_valid as datev,';
3921 $sql .= ' date_cloture as datecloture,';
3922 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3923 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3924 $sql .= ' WHERE c.rowid = '.((int) $id);
3925 $result = $this->db->query($sql);
3926 if ($result) {
3927 if ($this->db->num_rows($result)) {
3928 $obj = $this->db->fetch_object($result);
3929 $this->id = $obj->rowid;
3930 if ($obj->fk_user_author) {
3931 $this->user_creation_id = $obj->fk_user_author;
3932 }
3933 if ($obj->fk_user_valid) {
3934 $this->user_validation_id = $obj->fk_user_valid;
3935 }
3936 if ($obj->fk_user_cloture) {
3937 $this->user_closing_id = $obj->fk_user_cloture;
3938 }
3939
3940 $this->date_creation = $this->db->jdate($obj->datec);
3941 $this->date_modification = $this->db->jdate($obj->datem);
3942 $this->date_validation = $this->db->jdate($obj->datev);
3943 $this->date_cloture = $this->db->jdate($obj->datecloture);
3944 }
3945
3946 $this->db->free($result);
3947 } else {
3948 dol_print_error($this->db);
3949 }
3950 }
3951
3952
3960 public function initAsSpecimen()
3961 {
3962 global $conf, $langs;
3963
3964 dol_syslog(get_class($this)."::initAsSpecimen");
3965
3966 // Load array of products prodids
3967 $num_prods = 0;
3968 $prodids = array();
3969 $sql = "SELECT rowid";
3970 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3971 $sql .= " WHERE entity IN (".getEntity('product').")";
3972 $sql .= $this->db->plimit(100);
3973
3974 $resql = $this->db->query($sql);
3975 if ($resql) {
3976 $num_prods = $this->db->num_rows($resql);
3977 $i = 0;
3978 while ($i < $num_prods) {
3979 $i++;
3980 $row = $this->db->fetch_row($resql);
3981 $prodids[$i] = $row[0];
3982 }
3983 }
3984
3985 // Initialise parameters
3986 $this->id = 0;
3987 $this->ref = 'SPECIMEN';
3988 $this->specimen = 1;
3989 $this->entity = $conf->entity;
3990 $this->socid = 1;
3991 $this->date = time();
3992 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3993 $this->cond_reglement_code = 'RECEP';
3994 $this->mode_reglement_code = 'CHQ';
3995 $this->availability_code = 'DSP';
3996 $this->demand_reason_code = 'SRC_00';
3997
3998 $this->note_public = 'This is a comment (public)';
3999 $this->note_private = 'This is a comment (private)';
4000
4001 $this->multicurrency_tx = 1;
4002 $this->multicurrency_code = $conf->currency;
4003
4004 $this->status = $this::STATUS_DRAFT;
4005
4006 // Lines
4007 $nbp = 5;
4008 $xnbp = 0;
4009 while ($xnbp < $nbp) {
4010 $line = new OrderLine($this->db);
4011
4012 $line->desc = $langs->trans("Description")." ".$xnbp;
4013 $line->qty = 1;
4014 $line->subprice = 100;
4015 $line->price = 100;
4016 $line->tva_tx = 20;
4017 if ($xnbp == 2) {
4018 $line->total_ht = 50;
4019 $line->total_ttc = 60;
4020 $line->total_tva = 10;
4021 $line->remise_percent = 50;
4022 } else {
4023 $line->total_ht = 100;
4024 $line->total_ttc = 120;
4025 $line->total_tva = 20;
4026 $line->remise_percent = 0;
4027 }
4028 if ($num_prods > 0) {
4029 $prodid = mt_rand(1, $num_prods);
4030 $line->fk_product = $prodids[$prodid];
4031 $line->product_ref = 'SPECIMEN';
4032 }
4033
4034 $this->lines[$xnbp] = $line;
4035
4036 $this->total_ht += $line->total_ht;
4037 $this->total_tva += $line->total_tva;
4038 $this->total_ttc += $line->total_ttc;
4039
4040 $xnbp++;
4041 }
4042
4043 return 1;
4044 }
4045
4046
4052 public function loadStateBoard()
4053 {
4054 global $user;
4055
4056 $this->nb = array();
4057 $clause = "WHERE";
4058
4059 $sql = "SELECT count(co.rowid) as nb";
4060 $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
4061 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4062 if (!$user->hasRight('societe', 'client', 'voir')) {
4063 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4064 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4065 $clause = "AND";
4066 }
4067 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4068
4069 $resql = $this->db->query($sql);
4070 if ($resql) {
4071 while ($obj = $this->db->fetch_object($resql)) {
4072 $this->nb["orders"] = $obj->nb;
4073 }
4074 $this->db->free($resql);
4075 return 1;
4076 } else {
4077 dol_print_error($this->db);
4078 $this->error = $this->db->error();
4079 return -1;
4080 }
4081 }
4082
4088 public function getLinesArray()
4089 {
4090 return $this->fetch_lines();
4091 }
4092
4104 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4105 {
4106 global $conf, $langs;
4107
4108 $langs->load("orders");
4109 $outputlangs->load("products");
4110
4111 if (!dol_strlen($modele)) {
4112 $modele = 'einstein';
4113
4114 if (!empty($this->model_pdf)) {
4115 $modele = $this->model_pdf;
4116 } elseif (getDolGlobalString('COMMANDE_ADDON_PDF')) {
4117 $modele = getDolGlobalString('COMMANDE_ADDON_PDF');
4118 }
4119 }
4120
4121 $modelpath = "core/modules/commande/doc/";
4122
4123 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4124 }
4125
4126
4135 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4136 {
4137 $tables = array(
4138 'commande'
4139 );
4140
4141 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4142 }
4143
4152 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4153 {
4154 $tables = array(
4155 'commandedet',
4156 );
4157
4158 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4159 }
4160
4166 public function hasDelay()
4167 {
4168 global $conf;
4169
4170 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4171 return false; // Never late if not inside this status range
4172 }
4173
4174 $now = dol_now();
4175
4176 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4177 }
4178
4184 public function showDelay()
4185 {
4186 global $conf, $langs;
4187
4188 if (empty($this->delivery_date)) {
4189 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date, 'day');
4190 } else {
4191 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
4192 }
4193 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4194
4195 return $text;
4196 }
4197}
4198
4199
4204{
4208 public $element = 'commandedet';
4209
4210 public $table_element = 'commandedet';
4211
4212 public $oldline;
4213
4218 public $fk_commande;
4219
4226 public $commande_id;
4227
4228 public $fk_parent_line;
4229
4233 public $fk_facture;
4234
4238 public $ref_ext;
4239
4240 public $fk_remise_except;
4241
4245 public $rang = 0;
4246 public $fk_fournprice;
4247
4252 public $pa_ht;
4253 public $marge_tx;
4254 public $marque_tx;
4255
4260 public $remise;
4261
4262 // Start and end date of the line
4263 public $date_start;
4264 public $date_end;
4265
4266 public $skip_update_total; // Skip update price total for special lines
4267
4268
4274 public function __construct($db)
4275 {
4276 $this->db = $db;
4277 }
4278
4285 public function fetch($rowid)
4286 {
4287 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_parent_line, cd.fk_product, cd.product_type, cd.label as custom_label, cd.description, cd.price, cd.qty, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx,';
4288 $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4289 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.fk_product_fournisseur_price as fk_fournprice, cd.buy_price_ht as pa_ht, cd.rang, cd.special_code,';
4290 $sql .= ' cd.fk_unit,';
4291 $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4292 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4293 $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4294 $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4295 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4296 $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4297 $result = $this->db->query($sql);
4298 if ($result) {
4299 $objp = $this->db->fetch_object($result);
4300
4301 if (!$objp) {
4302 $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4303 return 0;
4304 }
4305
4306 $this->rowid = $objp->rowid;
4307 $this->id = $objp->rowid;
4308 $this->fk_commande = $objp->fk_commande;
4309 $this->fk_parent_line = $objp->fk_parent_line;
4310 $this->label = $objp->custom_label;
4311 $this->desc = $objp->description;
4312 $this->qty = $objp->qty;
4313 $this->price = $objp->price;
4314 $this->subprice = $objp->subprice;
4315 $this->ref_ext = $objp->ref_ext;
4316 $this->vat_src_code = $objp->vat_src_code;
4317 $this->tva_tx = $objp->tva_tx;
4318 $this->localtax1_tx = $objp->localtax1_tx;
4319 $this->localtax2_tx = $objp->localtax2_tx;
4320 $this->remise = $objp->remise;
4321 $this->remise_percent = $objp->remise_percent;
4322 $this->fk_remise_except = $objp->fk_remise_except;
4323 $this->fk_product = $objp->fk_product;
4324 $this->product_type = $objp->product_type;
4325 $this->info_bits = $objp->info_bits;
4326 $this->special_code = $objp->special_code;
4327 $this->total_ht = $objp->total_ht;
4328 $this->total_tva = $objp->total_tva;
4329 $this->total_localtax1 = $objp->total_localtax1;
4330 $this->total_localtax2 = $objp->total_localtax2;
4331 $this->total_ttc = $objp->total_ttc;
4332 $this->fk_fournprice = $objp->fk_fournprice;
4333 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4334 $this->pa_ht = $marginInfos[0];
4335 $this->marge_tx = $marginInfos[1];
4336 $this->marque_tx = $marginInfos[2];
4337 $this->special_code = $objp->special_code;
4338 $this->rang = $objp->rang;
4339
4340 $this->ref = $objp->product_ref; // deprecated
4341
4342 $this->product_ref = $objp->product_ref;
4343 $this->product_label = $objp->product_label;
4344 $this->product_desc = $objp->product_desc;
4345 $this->product_tobatch = $objp->product_tobatch;
4346 $this->fk_unit = $objp->fk_unit;
4347
4348 $this->date_start = $this->db->jdate($objp->date_start);
4349 $this->date_end = $this->db->jdate($objp->date_end);
4350
4351 $this->fk_multicurrency = $objp->fk_multicurrency;
4352 $this->multicurrency_code = $objp->multicurrency_code;
4353 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4354 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4355 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4356 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4357
4358 $this->fetch_optionals();
4359
4360 $this->db->free($result);
4361
4362 return 1;
4363 } else {
4364 $this->error = $this->db->lasterror();
4365 return -1;
4366 }
4367 }
4368
4376 public function delete(User $user, $notrigger = 0)
4377 {
4378 global $conf, $langs;
4379
4380 $error = 0;
4381
4382 if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4383 $this->id = $this->rowid;
4384 }
4385
4386 // check if order line is not in a shipment line before deleting
4387 $sqlCheckShipmentLine = "SELECT";
4388 $sqlCheckShipmentLine .= " ed.rowid";
4389 $sqlCheckShipmentLine .= " FROM " . MAIN_DB_PREFIX . "expeditiondet ed";
4390 $sqlCheckShipmentLine .= " WHERE ed.fk_elementdet = " . ((int) $this->id);
4391
4392 $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4393 if (!$resqlCheckShipmentLine) {
4394 $error++;
4395 $this->error = $this->db->lasterror();
4396 $this->errors[] = $this->error;
4397 } else {
4398 $langs->load('errors');
4399 $num = $this->db->num_rows($resqlCheckShipmentLine);
4400 if ($num > 0) {
4401 $error++;
4402 $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4403 $this->error = $langs->trans('ErrorRecordAlreadyExists') . ' : ' . $langs->trans('ShipmentLine') . ' ' . $objCheckShipmentLine->rowid;
4404 $this->errors[] = $this->error;
4405 }
4406 $this->db->free($resqlCheckShipmentLine);
4407 }
4408 if ($error) {
4409 dol_syslog(__METHOD__ . 'Error ; ' . $this->error, LOG_ERR);
4410 return -1;
4411 }
4412
4413 $this->db->begin();
4414
4415 if (!$notrigger) {
4416 // Call trigger
4417 $result = $this->call_trigger('LINEORDER_DELETE', $user);
4418 if ($result < 0) {
4419 $error++;
4420 }
4421 // End call triggers
4422 }
4423
4424 if (!$error) {
4425 $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . "commandedet WHERE rowid = " . ((int) $this->id);
4426
4427 dol_syslog("OrderLine::delete", LOG_DEBUG);
4428 $resql = $this->db->query($sql);
4429 if (!$resql) {
4430 $this->error = $this->db->lasterror();
4431 $error++;
4432 }
4433 }
4434
4435 // Remove extrafields
4436 if (!$error) {
4437 $result = $this->deleteExtraFields();
4438 if ($result < 0) {
4439 $error++;
4440 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4441 }
4442 }
4443
4444 if (!$error) {
4445 $this->db->commit();
4446 return 1;
4447 }
4448
4449 foreach ($this->errors as $errmsg) {
4450 dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
4451 $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
4452 }
4453 $this->db->rollback();
4454 return -1 * $error;
4455 }
4456
4464 public function insert($user = null, $notrigger = 0)
4465 {
4466 $error = 0;
4467
4468 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4469
4470 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4471
4472 // Clean parameters
4473 if (empty($this->tva_tx)) {
4474 $this->tva_tx = 0;
4475 }
4476 if (empty($this->localtax1_tx)) {
4477 $this->localtax1_tx = 0;
4478 }
4479 if (empty($this->localtax2_tx)) {
4480 $this->localtax2_tx = 0;
4481 }
4482 if (empty($this->localtax1_type)) {
4483 $this->localtax1_type = 0;
4484 }
4485 if (empty($this->localtax2_type)) {
4486 $this->localtax2_type = 0;
4487 }
4488 if (empty($this->total_localtax1)) {
4489 $this->total_localtax1 = 0;
4490 }
4491 if (empty($this->total_localtax2)) {
4492 $this->total_localtax2 = 0;
4493 }
4494 if (empty($this->rang)) {
4495 $this->rang = 0;
4496 }
4497 if (empty($this->remise_percent)) {
4498 $this->remise_percent = 0;
4499 }
4500 if (empty($this->info_bits)) {
4501 $this->info_bits = 0;
4502 }
4503 if (empty($this->special_code)) {
4504 $this->special_code = 0;
4505 }
4506 if (empty($this->fk_parent_line)) {
4507 $this->fk_parent_line = 0;
4508 }
4509 if (empty($this->pa_ht)) {
4510 $this->pa_ht = 0;
4511 }
4512 if (empty($this->ref_ext)) {
4513 $this->ref_ext = '';
4514 }
4515
4516 // if buy price not defined, define buyprice as configured in margin admin
4517 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4518 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4519 if ($result < 0) {
4520 return $result;
4521 } else {
4522 $this->pa_ht = $result;
4523 }
4524 }
4525
4526 // Check parameters
4527 if ($this->product_type < 0) {
4528 return -1;
4529 }
4530
4531 $this->db->begin();
4532
4533 // Insertion dans base de la ligne
4534 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4535 $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4536 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4537 $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4538 $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4539 $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4540 $sql .= ' fk_unit,';
4541 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4542 $sql .= ')';
4543 $sql .= " VALUES (".$this->fk_commande.",";
4544 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4545 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4546 $sql .= " '".$this->db->escape($this->desc)."',";
4547 $sql .= " '".price2num($this->qty)."',";
4548 $sql .= " '".$this->db->escape($this->ref_ext)."',";
4549 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4550 $sql .= " '".price2num($this->tva_tx)."',";
4551 $sql .= " '".price2num($this->localtax1_tx)."',";
4552 $sql .= " '".price2num($this->localtax2_tx)."',";
4553 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4554 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4555 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4556 $sql .= " '".$this->db->escape($this->product_type)."',";
4557 $sql .= " '".price2num($this->remise_percent)."',";
4558 $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice) : "null").",";
4559 $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4560 $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4561 $sql .= ' '.((int) $this->special_code).',';
4562 $sql .= ' '.((int) $this->rang).',';
4563 $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4564 $sql .= ' '.price2num($this->pa_ht).',';
4565 $sql .= " ".((int) $this->info_bits).",";
4566 $sql .= " ".price2num($this->total_ht, 'MT').",";
4567 $sql .= " ".price2num($this->total_tva, 'MT').",";
4568 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4569 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4570 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4571 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4572 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4573 $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4574 $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4575 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4576 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4577 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4578 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4579 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4580 $sql .= ')';
4581
4582 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4583 $resql = $this->db->query($sql);
4584 if ($resql) {
4585 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4586 $this->rowid = $this->id;
4587
4588 if (!$error) {
4589 $result = $this->insertExtraFields();
4590 if ($result < 0) {
4591 $error++;
4592 }
4593 }
4594
4595 if (!$error && !$notrigger) {
4596 // Call trigger
4597 $result = $this->call_trigger('LINEORDER_INSERT', $user);
4598 if ($result < 0) {
4599 $error++;
4600 }
4601 // End call triggers
4602 }
4603
4604 if (!$error) {
4605 $this->db->commit();
4606 return 1;
4607 }
4608
4609 foreach ($this->errors as $errmsg) {
4610 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4611 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4612 }
4613 $this->db->rollback();
4614 return -1 * $error;
4615 } else {
4616 $this->error = $this->db->error();
4617 $this->db->rollback();
4618 return -2;
4619 }
4620 }
4621
4629 public function update(User $user, $notrigger = 0)
4630 {
4631 $error = 0;
4632
4633 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4634
4635 // Clean parameters
4636 if (empty($this->tva_tx)) {
4637 $this->tva_tx = 0;
4638 }
4639 if (empty($this->localtax1_tx)) {
4640 $this->localtax1_tx = 0;
4641 }
4642 if (empty($this->localtax2_tx)) {
4643 $this->localtax2_tx = 0;
4644 }
4645 if (empty($this->localtax1_type)) {
4646 $this->localtax1_type = 0;
4647 }
4648 if (empty($this->localtax2_type)) {
4649 $this->localtax2_type = 0;
4650 }
4651 if (empty($this->qty)) {
4652 $this->qty = 0;
4653 }
4654 if (empty($this->total_localtax1)) {
4655 $this->total_localtax1 = 0;
4656 }
4657 if (empty($this->total_localtax2)) {
4658 $this->total_localtax2 = 0;
4659 }
4660 if (empty($this->marque_tx)) {
4661 $this->marque_tx = 0;
4662 }
4663 if (empty($this->marge_tx)) {
4664 $this->marge_tx = 0;
4665 }
4666 if (empty($this->remise_percent)) {
4667 $this->remise_percent = 0;
4668 }
4669 if (empty($this->remise)) {
4670 $this->remise = 0;
4671 }
4672 if (empty($this->info_bits)) {
4673 $this->info_bits = 0;
4674 }
4675 if (empty($this->special_code)) {
4676 $this->special_code = 0;
4677 }
4678 if (empty($this->product_type)) {
4679 $this->product_type = 0;
4680 }
4681 if (empty($this->fk_parent_line)) {
4682 $this->fk_parent_line = 0;
4683 }
4684 if (empty($this->pa_ht)) {
4685 $this->pa_ht = 0;
4686 }
4687 if (empty($this->ref_ext)) {
4688 $this->ref_ext = '';
4689 }
4690
4691 // if buy price not defined, define buyprice as configured in margin admin
4692 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4693 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4694 if ($result < 0) {
4695 return $result;
4696 } else {
4697 $this->pa_ht = $result;
4698 }
4699 }
4700
4701 $this->db->begin();
4702
4703 // Mise a jour ligne en base
4704 $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4705 $sql .= " description='".$this->db->escape($this->desc)."'";
4706 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4707 $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4708 $sql .= " , tva_tx=".price2num($this->tva_tx);
4709 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4710 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4711 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4712 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4713 $sql .= " , qty=".price2num($this->qty);
4714 $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4715 $sql .= " , subprice=".price2num($this->subprice);
4716 $sql .= " , remise_percent=".price2num($this->remise_percent);
4717 $sql .= " , price=".price2num($this->price); // TODO A virer
4718 $sql .= " , remise=".price2num($this->remise); // TODO A virer
4719 if (empty($this->skip_update_total)) {
4720 $sql .= " , total_ht=".price2num($this->total_ht);
4721 $sql .= " , total_tva=".price2num($this->total_tva);
4722 $sql .= " , total_ttc=".price2num($this->total_ttc);
4723 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4724 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4725 }
4726 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4727 $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4728 $sql .= " , info_bits=".((int) $this->info_bits);
4729 $sql .= " , special_code=".((int) $this->special_code);
4730 $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4731 $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4732 $sql .= " , product_type=".$this->product_type;
4733 $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4734 if (!empty($this->rang)) {
4735 $sql .= ", rang=".((int) $this->rang);
4736 }
4737 $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4738
4739 // Multicurrency
4740 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4741 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4742 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4743 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4744
4745 $sql .= " WHERE rowid = ".((int) $this->rowid);
4746
4747 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4748 $resql = $this->db->query($sql);
4749 if ($resql) {
4750 if (!$error) {
4751 $this->id = $this->rowid;
4752 $result = $this->insertExtraFields();
4753 if ($result < 0) {
4754 $error++;
4755 }
4756 }
4757
4758 if (!$error && !$notrigger) {
4759 // Call trigger
4760 $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4761 if ($result < 0) {
4762 $error++;
4763 }
4764 // End call triggers
4765 }
4766
4767 if (!$error) {
4768 $this->db->commit();
4769 return 1;
4770 }
4771
4772 foreach ($this->errors as $errmsg) {
4773 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4774 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4775 }
4776 $this->db->rollback();
4777 return -1 * $error;
4778 } else {
4779 $this->error = $this->db->error();
4780 $this->db->rollback();
4781 return -2;
4782 }
4783 }
4784
4785 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4792 public function update_total()
4793 {
4794 // phpcs:enable
4795 $this->db->begin();
4796
4797 // Clean parameters
4798 if (empty($this->total_localtax1)) {
4799 $this->total_localtax1 = 0;
4800 }
4801 if (empty($this->total_localtax2)) {
4802 $this->total_localtax2 = 0;
4803 }
4804
4805 // Mise a jour ligne en base
4806 $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4807 $sql .= " total_ht='".price2num($this->total_ht)."'";
4808 $sql .= ",total_tva='".price2num($this->total_tva)."'";
4809 $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4810 $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4811 $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4812 $sql .= " WHERE rowid = ".((int) $this->rowid);
4813
4814 dol_syslog("OrderLine::update_total", LOG_DEBUG);
4815
4816 $resql = $this->db->query($sql);
4817 if ($resql) {
4818 $this->db->commit();
4819 return 1;
4820 } else {
4821 $this->error = $this->db->error();
4822 $this->db->rollback();
4823 return -2;
4824 }
4825 }
4826}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:637
$object ref
Definition info.php:79
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 a fixed amount discount.
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.
$pos_source
key of pos source ('0', '1', ...)
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 clicable 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.
getNextNumRef($soc)
Returns the reference to the following non used Order depending on the active numbering module define...
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 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 line of fixed discount in the order in DB.
getNbOfProductsLines()
Return number of line with type product.
update(User $user, $notrigger=0)
Update database.
$module_source
key of module source when order generated from a dedicated module ('cashdesk', 'takepos',...
classifyUnBilled(User $user, $notrigger=0)
Classify the order as not invoiced.
setDiscount($user, $remise, $notrigger=0)
Set a percentage discount.
cloture($user, $notrigger=0)
Close order.
cancel($idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
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)
Return a array with the pending stock by product.
countNbOfShipments()
Returns an array with expeditions lines number.
liste_array($shortlist=0, $draft=0, $excluser=null, $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
Return list of orders (eventuelly filtered on a user) into an array.
fetch($id, $ref='', $ref_ext='', $notused='')
Get object from database.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
Add line into array $this->client must be loaded.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
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.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
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)
fetch_project()
Load the project with id $this->fk_project into this->project.
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.
Superclass for orders classes.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class to manage shipments.
Class to manage standard extra fields.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage order lines.
insert($user=null, $notrigger=0)
Insert line into database.
update(User $user, $notrigger=0)
Update the line object into db.
update_total()
Update DB line fields total_xxx Used by migration.
__construct($db)
Constructor.
fetch($rowid)
Load line order.
Class to manage products or services.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
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_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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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)
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return 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.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1991
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:1991