dolibarr 18.0.6
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-2023 Frédéric France <frederic.france@netlogic.fr>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <https://www.gnu.org/licenses/>.
30 */
31
37include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
38require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
40require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
41require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
42require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
43
44
48class Commande extends CommonOrder
49{
53 public $element = 'commande';
54
58 public $table_element = 'commande';
59
63 public $table_element_line = 'commandedet';
64
68 public $class_element_line = 'OrderLine';
69
73 public $fk_element = 'fk_commande';
74
78 public $picto = 'order';
79
84 public $ismultientitymanaged = 1;
85
90 public $restrictiononfksoc = 1;
91
95 protected $table_ref_field = 'ref';
96
100 public $socid;
101
105 public $ref_client;
106
110 public $ref_customer;
111
115 public $contactid;
116
121 public $statut;
122
126 public $billed;
127
131 public $brouillon;
132
136 public $cond_reglement_code;
137
141 public $cond_reglement_doc;
142
146 public $deposit_percent;
147
151 public $fk_account;
152
156 public $mode_reglement;
157
161 public $mode_reglement_id;
162
166 public $mode_reglement_code;
167
172 public $availability_id;
173
178 public $availability_code;
179
184 public $availability;
185
189 public $demand_reason_id;
190
194 public $demand_reason_code;
195
199 public $date;
200
206 public $date_commande;
207
213 public $date_livraison;
214
218 public $delivery_date;
219
223 public $fk_remise_except;
224
229
230 public $remise_absolue;
231 public $info_bits;
232 public $rang;
233 public $special_code;
234 public $source; // Order mode. How we received order (by phone, by email, ...)
235
239 public $warehouse_id;
240
241 public $extraparams = array();
242
243 public $linked_objects = array();
244
248 public $user_author_id;
249
253 public $user_valid;
254
258 public $line;
259
263 public $lines = array();
264
265 // Multicurrency
269 public $fk_multicurrency;
270
274 public $multicurrency_code;
275 public $multicurrency_tx;
276 public $multicurrency_total_ht;
277 public $multicurrency_total_tva;
278 public $multicurrency_total_ttc;
279 public $multicurrency_total_localtax1; // not in database
280 public $multicurrency_total_localtax2; // not in database
281
286
290 public $expeditions;
291
295 public $online_payment_url;
296
297
322 // BEGIN MODULEBUILDER PROPERTIES
326 public $fields = array(
327 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
328 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
329 'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25),
330 'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>26),
331 'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>28),
332 'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>-1, 'notnull'=>1, 'position'=>20),
333 'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>25),
334 'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>1, 'position'=>60),
335 'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
336 'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
337 'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
338 'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
339 'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
340 //'amount_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
341 //'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
342 'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
343 //'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
344 'total_tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
345 'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
346 'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
347 'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
348 'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
349 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>150),
350 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155),
351 'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>160),
352 //'facture' =>array('type'=>'tinyint(4)', 'label'=>'ParentInvoice', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
353 'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>-1, 'position'=>170),
354 'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
355 'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
356 'deposit_percent' =>array('type'=>'varchar(63)', 'label'=>'DepositPercent', 'enabled'=>1, 'visible'=>-1, 'position'=>181),
357 'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
358 'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
359 'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
360 'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>'isModEnabled("stock")', 'visible'=>-1, 'position'=>200),
361 'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
362 'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
363 //'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
364 'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
365 'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>230),
366 'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>235),
367 'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
368 'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>245),
369 'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
370 'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
371 'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>260, 'isameasure'=>1),
372 'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>265, 'isameasure'=>1),
373 'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>270),
374 'module_source' =>array('type'=>'varchar(32)', 'label'=>'POSModule', 'enabled'=>1, 'visible'=>-1, 'position'=>275),
375 'pos_source' =>array('type'=>'varchar(32)', 'label'=>'POSTerminal', 'enabled'=>1, 'visible'=>-1, 'position'=>280),
376 'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>300),
377 'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>302),
378 'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>304),
379 'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>306),
380 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
381 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
382 );
383 // END MODULEBUILDER PROPERTIES
384
389
393 const STATUS_CANCELED = -1;
397 const STATUS_DRAFT = 0;
406 const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
407
411 const STATUS_CLOSED = 3;
412
413
419 public function __construct($db)
420 {
421 $this->db = $db;
422 }
423
431 public function getNextNumRef($soc)
432 {
433 global $langs, $conf;
434 $langs->load("order");
435
436 if (!empty($conf->global->COMMANDE_ADDON)) {
437 $mybool = false;
438
439 $file = $conf->global->COMMANDE_ADDON.".php";
440 $classname = $conf->global->COMMANDE_ADDON;
441
442 // Include file with class
443 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
444 foreach ($dirmodels as $reldir) {
445 $dir = dol_buildpath($reldir."core/modules/commande/");
446
447 // Load file with numbering class (if found)
448 $mybool |= @include_once $dir.$file;
449 }
450
451 if ($mybool === false) {
452 dol_print_error('', "Failed to include file ".$file);
453 return '';
454 }
455
456 $obj = new $classname();
457 $numref = $obj->getNextValue($soc, $this);
458
459 if ($numref != "") {
460 return $numref;
461 } else {
462 $this->error = $obj->error;
463 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
464 return "";
465 }
466 } else {
467 print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
468 return "";
469 }
470 }
471
472
481 public function valid($user, $idwarehouse = 0, $notrigger = 0)
482 {
483 global $conf, $langs;
484
485 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
486
487 $error = 0;
488
489 // Protection
490 if ($this->statut == self::STATUS_VALIDATED) {
491 dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
492 return 0;
493 }
494
495 if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
496 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
497 $this->error = 'NotEnoughPermissions';
498 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
499 return -1;
500 }
501
502 $now = dol_now();
503
504 $this->db->begin();
505
506 // Definition du nom de module de numerotation de commande
507 $soc = new Societe($this->db);
508 $soc->fetch($this->socid);
509
510 // Class of company linked to order
511 $result = $soc->set_as_client();
512
513 // Define new ref
514 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
515 $num = $this->getNextNumRef($soc);
516 } else {
517 $num = $this->ref;
518 }
519 $this->newref = dol_sanitizeFileName($num);
520
521 // Validate
522 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
523 $sql .= " SET ref = '".$this->db->escape($num)."',";
524 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
525 $sql .= " date_valid='".$this->db->idate($now)."',";
526 $sql .= " fk_user_valid = ".($user->id > 0 ? (int) $user->id : "null").",";
527 $sql .= " fk_user_modif = ".((int) $user->id);
528 $sql .= " WHERE rowid = ".((int) $this->id);
529
530 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
531 $resql = $this->db->query($sql);
532 if (!$resql) {
533 dol_print_error($this->db);
534 $this->error = $this->db->lasterror();
535 $error++;
536 }
537
538 if (!$error) {
539 // If stock is incremented on validate order, we must increment it
540 if ($result >= 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
541 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
542 $langs->load("agenda");
543
544 // Loop on each line
545 $cpt = count($this->lines);
546 for ($i = 0; $i < $cpt; $i++) {
547 if ($this->lines[$i]->fk_product > 0) {
548 $mouvP = new MouvementStock($this->db);
549 $mouvP->origin = &$this;
550 $mouvP->setOrigin($this->element, $this->id);
551 // We decrement stock of product (and sub-products)
552 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
553 if ($result < 0) {
554 $error++;
555 $this->error = $mouvP->error;
556 }
557 }
558 if ($error) {
559 break;
560 }
561 }
562 }
563 }
564
565 if (!$error && !$notrigger) {
566 // Call trigger
567 $result = $this->call_trigger('ORDER_VALIDATE', $user);
568 if ($result < 0) {
569 $error++;
570 }
571 // End call triggers
572 }
573
574 if (!$error) {
575 $this->oldref = $this->ref;
576
577 // Rename directory if dir was a temporary ref
578 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
579 // Now we rename also files into index
580 $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)."'";
581 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
582 $resql = $this->db->query($sql);
583 if (!$resql) {
584 $error++; $this->error = $this->db->lasterror();
585 }
586 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'commande/".$this->db->escape($this->newref)."'";
587 $sql .= " WHERE filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
588 $resql = $this->db->query($sql);
589 if (!$resql) {
590 $error++; $this->error = $this->db->lasterror();
591 }
592
593 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
594 $oldref = dol_sanitizeFileName($this->ref);
595 $newref = dol_sanitizeFileName($num);
596 $dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
597 $dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
598 if (!$error && file_exists($dirsource)) {
599 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
600
601 if (@rename($dirsource, $dirdest)) {
602 dol_syslog("Rename ok");
603 // Rename docs starting with $oldref with $newref
604 $listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
605 foreach ($listoffiles as $fileentry) {
606 $dirsource = $fileentry['name'];
607 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
608 $dirsource = $fileentry['path'].'/'.$dirsource;
609 $dirdest = $fileentry['path'].'/'.$dirdest;
610 @rename($dirsource, $dirdest);
611 }
612 }
613 }
614 }
615 }
616
617 // Set new ref and current status
618 if (!$error) {
619 $this->ref = $num;
621 $this->brouillon = 0;
622 }
623
624 if (!$error) {
625 $this->db->commit();
626 return 1;
627 } else {
628 $this->db->rollback();
629 return -1;
630 }
631 }
632
633 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
641 public function setDraft($user, $idwarehouse = -1)
642 {
643 //phpcs:enable
644 global $conf, $langs;
645
646 $error = 0;
647
648 // Protection
649 if ($this->statut <= self::STATUS_DRAFT) {
650 return 0;
651 }
652
653 if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
654 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate)))) {
655 $this->error = 'Permission denied';
656 return -1;
657 }
658
659 dol_syslog(__METHOD__, LOG_DEBUG);
660
661 $this->db->begin();
662
663 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
664 $sql .= " SET fk_statut = ".self::STATUS_DRAFT.",";
665 $sql .= " fk_user_modif = ".((int) $user->id);
666 $sql .= " WHERE rowid = ".((int) $this->id);
667
668 if ($this->db->query($sql)) {
669 if (!$error) {
670 $this->oldcopy = clone $this;
671 }
672
673 // If stock is decremented on validate order, we must reincrement it
674 if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
675 $result = 0;
676
677 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
678 $langs->load("agenda");
679
680 $num = count($this->lines);
681 for ($i = 0; $i < $num; $i++) {
682 if ($this->lines[$i]->fk_product > 0) {
683 $mouvP = new MouvementStock($this->db);
684 $mouvP->origin = &$this;
685 $mouvP->setOrigin($this->element, $this->id);
686 // We increment stock of product (and sub-products)
687 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
688 if ($result < 0) {
689 $error++; $this->error = $mouvP->error; break;
690 }
691 }
692 }
693 }
694
695 if (!$error) {
696 // Call trigger
697 $result = $this->call_trigger('ORDER_UNVALIDATE', $user);
698 if ($result < 0) {
699 $error++;
700 }
701 }
702
703 if (!$error) {
704 $this->statut = self::STATUS_DRAFT;
705 $this->db->commit();
706 return 1;
707 } else {
708 $this->db->rollback();
709 return -1;
710 }
711 } else {
712 $this->error = $this->db->error();
713 $this->db->rollback();
714 return -1;
715 }
716 }
717
718
719 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
727 public function set_reopen($user)
728 {
729 // phpcs:enable
730 $error = 0;
731
732 if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) {
733 dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
734 return 0;
735 }
736
737 $this->db->begin();
738
739 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
740 $sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0,';
741 $sql .= " fk_user_modif = ".((int) $user->id);
742 $sql .= " WHERE rowid = ".((int) $this->id);
743
744 dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
745 $resql = $this->db->query($sql);
746 if ($resql) {
747 // Call trigger
748 $result = $this->call_trigger('ORDER_REOPEN', $user);
749 if ($result < 0) {
750 $error++;
751 }
752 // End call triggers
753 } else {
754 $error++;
755 $this->error = $this->db->lasterror();
756 dol_print_error($this->db);
757 }
758
759 if (!$error) {
761 $this->billed = 0;
762
763 $this->db->commit();
764 return 1;
765 } else {
766 foreach ($this->errors as $errmsg) {
767 dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
768 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
769 }
770 $this->db->rollback();
771 return -1 * $error;
772 }
773 }
774
782 public function cloture($user, $notrigger = 0)
783 {
784 global $conf;
785
786 $error = 0;
787
788 $usercanclose = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
789 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->close)));
790
791 if ($usercanclose) {
792 if ($this->statut == self::STATUS_CLOSED) {
793 return 0;
794 }
795 $this->db->begin();
796
797 $now = dol_now();
798
799 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
800 $sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
801 $sql .= ' fk_user_cloture = '.((int) $user->id).',';
802 $sql .= " date_cloture = '".$this->db->idate($now)."',";
803 $sql .= " fk_user_modif = ".((int) $user->id);
804 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
805
806 if ($this->db->query($sql)) {
807 if (!$notrigger) {
808 // Call trigger
809 $result = $this->call_trigger('ORDER_CLOSE', $user);
810 if ($result < 0) {
811 $error++;
812 }
813 // End call triggers
814 }
815
816 if (!$error) {
818
819 $this->db->commit();
820 return 1;
821 } else {
822 $this->db->rollback();
823 return -1;
824 }
825 } else {
826 $this->error = $this->db->lasterror();
827
828 $this->db->rollback();
829 return -1;
830 }
831 }
832 return 0;
833 }
834
842 public function cancel($idwarehouse = -1)
843 {
844 global $conf, $user, $langs;
845
846 $error = 0;
847
848 $this->db->begin();
849
850 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
851 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.",";
852 $sql .= " fk_user_modif = ".((int) $user->id);
853 $sql .= " WHERE rowid = ".((int) $this->id);
854 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
855
856 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
857 if ($this->db->query($sql)) {
858 // If stock is decremented on validate order, we must reincrement it
859 if (isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) {
860 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
861 $langs->load("agenda");
862
863 $num = count($this->lines);
864 for ($i = 0; $i < $num; $i++) {
865 if ($this->lines[$i]->fk_product > 0) {
866 $mouvP = new MouvementStock($this->db);
867 $mouvP->setOrigin($this->element, $this->id);
868 // We increment stock of product (and sub-products)
869 $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
870 if ($result < 0) {
871 $error++;
872 $this->error = $mouvP->error;
873 break;
874 }
875 }
876 }
877 }
878
879 if (!$error) {
880 // Call trigger
881 $result = $this->call_trigger('ORDER_CANCEL', $user);
882 if ($result < 0) {
883 $error++;
884 }
885 // End call triggers
886 }
887
888 if (!$error) {
890 $this->db->commit();
891 return 1;
892 } else {
893 foreach ($this->errors as $errmsg) {
894 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
895 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
896 }
897 $this->db->rollback();
898 return -1 * $error;
899 }
900 } else {
901 $this->error = $this->db->error();
902 $this->db->rollback();
903 return -1;
904 }
905 }
906
915 public function create($user, $notrigger = 0)
916 {
917 global $conf, $langs, $mysoc;
918 $error = 0;
919
920 // Clean parameters
921 $this->brouillon = 1; // set command as draft
922
923 // Set tmp vars
924 $date = ($this->date_commande ? $this->date_commande : $this->date);
925 $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
926
927 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
928 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
929 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
930 } else {
931 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
932 }
933 if (empty($this->fk_multicurrency)) {
934 $this->multicurrency_code = $conf->currency;
935 $this->fk_multicurrency = 0;
936 $this->multicurrency_tx = 1;
937 }
938
939 dol_syslog(get_class($this)."::create user=".$user->id);
940
941 // Check parameters
942 if (!empty($this->ref)) { // We check that ref is not already used
943 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
944 if ($result > 0) {
945 $this->error = 'ErrorRefAlreadyExists';
946 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
947 $this->db->rollback();
948 return -1;
949 }
950 }
951
952 $soc = new Societe($this->db);
953 $result = $soc->fetch($this->socid);
954 if ($result < 0) {
955 $this->error = "Failed to fetch company";
956 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
957 return -2;
958 }
959 if (!empty($conf->global->ORDER_REQUIRE_SOURCE) && $this->source < 0) {
960 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
961 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
962 return -1;
963 }
964
965 $now = dol_now();
966
967 $this->db->begin();
968
969 $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
970 $sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client";
971 $sql .= ", model_pdf, fk_cond_reglement, deposit_percent, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
972 $sql .= ", fk_shipping_method";
973 $sql .= ", fk_warehouse";
974 $sql .= ", remise_absolue, remise_percent";
975 $sql .= ", fk_incoterms, location_incoterms";
976 $sql .= ", entity, module_source, pos_source";
977 $sql .= ", fk_multicurrency";
978 $sql .= ", multicurrency_code";
979 $sql .= ", multicurrency_tx";
980 $sql .= ")";
981 $sql .= " VALUES ('(PROV)', ".((int) $this->socid).", '".$this->db->idate($now)."', ".((int) $user->id);
982 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
983 $sql .= ", '".$this->db->idate($date)."'";
984 $sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
985 $sql .= ", '".$this->db->escape($this->note_private)."'";
986 $sql .= ", '".$this->db->escape($this->note_public)."'";
987 $sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
988 $sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
989 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
990 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
991 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null");
992 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
993 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
994 $sql .= ", ".($this->availability_id > 0 ? ((int) $this->availability_id) : "null");
995 $sql .= ", ".($this->demand_reason_id > 0 ? ((int) $this->demand_reason_id) : "null");
996 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
997 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : 'NULL');
998 $sql .= ", ".(!empty($this->shipping_method_id) && $this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
999 $sql .= ", ".(!empty($this->warehouse_id) && $this->warehouse_id > 0 ? ((int) $this->warehouse_id) : 'NULL');
1000 $sql .= ", ".($this->remise_absolue > 0 ? $this->db->escape($this->remise_absolue) : 'NULL');
1001 $sql .= ", ".($this->remise_percent > 0 ? $this->db->escape($this->remise_percent) : 0); // TODO deprecated
1002 $sql .= ", ".(int) $this->fk_incoterms;
1003 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1004 $sql .= ", ".setEntity($this);
1005 $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
1006 $sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
1007 $sql .= ", ".(int) $this->fk_multicurrency;
1008 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1009 $sql .= ", ".(float) $this->multicurrency_tx;
1010 $sql .= ")";
1011
1012 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1013 $resql = $this->db->query($sql);
1014 if ($resql) {
1015 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
1016
1017 if ($this->id) {
1018 $fk_parent_line = 0;
1019 $num = count($this->lines);
1020
1021 /*
1022 * Insert products details into db
1023 */
1024 for ($i = 0; $i < $num; $i++) {
1025 $line = $this->lines[$i];
1026
1027 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
1028 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
1029 if (!is_object($line)) {
1030 $line = (object) $line;
1031 }
1032
1033 // Reset fk_parent_line for no child products and special product
1034 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1035 $fk_parent_line = 0;
1036 }
1037
1038 // Complete vat rate with code
1039 $vatrate = $line->tva_tx;
1040 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1041 $vatrate .= ' ('.$line->vat_src_code.')';
1042 }
1043
1044 if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1045 $originid = $line->origin_id;
1046 $origintype = $line->origin;
1047 } else {
1048 $originid = $line->id;
1049 $origintype = $this->element;
1050 }
1051
1052 // ref_ext
1053 if (empty($line->ref_ext)) {
1054 $line->ref_ext = '';
1055 }
1056
1057 $result = $this->addline(
1058 $line->desc,
1059 $line->subprice,
1060 $line->qty,
1061 $vatrate,
1062 $line->localtax1_tx,
1063 $line->localtax2_tx,
1064 $line->fk_product,
1065 $line->remise_percent,
1066 $line->info_bits,
1067 $line->fk_remise_except,
1068 'HT',
1069 0,
1070 $line->date_start,
1071 $line->date_end,
1072 $line->product_type,
1073 $line->rang,
1074 $line->special_code,
1075 $fk_parent_line,
1076 $line->fk_fournprice,
1077 $line->pa_ht,
1078 $line->label,
1079 $line->array_options,
1080 $line->fk_unit,
1081 $origintype,
1082 $originid,
1083 0,
1084 $line->ref_ext,
1085 1
1086 );
1087 if ($result < 0) {
1088 if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER) {
1089 $this->error = $this->db->lasterror();
1090 $this->errors[] = $this->error;
1091 dol_print_error($this->db);
1092 }
1093 $this->db->rollback();
1094 return -1;
1095 }
1096 // Defined the new fk_parent_line
1097 if ($result > 0 && $line->product_type == 9) {
1098 $fk_parent_line = $result;
1099 }
1100 }
1101
1102 $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.
1103
1104 // update ref
1105 $initialref = '(PROV'.$this->id.')';
1106 if (!empty($this->ref)) {
1107 $initialref = $this->ref;
1108 }
1109
1110 $sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".((int) $this->id);
1111 if ($this->db->query($sql)) {
1112 $this->ref = $initialref;
1113
1114 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1115 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1116 }
1117
1118 // Add object linked
1119 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1120 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1121 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, ...))
1122 foreach ($tmp_origin_id as $origin_id) {
1123 $ret = $this->add_object_linked($origin, $origin_id);
1124 if (!$ret) {
1125 $this->error = $this->db->lasterror();
1126 $error++;
1127 }
1128 }
1129 } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1130 {
1131 $origin_id = $tmp_origin_id;
1132 $ret = $this->add_object_linked($origin, $origin_id);
1133 if (!$ret) {
1134 $this->error = $this->db->lasterror();
1135 $error++;
1136 }
1137 }
1138 }
1139 }
1140
1141 if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1142 $originforcontact = $this->origin;
1143 $originidforcontact = $this->origin_id;
1144 if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1145 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1146 $exp = new Expedition($this->db);
1147 $exp->fetch($this->origin_id);
1148 $exp->fetchObjectLinked();
1149 if (count($exp->linkedObjectsIds['commande']) > 0) {
1150 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1151 $originforcontact = 'commande';
1152 if (is_object($value)) {
1153 $originidforcontact = $value->id;
1154 } else {
1155 $originidforcontact = $value;
1156 }
1157 break; // We take first one
1158 }
1159 }
1160 }
1161
1162 $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";
1163 $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1164
1165 $resqlcontact = $this->db->query($sqlcontact);
1166 if ($resqlcontact) {
1167 while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1168 //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1169 $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
1170 }
1171 } else {
1172 dol_print_error($resqlcontact);
1173 }
1174 }
1175
1176 if (!$error) {
1177 $result = $this->insertExtraFields();
1178 if ($result < 0) {
1179 $error++;
1180 }
1181 }
1182
1183 if (!$error && !$notrigger) {
1184 // Call trigger
1185 $result = $this->call_trigger('ORDER_CREATE', $user);
1186 if ($result < 0) {
1187 $error++;
1188 }
1189 // End call triggers
1190 }
1191
1192 if (!$error) {
1193 $this->db->commit();
1194 return $this->id;
1195 } else {
1196 $this->db->rollback();
1197 return -1 * $error;
1198 }
1199 } else {
1200 $this->error = $this->db->lasterror();
1201 $this->db->rollback();
1202 return -1;
1203 }
1204 }
1205
1206 return 0;
1207 } else {
1208 dol_print_error($this->db);
1209 $this->db->rollback();
1210 return -1;
1211 }
1212 }
1213
1214
1222 public function createFromClone(User $user, $socid = 0)
1223 {
1224 global $conf, $user, $hookmanager;
1225
1226 $error = 0;
1227
1228 $this->db->begin();
1229
1230 // get lines so they will be clone
1231 foreach ($this->lines as $line) {
1232 $line->fetch_optionals();
1233 }
1234
1235 // Load source object
1236 $objFrom = clone $this;
1237
1238 // Change socid if needed
1239 if (!empty($socid) && $socid != $this->socid) {
1240 $objsoc = new Societe($this->db);
1241
1242 if ($objsoc->fetch($socid) > 0) {
1243 $this->socid = $objsoc->id;
1244 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1245 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1246 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1247 $this->fk_project = 0;
1248 $this->fk_delivery_address = 0;
1249 }
1250
1251 // TODO Change product price if multi-prices
1252 }
1253
1254 $this->id = 0;
1255 $this->ref = '';
1256 $this->statut = self::STATUS_DRAFT;
1257
1258 // Clear fields
1259 $this->user_author_id = $user->id;
1260 $this->user_valid = 0; // deprecated
1261 $this->user_validation_id = 0;
1262 $this->date = dol_now();
1263 $this->date_commande = dol_now();
1264 $this->date_creation = '';
1265 $this->date_validation = '';
1266 if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) {
1267 $this->ref_client = '';
1268 $this->ref_customer = '';
1269 }
1270
1271 // Do not clone ref_ext
1272 $num = count($this->lines);
1273 for ($i = 0; $i < $num; $i++) {
1274 $this->lines[$i]->ref_ext = '';
1275 }
1276
1277 // Create clone
1278 $this->context['createfromclone'] = 'createfromclone';
1279 $result = $this->create($user);
1280 if ($result < 0) {
1281 $error++;
1282 }
1283
1284 if (!$error) {
1285 // copy internal contacts
1286 if ($this->copy_linked_contact($objFrom, 'internal') < 0) {
1287 $error++;
1288 }
1289 }
1290
1291 if (!$error) {
1292 // copy external contacts if same company
1293 if ($this->socid == $objFrom->socid) {
1294 if ($this->copy_linked_contact($objFrom, 'external') < 0) {
1295 $error++;
1296 }
1297 }
1298 }
1299
1300 if (!$error) {
1301 // Hook of thirdparty module
1302 if (is_object($hookmanager)) {
1303 $parameters = array('objFrom'=>$objFrom);
1304 $action = '';
1305 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1306 if ($reshook < 0) {
1307 $this->setErrorsFromObject($hookmanager);
1308 $error++;
1309 }
1310 }
1311 }
1312
1313 unset($this->context['createfromclone']);
1314
1315 // End
1316 if (!$error) {
1317 $this->db->commit();
1318 return $this->id;
1319 } else {
1320 $this->db->rollback();
1321 return -1;
1322 }
1323 }
1324
1325
1333 public function createFromProposal($object, User $user)
1334 {
1335 global $conf, $hookmanager;
1336
1337 dol_include_once('/multicurrency/class/multicurrency.class.php');
1338 dol_include_once('/core/class/extrafields.class.php');
1339
1340 $error = 0;
1341
1342
1343 $this->date_commande = dol_now();
1344 $this->source = 0;
1345
1346 $num = count($object->lines);
1347 for ($i = 0; $i < $num; $i++) {
1348 $line = new OrderLine($this->db);
1349
1350 $line->libelle = $object->lines[$i]->libelle;
1351 $line->label = $object->lines[$i]->label;
1352 $line->desc = $object->lines[$i]->desc;
1353 $line->price = $object->lines[$i]->price;
1354 $line->subprice = $object->lines[$i]->subprice;
1355 $line->vat_src_code = $object->lines[$i]->vat_src_code;
1356 $line->tva_tx = $object->lines[$i]->tva_tx;
1357 $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
1358 $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
1359 $line->qty = $object->lines[$i]->qty;
1360 $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
1361 $line->remise_percent = $object->lines[$i]->remise_percent;
1362 $line->fk_product = $object->lines[$i]->fk_product;
1363 $line->info_bits = $object->lines[$i]->info_bits;
1364 $line->product_type = $object->lines[$i]->product_type;
1365 $line->rang = $object->lines[$i]->rang;
1366 $line->special_code = $object->lines[$i]->special_code;
1367 $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
1368 $line->fk_unit = $object->lines[$i]->fk_unit;
1369
1370 $line->date_start = $object->lines[$i]->date_start;
1371 $line->date_end = $object->lines[$i]->date_end;
1372
1373 $line->fk_fournprice = $object->lines[$i]->fk_fournprice;
1374 $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);
1375 $line->pa_ht = $marginInfos[0];
1376 $line->marge_tx = $marginInfos[1];
1377 $line->marque_tx = $marginInfos[2];
1378
1379 $line->origin = $object->element;
1380 $line->origin_id = $object->lines[$i]->id;
1381
1382 // get extrafields from original line
1383 $object->lines[$i]->fetch_optionals();
1384 foreach ($object->lines[$i]->array_options as $options_key => $value) {
1385 $line->array_options[$options_key] = $value;
1386 }
1387
1388 $this->lines[$i] = $line;
1389 }
1390
1391 $this->entity = $object->entity;
1392 $this->socid = $object->socid;
1393 $this->fk_project = $object->fk_project;
1394 $this->cond_reglement_id = $object->cond_reglement_id;
1395 $this->deposit_percent = $object->deposit_percent;
1396 $this->mode_reglement_id = $object->mode_reglement_id;
1397 $this->fk_account = $object->fk_account;
1398 $this->availability_id = $object->availability_id;
1399 $this->demand_reason_id = $object->demand_reason_id;
1400 $this->date_livraison = $object->date_livraison; // deprecated
1401 $this->delivery_date = $object->date_livraison;
1402 $this->shipping_method_id = $object->shipping_method_id;
1403 $this->warehouse_id = $object->warehouse_id;
1404 $this->fk_delivery_address = $object->fk_delivery_address;
1405 $this->contact_id = $object->contact_id;
1406 $this->ref_client = $object->ref_client;
1407 $this->ref_customer = $object->ref_client;
1408
1409 if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) {
1410 $this->note_private = $object->note_private;
1411 $this->note_public = $object->note_public;
1412 }
1413
1414 $this->origin = $object->element;
1415 $this->origin_id = $object->id;
1416
1417 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1418 if (!empty($conf->multicurrency->enabled)) {
1419 if (!empty($object->multicurrency_code)) {
1420 $this->multicurrency_code = $object->multicurrency_code;
1421 }
1422 if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($object->multicurrency_tx)) {
1423 $this->multicurrency_tx = $object->multicurrency_tx;
1424 }
1425
1426 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1427 $tmparray = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date_commande);
1428 $this->fk_multicurrency = $tmparray[0];
1429 $this->multicurrency_tx = $tmparray[1];
1430 } else {
1431 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1432 }
1433 if (empty($this->fk_multicurrency)) {
1434 $this->multicurrency_code = $conf->currency;
1435 $this->fk_multicurrency = 0;
1436 $this->multicurrency_tx = 1;
1437 }
1438 }
1439
1440 // get extrafields from original line
1441 $object->fetch_optionals();
1442
1443 $e = new ExtraFields($this->db);
1444 $element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1445
1446 foreach ($object->array_options as $options_key => $value) {
1447 if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1448 $this->array_options[$options_key] = $value;
1449 }
1450 }
1451 // Possibility to add external linked objects with hooks
1452 $this->linked_objects[$this->origin] = $this->origin_id;
1453 if (isset($object->other_linked_objects) && is_array($object->other_linked_objects) && !empty($object->other_linked_objects)) {
1454 $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1455 }
1456
1457 $ret = $this->create($user);
1458
1459 if ($ret > 0) {
1460 // Actions hooked (by external module)
1461 $hookmanager->initHooks(array('orderdao'));
1462
1463 $parameters = array('objFrom'=>$object);
1464 $action = '';
1465 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1466 if ($reshook < 0) {
1467 $this->setErrorsFromObject($hookmanager);
1468 $error++;
1469 }
1470
1471 if (!$error) {
1472 // Validate immediatly the order
1473 if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL)) {
1474 $this->fetch($ret);
1475 $this->valid($user);
1476 }
1477 return $ret;
1478 } else {
1479 return -1;
1480 }
1481 } else {
1482 return -1;
1483 }
1484 }
1485
1486
1527 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 = 0, $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $ref_ext = '', $noupdateafterinsertline = 0)
1528 {
1529 global $mysoc, $conf, $langs, $user;
1530
1531 $logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1532 $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";
1533 $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";
1534 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1535
1536 if ($this->statut == self::STATUS_DRAFT) {
1537 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1538
1539 // Clean parameters
1540
1541 if (empty($remise_percent)) {
1542 $remise_percent = 0;
1543 }
1544 if (empty($qty)) {
1545 $qty = 0;
1546 }
1547 if (empty($info_bits)) {
1548 $info_bits = 0;
1549 }
1550 if (empty($rang)) {
1551 $rang = 0;
1552 }
1553 if (empty($txtva)) {
1554 $txtva = 0;
1555 }
1556 if (empty($txlocaltax1)) {
1557 $txlocaltax1 = 0;
1558 }
1559 if (empty($txlocaltax2)) {
1560 $txlocaltax2 = 0;
1561 }
1562 if (empty($fk_parent_line) || $fk_parent_line < 0) {
1563 $fk_parent_line = 0;
1564 }
1565 if (empty($this->fk_multicurrency)) {
1566 $this->fk_multicurrency = 0;
1567 }
1568 if (empty($ref_ext)) {
1569 $ref_ext = '';
1570 }
1571
1573 $qty = price2num($qty);
1574 $pu_ht = price2num($pu_ht);
1575 $pu_ht_devise = price2num($pu_ht_devise);
1576 $pu_ttc = price2num($pu_ttc);
1577 $pa_ht = price2num($pa_ht);
1578 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1579 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1580 }
1581 $txlocaltax1 = price2num($txlocaltax1);
1582 $txlocaltax2 = price2num($txlocaltax2);
1583 if ($price_base_type == 'HT') {
1584 $pu = $pu_ht;
1585 } else {
1586 $pu = $pu_ttc;
1587 }
1588 $label = trim($label);
1589 $desc = trim($desc);
1590
1591 // Check parameters
1592 if ($type < 0) {
1593 return -1;
1594 }
1595
1596 if ($date_start && $date_end && $date_start > $date_end) {
1597 $langs->load("errors");
1598 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1599 return -1;
1600 }
1601
1602 $this->db->begin();
1603
1604 $product_type = $type;
1605 if (!empty($fk_product) && $fk_product > 0) {
1606 $product = new Product($this->db);
1607 $result = $product->fetch($fk_product);
1608 $product_type = $product->type;
1609
1610 if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
1611 $langs->load("errors");
1612 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1613 $this->errors[] = $this->error;
1614 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1615 $this->db->rollback();
1617 }
1618 }
1619 // Calcul du total TTC et de la TVA pour la ligne a partir de
1620 // qty, pu, remise_percent et txtva
1621 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1622 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1623
1624 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1625
1626 // Clean vat code
1627 $reg = array();
1628 $vat_src_code = '';
1629 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1630 $vat_src_code = $reg[1];
1631 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1632 }
1633
1634 $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);
1635
1636 /*var_dump($txlocaltax1);
1637 var_dump($txlocaltax2);
1638 var_dump($localtaxes_type);
1639 var_dump($tabprice);
1640 var_dump($tabprice[9]);
1641 var_dump($tabprice[10]);
1642 exit;*/
1643
1644 $total_ht = $tabprice[0];
1645 $total_tva = $tabprice[1];
1646 $total_ttc = $tabprice[2];
1647 $total_localtax1 = $tabprice[9];
1648 $total_localtax2 = $tabprice[10];
1649 $pu_ht = $tabprice[3];
1650
1651 // MultiCurrency
1652 $multicurrency_total_ht = $tabprice[16];
1653 $multicurrency_total_tva = $tabprice[17];
1654 $multicurrency_total_ttc = $tabprice[18];
1655 $pu_ht_devise = $tabprice[19];
1656
1657 // Rang to use
1658 $ranktouse = $rang;
1659 if ($ranktouse == -1) {
1660 $rangmax = $this->line_max($fk_parent_line);
1661 $ranktouse = $rangmax + 1;
1662 }
1663
1664 // TODO A virer
1665 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1666 $price = $pu;
1667 $remise = 0;
1668 if ($remise_percent > 0) {
1669 $remise = round(($pu * $remise_percent / 100), 2);
1670 $price = $pu - $remise;
1671 }
1672
1673 // Insert line
1674 $this->line = new OrderLine($this->db);
1675
1676 $this->line->context = $this->context;
1677
1678 $this->line->fk_commande = $this->id;
1679 $this->line->label = $label;
1680 $this->line->desc = $desc;
1681 $this->line->qty = $qty;
1682 $this->line->ref_ext = $ref_ext;
1683
1684 $this->line->vat_src_code = $vat_src_code;
1685 $this->line->tva_tx = $txtva;
1686 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1687 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1688 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1689 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1690 $this->line->fk_product = $fk_product;
1691 $this->line->product_type = $product_type;
1692 $this->line->fk_remise_except = $fk_remise_except;
1693 $this->line->remise_percent = $remise_percent;
1694 $this->line->subprice = $pu_ht;
1695 $this->line->rang = $ranktouse;
1696 $this->line->info_bits = $info_bits;
1697 $this->line->total_ht = $total_ht;
1698 $this->line->total_tva = $total_tva;
1699 $this->line->total_localtax1 = $total_localtax1;
1700 $this->line->total_localtax2 = $total_localtax2;
1701 $this->line->total_ttc = $total_ttc;
1702 $this->line->special_code = $special_code;
1703 $this->line->origin = $origin;
1704 $this->line->origin_id = $origin_id;
1705 $this->line->fk_parent_line = $fk_parent_line;
1706 $this->line->fk_unit = $fk_unit;
1707
1708 $this->line->date_start = $date_start;
1709 $this->line->date_end = $date_end;
1710
1711 $this->line->fk_fournprice = $fk_fournprice;
1712 $this->line->pa_ht = $pa_ht;
1713
1714 // Multicurrency
1715 $this->line->fk_multicurrency = $this->fk_multicurrency;
1716 $this->line->multicurrency_code = $this->multicurrency_code;
1717 $this->line->multicurrency_subprice = $pu_ht_devise;
1718 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1719 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1720 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1721
1722 // TODO Ne plus utiliser
1723 $this->line->price = $price;
1724
1725 if (is_array($array_options) && count($array_options) > 0) {
1726 $this->line->array_options = $array_options;
1727 }
1728
1729 $result = $this->line->insert($user);
1730 if ($result > 0) {
1731 // Reorder if child line
1732 if (!empty($fk_parent_line)) {
1733 $this->line_order(true, 'DESC');
1734 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
1735 $linecount = count($this->lines);
1736 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1737 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1738 }
1739 }
1740
1741 // Mise a jour informations denormalisees au niveau de la commande meme
1742 if (empty($noupdateafterinsertline)) {
1743 $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.
1744 }
1745
1746 if ($result > 0) {
1747 $this->db->commit();
1748 return $this->line->id;
1749 } else {
1750 $this->db->rollback();
1751 return -1;
1752 }
1753 } else {
1754 $this->error = $this->line->error;
1755 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1756 $this->db->rollback();
1757 return -2;
1758 }
1759 } else {
1760 dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1761 return -3;
1762 }
1763 }
1764
1765
1766 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1780 public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1781 {
1782 // phpcs:enable
1783 global $conf, $mysoc;
1784
1785 if (!$qty) {
1786 $qty = 1;
1787 }
1788
1789 if ($idproduct > 0) {
1790 $prod = new Product($this->db);
1791 $prod->fetch($idproduct);
1792
1793 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1794 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1795 if (empty($tva_tx)) {
1796 $tva_npr = 0;
1797 }
1798 $vat_src_code = ''; // May be defined into tva_tx
1799
1800 $localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1801 $localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1802
1803 // multiprix
1804 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1805 $price = $prod->multiprices[$this->thirdparty->price_level];
1806 } else {
1807 $price = $prod->price;
1808 }
1809
1810 $line = new OrderLine($this->db);
1811
1812 $line->context = $this->context;
1813
1814 $line->fk_product = $idproduct;
1815 $line->desc = $prod->description;
1816 $line->qty = $qty;
1817 $line->subprice = $price;
1818 $line->remise_percent = $remise_percent;
1819 $line->vat_src_code = $vat_src_code;
1820 $line->tva_tx = $tva_tx;
1821 $line->localtax1_tx = $localtax1_tx;
1822 $line->localtax2_tx = $localtax2_tx;
1823 $line->ref = $prod->ref;
1824 $line->libelle = $prod->label;
1825 $line->product_desc = $prod->description;
1826 $line->fk_unit = $prod->fk_unit;
1827
1828 // Save the start and end date of the line in the object
1829 if ($date_start) {
1830 $line->date_start = $date_start;
1831 }
1832 if ($date_end) {
1833 $line->date_end = $date_end;
1834 }
1835
1836 $this->lines[] = $line;
1837
1856 }
1857 }
1858
1859
1869 public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1870 {
1871 // Check parameters
1872 if (empty($id) && empty($ref) && empty($ref_ext)) {
1873 return -1;
1874 }
1875
1876 $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';
1877 $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';
1878 $sql .= ', c.fk_account';
1879 $sql .= ', c.date_commande, c.date_valid, c.tms';
1880 $sql .= ', c.date_livraison as delivery_date';
1881 $sql .= ', c.fk_shipping_method';
1882 $sql .= ', c.fk_warehouse';
1883 $sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1884 $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';
1885 $sql .= ', c.fk_incoterms, c.location_incoterms';
1886 $sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1887 $sql .= ", c.module_source, c.pos_source";
1888 $sql .= ", i.libelle as label_incoterms";
1889 $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1890 $sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1891 $sql .= ', ca.code as availability_code, ca.label as availability_label';
1892 $sql .= ', dr.code as demand_reason_code';
1893 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1894 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1895 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1896 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1897 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1898 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1899
1900 if ($id) {
1901 $sql .= " WHERE c.rowid=".((int) $id);
1902 } else {
1903 $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1904 }
1905
1906 if ($ref) {
1907 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1908 }
1909 if ($ref_ext) {
1910 $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1911 }
1912
1913 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1914 $result = $this->db->query($sql);
1915 if ($result) {
1916 $obj = $this->db->fetch_object($result);
1917 if ($obj) {
1918 $this->id = $obj->rowid;
1919 $this->entity = $obj->entity;
1920
1921 $this->ref = $obj->ref;
1922 $this->ref_client = $obj->ref_client;
1923 $this->ref_customer = $obj->ref_client;
1924 $this->ref_ext = $obj->ref_ext;
1925
1926 $this->socid = $obj->fk_soc;
1927 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1928
1929 $this->fk_project = $obj->fk_project;
1930 $this->project = null; // Clear if another value was already set by fetch_projet
1931
1932 $this->statut = $obj->fk_statut;
1933 $this->status = $obj->fk_statut;
1934
1935 $this->user_author_id = $obj->fk_user_author;
1936 $this->user_creation_id = $obj->fk_user_author;
1937 $this->user_validation_id = $obj->fk_user_valid;
1938 $this->user_valid = $obj->fk_user_valid; // deprecated
1939 $this->user_modification_id = $obj->fk_user_modif;
1940 $this->user_modification = $obj->fk_user_modif;
1941 $this->total_ht = $obj->total_ht;
1942 $this->total_tva = $obj->total_tva;
1943 $this->total_localtax1 = $obj->total_localtax1;
1944 $this->total_localtax2 = $obj->total_localtax2;
1945 $this->total_ttc = $obj->total_ttc;
1946 $this->date = $this->db->jdate($obj->date_commande);
1947 $this->date_commande = $this->db->jdate($obj->date_commande);
1948 $this->date_creation = $this->db->jdate($obj->date_creation);
1949 $this->date_validation = $this->db->jdate($obj->date_valid);
1950 $this->date_modification = $this->db->jdate($obj->tms);
1951 $this->remise = $obj->remise;
1952 $this->remise_percent = $obj->remise_percent; // TODO deprecated
1953 $this->remise_absolue = $obj->remise_absolue;
1954 $this->source = $obj->source;
1955 $this->billed = $obj->billed;
1956 $this->note = $obj->note_private; // deprecated
1957 $this->note_private = $obj->note_private;
1958 $this->note_public = $obj->note_public;
1959 $this->model_pdf = $obj->model_pdf;
1960 $this->modelpdf = $obj->model_pdf; // deprecated
1961 $this->last_main_doc = $obj->last_main_doc;
1962 $this->mode_reglement_id = $obj->fk_mode_reglement;
1963 $this->mode_reglement_code = $obj->mode_reglement_code;
1964 $this->mode_reglement = $obj->mode_reglement_libelle;
1965 $this->cond_reglement_id = $obj->fk_cond_reglement;
1966 $this->cond_reglement_code = $obj->cond_reglement_code;
1967 $this->cond_reglement = $obj->cond_reglement_libelle;
1968 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1969 $this->deposit_percent = $obj->deposit_percent;
1970 $this->fk_account = $obj->fk_account;
1971 $this->availability_id = $obj->fk_availability;
1972 $this->availability_code = $obj->availability_code;
1973 $this->availability = $obj->availability_label;
1974 $this->demand_reason_id = $obj->fk_input_reason;
1975 $this->demand_reason_code = $obj->demand_reason_code;
1976 $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1977 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1978 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1979 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1980 $this->fk_delivery_address = $obj->fk_delivery_address;
1981 $this->module_source = $obj->module_source;
1982 $this->pos_source = $obj->pos_source;
1983
1984 //Incoterms
1985 $this->fk_incoterms = $obj->fk_incoterms;
1986 $this->location_incoterms = $obj->location_incoterms;
1987 $this->label_incoterms = $obj->label_incoterms;
1988
1989 // Multicurrency
1990 $this->fk_multicurrency = $obj->fk_multicurrency;
1991 $this->multicurrency_code = $obj->multicurrency_code;
1992 $this->multicurrency_tx = $obj->multicurrency_tx;
1993 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1994 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1995 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1996
1997 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1998
1999 $this->lines = array();
2000
2001 if ($this->statut == self::STATUS_DRAFT) {
2002 $this->brouillon = 1;
2003 }
2004
2005 // Retrieve all extrafield
2006 // fetch optionals attributes and labels
2007 $this->fetch_optionals();
2008
2009 $this->db->free($result);
2010
2011 // Lines
2012 $result = $this->fetch_lines();
2013 if ($result < 0) {
2014 return -3;
2015 }
2016 return 1;
2017 } else {
2018 $this->error = 'Order with id '.$id.' not found sql='.$sql;
2019 return 0;
2020 }
2021 } else {
2022 $this->error = $this->db->error();
2023 return -1;
2024 }
2025 }
2026
2027
2028 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2035 public function insert_discount($idremise)
2036 {
2037 // phpcs:enable
2038 global $langs;
2039
2040 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2041 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
2042
2043 $this->db->begin();
2044
2045 $remise = new DiscountAbsolute($this->db);
2046 $result = $remise->fetch($idremise);
2047
2048 if ($result > 0) {
2049 if ($remise->fk_facture) { // Protection against multiple submission
2050 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
2051 $this->db->rollback();
2052 return -5;
2053 }
2054
2055 $line = new OrderLine($this->db);
2056
2057 $line->fk_commande = $this->id;
2058 $line->fk_remise_except = $remise->id;
2059 $line->desc = $remise->description; // Description ligne
2060 $line->vat_src_code = $remise->vat_src_code;
2061 $line->tva_tx = $remise->tva_tx;
2062 $line->subprice = -$remise->amount_ht;
2063 $line->price = -$remise->amount_ht;
2064 $line->fk_product = 0; // Id produit predefini
2065 $line->qty = 1;
2066 $line->remise_percent = 0;
2067 $line->rang = -1;
2068 $line->info_bits = 2;
2069
2070 $line->total_ht = -$remise->amount_ht;
2071 $line->total_tva = -$remise->amount_tva;
2072 $line->total_ttc = -$remise->amount_ttc;
2073
2074 $result = $line->insert();
2075 if ($result > 0) {
2076 $result = $this->update_price(1);
2077 if ($result > 0) {
2078 $this->db->commit();
2079 return 1;
2080 } else {
2081 $this->db->rollback();
2082 return -1;
2083 }
2084 } else {
2085 $this->error = $line->error;
2086 $this->errors = $line->errors;
2087 $this->db->rollback();
2088 return -2;
2089 }
2090 } else {
2091 $this->db->rollback();
2092 return -2;
2093 }
2094 }
2095
2096
2097 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2105 public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
2106 {
2107 // phpcs:enable
2108 global $langs, $conf;
2109
2110 $this->lines = array();
2111
2112 $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,';
2113 $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,';
2114 $sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
2115 $sql .= ' l.fk_unit,';
2116 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
2117 $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,';
2118 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
2119 $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
2120 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
2121 $sql .= ' WHERE l.fk_commande = '.((int) $this->id);
2122 if ($only_product) {
2123 $sql .= ' AND p.fk_product_type = 0';
2124 }
2125 $sql .= ' ORDER BY l.rang, l.rowid';
2126
2127 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
2128 $result = $this->db->query($sql);
2129 if ($result) {
2130 $num = $this->db->num_rows($result);
2131
2132 $i = 0;
2133 while ($i < $num) {
2134 $objp = $this->db->fetch_object($result);
2135
2136 $line = new OrderLine($this->db);
2137
2138 $line->rowid = $objp->rowid;
2139 $line->id = $objp->rowid;
2140 $line->fk_commande = $objp->fk_commande;
2141 $line->commande_id = $objp->fk_commande;
2142 $line->label = $objp->custom_label;
2143 $line->desc = $objp->description;
2144 $line->description = $objp->description; // Description line
2145 $line->product_type = $objp->product_type;
2146 $line->qty = $objp->qty;
2147 $line->ref_ext = $objp->ref_ext;
2148
2149 $line->vat_src_code = $objp->vat_src_code;
2150 $line->tva_tx = $objp->tva_tx;
2151 $line->localtax1_tx = $objp->localtax1_tx;
2152 $line->localtax2_tx = $objp->localtax2_tx;
2153 $line->localtax1_type = $objp->localtax1_type;
2154 $line->localtax2_type = $objp->localtax2_type;
2155 $line->total_ht = $objp->total_ht;
2156 $line->total_ttc = $objp->total_ttc;
2157 $line->total_tva = $objp->total_tva;
2158 $line->total_localtax1 = $objp->total_localtax1;
2159 $line->total_localtax2 = $objp->total_localtax2;
2160 $line->subprice = $objp->subprice;
2161 $line->fk_remise_except = $objp->fk_remise_except;
2162 $line->remise_percent = $objp->remise_percent;
2163 $line->price = $objp->price;
2164 $line->fk_product = $objp->fk_product;
2165 $line->fk_fournprice = $objp->fk_fournprice;
2166 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2167 $line->pa_ht = $marginInfos[0];
2168 $line->marge_tx = $marginInfos[1];
2169 $line->marque_tx = $marginInfos[2];
2170 $line->rang = $objp->rang;
2171 $line->info_bits = $objp->info_bits;
2172 $line->special_code = $objp->special_code;
2173 $line->fk_parent_line = $objp->fk_parent_line;
2174
2175 $line->ref = $objp->product_ref;
2176 $line->libelle = $objp->product_label;
2177
2178 $line->product_ref = $objp->product_ref;
2179 $line->product_label = $objp->product_label;
2180 $line->product_tosell = $objp->product_tosell;
2181 $line->product_tobuy = $objp->product_tobuy;
2182 $line->product_desc = $objp->product_desc;
2183 $line->product_tobatch = $objp->product_tobatch;
2184 $line->product_barcode = $objp->product_barcode;
2185
2186 $line->fk_product_type = $objp->fk_product_type; // Produit ou service
2187 $line->fk_unit = $objp->fk_unit;
2188
2189 $line->weight = $objp->weight;
2190 $line->weight_units = $objp->weight_units;
2191 $line->volume = $objp->volume;
2192 $line->volume_units = $objp->volume_units;
2193
2194 $line->date_start = $this->db->jdate($objp->date_start);
2195 $line->date_end = $this->db->jdate($objp->date_end);
2196
2197 // Multicurrency
2198 $line->fk_multicurrency = $objp->fk_multicurrency;
2199 $line->multicurrency_code = $objp->multicurrency_code;
2200 $line->multicurrency_subprice = $objp->multicurrency_subprice;
2201 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
2202 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
2203 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2204
2205 $line->fetch_optionals();
2206
2207 // multilangs
2208 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2209 $tmpproduct = new Product($this->db);
2210 $tmpproduct->fetch($objp->fk_product);
2211 $tmpproduct->getMultiLangs();
2212
2213 $line->multilangs = $tmpproduct->multilangs;
2214 }
2215
2216 $this->lines[$i] = $line;
2217
2218 $i++;
2219 }
2220
2221 $this->db->free($result);
2222
2223 return 1;
2224 } else {
2225 $this->error = $this->db->error();
2226 return -3;
2227 }
2228 }
2229
2230
2236 public function getNbOfProductsLines()
2237 {
2238 $nb = 0;
2239 foreach ($this->lines as $line) {
2240 if ($line->product_type == 0) {
2241 $nb++;
2242 }
2243 }
2244 return $nb;
2245 }
2246
2252 public function getNbOfServicesLines()
2253 {
2254 $nb = 0;
2255 foreach ($this->lines as $line) {
2256 if ($line->product_type == 1) {
2257 $nb++;
2258 }
2259 }
2260 return $nb;
2261 }
2262
2268 public function getNbOfShipments()
2269 {
2270 $nb = 0;
2271
2272 $sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2273 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2274 $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2275 $sql .= ' WHERE';
2276 $sql .= ' ed.fk_origin_line = cd.rowid';
2277 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2278 //print $sql;
2279
2280 dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2281 $resql = $this->db->query($sql);
2282 if ($resql) {
2283 $obj = $this->db->fetch_object($resql);
2284 if ($obj) {
2285 $nb = $obj->nb;
2286 }
2287
2288 $this->db->free($resql);
2289 return $nb;
2290 } else {
2291 $this->error = $this->db->lasterror();
2292 return -1;
2293 }
2294 }
2295
2304 public function loadExpeditions($filtre_statut = -1, $fk_product = 0)
2305 {
2306 $this->expeditions = array();
2307
2308 $sql = 'SELECT cd.rowid, cd.fk_product,';
2309 $sql .= ' sum(ed.qty) as qty';
2310 $sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2311 if ($filtre_statut >= 0) {
2312 $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2313 }
2314 $sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2315 $sql .= ' WHERE';
2316 if ($filtre_statut >= 0) {
2317 $sql .= ' ed.fk_expedition = e.rowid AND';
2318 }
2319 $sql .= ' ed.fk_origin_line = cd.rowid';
2320 $sql .= ' AND cd.fk_commande = '.((int) $this->id);
2321 if ($fk_product > 0) {
2322 $sql .= ' AND cd.fk_product = '.((int) $fk_product);
2323 }
2324 if ($filtre_statut >= 0) {
2325 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
2326 }
2327 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
2328 //print $sql;
2329
2330 dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2331 $resql = $this->db->query($sql);
2332 if ($resql) {
2333 $num = $this->db->num_rows($resql);
2334 $i = 0;
2335 while ($i < $num) {
2336 $obj = $this->db->fetch_object($resql);
2337 $this->expeditions[$obj->rowid] = $obj->qty;
2338 $i++;
2339 }
2340 $this->db->free($resql);
2341 return $num;
2342 } else {
2343 $this->error = $this->db->lasterror();
2344 return -1;
2345 }
2346 }
2347
2353 public function countNbOfShipments()
2354 {
2355 $sql = 'SELECT count(*)';
2356 $sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2357 $sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2358 $sql .= ' WHERE el.fk_source = '.((int) $this->id);
2359 $sql .= " AND el.sourcetype = 'commande'";
2360 $sql .= " AND el.fk_target = e.rowid";
2361 $sql .= " AND el.targettype = 'shipping'";
2362
2363 $resql = $this->db->query($sql);
2364 if ($resql) {
2365 $row = $this->db->fetch_row($resql);
2366 return $row[0];
2367 } else {
2368 dol_print_error($this->db);
2369 }
2370
2371 return 0;
2372 }
2373
2374 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2383 public function stock_array($filtre_statut = self::STATUS_CANCELED)
2384 {
2385 // phpcs:enable
2386 $this->stocks = array();
2387
2388 // Tableau des id de produit de la commande
2389 $array_of_product = array();
2390
2391 // Recherche total en stock pour chaque produit
2392 // TODO $array_of_product est défini vide juste au dessus !!
2393 if (count($array_of_product)) {
2394 $sql = "SELECT fk_product, sum(ps.reel) as total";
2395 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2396 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize(join(',', $array_of_product)).")";
2397 $sql .= ' GROUP BY fk_product';
2398 $resql = $this->db->query($sql);
2399 if ($resql) {
2400 $num = $this->db->num_rows($resql);
2401 $i = 0;
2402 while ($i < $num) {
2403 $obj = $this->db->fetch_object($resql);
2404 $this->stocks[$obj->fk_product] = $obj->total;
2405 $i++;
2406 }
2407 $this->db->free($resql);
2408 }
2409 }
2410 return 0;
2411 }
2412
2421 public function deleteline($user = null, $lineid = 0, $id = 0)
2422 {
2423 if ($this->statut == self::STATUS_DRAFT) {
2424 $this->db->begin();
2425
2426 // Delete line
2427 $line = new OrderLine($this->db);
2428
2429 $line->context = $this->context;
2430
2431 // Load data
2432 $line->fetch($lineid);
2433
2434 if ($id > 0 && $line->fk_commande != $id) {
2435 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2436 return -1;
2437 }
2438
2439 // Memorize previous line for triggers
2440 $staticline = clone $line;
2441 $line->oldline = $staticline;
2442
2443 if ($line->delete($user) > 0) {
2444 $result = $this->update_price(1);
2445
2446 if ($result > 0) {
2447 $this->db->commit();
2448 return 1;
2449 } else {
2450 $this->db->rollback();
2451 $this->error = $this->db->lasterror();
2452 return -1;
2453 }
2454 } else {
2455 $this->db->rollback();
2456 $this->error = $line->error;
2457 return -1;
2458 }
2459 } else {
2460 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2461 return -1;
2462 }
2463 }
2464
2465 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2476 public function set_remise($user, $remise, $notrigger = 0)
2477 {
2478 // phpcs:enable
2479 dol_syslog(get_class($this)."::set_remise is deprecated, use setDiscount instead", LOG_NOTICE);
2480 return $this->setDiscount($user, $remise, $notrigger);
2481 }
2482
2492 public function setDiscount($user, $remise, $notrigger = 0)
2493 {
2494 $remise = trim($remise) ?trim($remise) : 0;
2495
2496 if ($user->hasRight('commande', 'creer')) {
2497 $error = 0;
2498
2499 $this->db->begin();
2500
2501 $remise = price2num($remise, 2);
2502
2503 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2504 $sql .= ' SET remise_percent = '.((float) $remise);
2505 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_DRAFT);
2506
2507 dol_syslog(__METHOD__, LOG_DEBUG);
2508 $resql = $this->db->query($sql);
2509 if (!$resql) {
2510 $this->errors[] = $this->db->error();
2511 $error++;
2512 }
2513
2514 if (!$error) {
2515 $this->oldcopy = clone $this;
2516 $this->remise_percent = $remise;
2517 $this->update_price(1);
2518 }
2519
2520 if (!$notrigger && empty($error)) {
2521 // Call trigger
2522 $result = $this->call_trigger('ORDER_MODIFY', $user);
2523 if ($result < 0) {
2524 $error++;
2525 }
2526 // End call triggers
2527 }
2528
2529 if (!$error) {
2530 $this->db->commit();
2531 return 1;
2532 } else {
2533 foreach ($this->errors as $errmsg) {
2534 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2535 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2536 }
2537 $this->db->rollback();
2538 return -1 * $error;
2539 }
2540 }
2541
2542 return 0;
2543 }
2544
2545
2546 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2555 public function set_remise_absolue($user, $remise, $notrigger = 0)
2556 {
2557 // phpcs:enable
2558 if (empty($remise)) {
2559 $remise = 0;
2560 }
2561
2562 $remise = price2num($remise);
2563
2564 if ($user->hasRight('commande', 'creer')) {
2565 $error = 0;
2566
2567 $this->db->begin();
2568
2569 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2570 $sql .= ' SET remise_absolue = '.((float) $remise);
2571 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.self::STATUS_DRAFT;
2572
2573 dol_syslog(__METHOD__, LOG_DEBUG);
2574 $resql = $this->db->query($sql);
2575 if (!$resql) {
2576 $this->errors[] = $this->db->error();
2577 $error++;
2578 }
2579
2580 if (!$error) {
2581 $this->oldcopy = clone $this;
2582 $this->remise_absolue = $remise;
2583 $this->update_price(1);
2584 }
2585
2586 if (!$notrigger && empty($error)) {
2587 // Call trigger
2588 $result = $this->call_trigger('ORDER_MODIFY', $user);
2589 if ($result < 0) {
2590 $error++;
2591 }
2592 // End call triggers
2593 }
2594
2595 if (!$error) {
2596 $this->db->commit();
2597 return 1;
2598 } else {
2599 foreach ($this->errors as $errmsg) {
2600 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2601 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2602 }
2603 $this->db->rollback();
2604 return -1 * $error;
2605 }
2606 }
2607
2608 return 0;
2609 }
2610
2611
2612 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2621 public function set_date($user, $date, $notrigger = 0)
2622 {
2623 // phpcs:enable
2624 if ($user->hasRight('commande', 'creer')) {
2625 $error = 0;
2626
2627 $this->db->begin();
2628
2629 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2630 $sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2631 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".((int) self::STATUS_DRAFT);
2632
2633 dol_syslog(__METHOD__, LOG_DEBUG);
2634 $resql = $this->db->query($sql);
2635 if (!$resql) {
2636 $this->errors[] = $this->db->error();
2637 $error++;
2638 }
2639
2640 if (!$error) {
2641 $this->oldcopy = clone $this;
2642 $this->date = $date;
2643 }
2644
2645 if (!$notrigger && empty($error)) {
2646 // Call trigger
2647 $result = $this->call_trigger('ORDER_MODIFY', $user);
2648 if ($result < 0) {
2649 $error++;
2650 }
2651 // End call triggers
2652 }
2653
2654 if (!$error) {
2655 $this->db->commit();
2656 return 1;
2657 } else {
2658 foreach ($this->errors as $errmsg) {
2659 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2660 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2661 }
2662 $this->db->rollback();
2663 return -1 * $error;
2664 }
2665 } else {
2666 return -2;
2667 }
2668 }
2669
2670 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2680 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2681 {
2682 // phpcs:enable
2683 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2684 }
2685
2694 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2695 {
2696 if ($user->hasRight('commande', 'creer')) {
2697 $error = 0;
2698
2699 $this->db->begin();
2700
2701 $sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2702 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2703 $sql .= " WHERE rowid = ".((int) $this->id);
2704
2705 dol_syslog(__METHOD__, LOG_DEBUG);
2706 $resql = $this->db->query($sql);
2707 if (!$resql) {
2708 $this->errors[] = $this->db->error();
2709 $error++;
2710 }
2711
2712 if (!$error) {
2713 $this->oldcopy = clone $this;
2714 $this->date_livraison = $delivery_date;
2715 $this->delivery_date = $delivery_date;
2716 }
2717
2718 if (!$notrigger && empty($error)) {
2719 // Call trigger
2720 $result = $this->call_trigger('ORDER_MODIFY', $user);
2721 if ($result < 0) {
2722 $error++;
2723 }
2724 // End call triggers
2725 }
2726
2727 if (!$error) {
2728 $this->db->commit();
2729 return 1;
2730 } else {
2731 foreach ($this->errors as $errmsg) {
2732 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2733 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2734 }
2735 $this->db->rollback();
2736 return -1 * $error;
2737 }
2738 } else {
2739 return -2;
2740 }
2741 }
2742
2743 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2757 public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2758 {
2759 // phpcs:enable
2760 global $user;
2761
2762 $ga = array();
2763
2764 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2765 $sql .= " c.rowid as cid, c.ref";
2766 if (empty($user->rights->societe->client->voir) && !$socid) {
2767 $sql .= ", sc.fk_soc, sc.fk_user";
2768 }
2769 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2770 if (empty($user->rights->societe->client->voir) && !$socid) {
2771 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2772 }
2773 $sql .= " WHERE c.entity IN (".getEntity('commande').")";
2774 $sql .= " AND c.fk_soc = s.rowid";
2775 if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2776 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2777 }
2778 if ($socid) {
2779 $sql .= " AND s.rowid = ".((int) $socid);
2780 }
2781 if ($draft) {
2782 $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2783 }
2784 if (is_object($excluser)) {
2785 $sql .= " AND c.fk_user_author <> ".((int) $excluser->id);
2786 }
2787 $sql .= $this->db->order($sortfield, $sortorder);
2788 $sql .= $this->db->plimit($limit, $offset);
2789
2790 $result = $this->db->query($sql);
2791 if ($result) {
2792 $numc = $this->db->num_rows($result);
2793 if ($numc) {
2794 $i = 0;
2795 while ($i < $numc) {
2796 $obj = $this->db->fetch_object($result);
2797
2798 if ($shortlist == 1) {
2799 $ga[$obj->cid] = $obj->ref;
2800 } elseif ($shortlist == 2) {
2801 $ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2802 } else {
2803 $ga[$i]['id'] = $obj->cid;
2804 $ga[$i]['ref'] = $obj->ref;
2805 $ga[$i]['name'] = $obj->name;
2806 }
2807 $i++;
2808 }
2809 }
2810 return $ga;
2811 } else {
2812 dol_print_error($this->db);
2813 return -1;
2814 }
2815 }
2816
2824 public function availability($availability_id, $notrigger = 0)
2825 {
2826 global $user;
2827
2828 dol_syslog('Commande::availability('.$availability_id.')');
2829 if ($this->statut >= self::STATUS_DRAFT) {
2830 $error = 0;
2831
2832 $this->db->begin();
2833
2834 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2835 $sql .= ' SET fk_availability = '.((int) $availability_id);
2836 $sql .= ' WHERE rowid='.((int) $this->id);
2837
2838 dol_syslog(__METHOD__, LOG_DEBUG);
2839 $resql = $this->db->query($sql);
2840 if (!$resql) {
2841 $this->errors[] = $this->db->error();
2842 $error++;
2843 }
2844
2845 if (!$error) {
2846 $this->oldcopy = clone $this;
2847 $this->availability_id = $availability_id;
2848 }
2849
2850 if (!$notrigger && empty($error)) {
2851 // Call trigger
2852 $result = $this->call_trigger('ORDER_MODIFY', $user);
2853 if ($result < 0) {
2854 $error++;
2855 }
2856 // End call triggers
2857 }
2858
2859 if (!$error) {
2860 $this->db->commit();
2861 return 1;
2862 } else {
2863 foreach ($this->errors as $errmsg) {
2864 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2865 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2866 }
2867 $this->db->rollback();
2868 return -1 * $error;
2869 }
2870 } else {
2871 $error_str = 'Command status do not meet requirement '.$this->statut;
2872 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2873 $this->error = $error_str;
2874 $this->errors[] = $this->error;
2875 return -2;
2876 }
2877 }
2878
2879 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2887 public function demand_reason($demand_reason_id, $notrigger = 0)
2888 {
2889 // phpcs:enable
2890 global $user;
2891
2892 dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2893 if ($this->statut >= self::STATUS_DRAFT) {
2894 $error = 0;
2895
2896 $this->db->begin();
2897
2898 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2899 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
2900 $sql .= ' WHERE rowid='.((int) $this->id);
2901
2902 dol_syslog(__METHOD__, LOG_DEBUG);
2903 $resql = $this->db->query($sql);
2904 if (!$resql) {
2905 $this->errors[] = $this->db->error();
2906 $error++;
2907 }
2908
2909 if (!$error) {
2910 $this->oldcopy = clone $this;
2911 $this->demand_reason_id = $demand_reason_id;
2912 }
2913
2914 if (!$notrigger && empty($error)) {
2915 // Call trigger
2916 $result = $this->call_trigger('ORDER_MODIFY', $user);
2917 if ($result < 0) {
2918 $error++;
2919 }
2920 // End call triggers
2921 }
2922
2923 if (!$error) {
2924 $this->db->commit();
2925 return 1;
2926 } else {
2927 foreach ($this->errors as $errmsg) {
2928 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2929 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2930 }
2931 $this->db->rollback();
2932 return -1 * $error;
2933 }
2934 } else {
2935 $error_str = 'order status do not meet requirement '.$this->statut;
2936 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2937 $this->error = $error_str;
2938 $this->errors[] = $this->error;
2939 return -2;
2940 }
2941 }
2942
2943 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2952 public function set_ref_client($user, $ref_client, $notrigger = 0)
2953 {
2954 // phpcs:enable
2955 if ($user->hasRight('commande', 'creer')) {
2956 $error = 0;
2957
2958 $this->db->begin();
2959
2960 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2961 $sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2962 $sql .= ' WHERE rowid = '.((int) $this->id);
2963
2964 dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2965 $resql = $this->db->query($sql);
2966 if (!$resql) {
2967 $this->errors[] = $this->db->error();
2968 $error++;
2969 }
2970
2971 if (!$error) {
2972 $this->oldcopy = clone $this;
2973 $this->ref_client = $ref_client;
2974 $this->ref_customer = $ref_client;
2975 }
2976
2977 if (!$notrigger && empty($error)) {
2978 // Call trigger
2979 $result = $this->call_trigger('ORDER_MODIFY', $user);
2980 if ($result < 0) {
2981 $error++;
2982 }
2983 // End call triggers
2984 }
2985 if (!$error) {
2986 $this->db->commit();
2987 return 1;
2988 } else {
2989 foreach ($this->errors as $errmsg) {
2990 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2991 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2992 }
2993 $this->db->rollback();
2994 return -1 * $error;
2995 }
2996 } else {
2997 return -1;
2998 }
2999 }
3000
3008 public function classifyBilled(User $user, $notrigger = 0)
3009 {
3010 $error = 0;
3011
3012 if ($this->billed) {
3013 return 0;
3014 }
3015
3016 $this->db->begin();
3017
3018 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
3019 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3020
3021 dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
3022 if ($this->db->query($sql)) {
3023 if (!$error) {
3024 $this->oldcopy = clone $this;
3025 $this->billed = 1;
3026 }
3027
3028 if (!$notrigger && empty($error)) {
3029 // Call trigger
3030 $result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
3031 if ($result < 0) {
3032 $error++;
3033 }
3034 // End call triggers
3035 }
3036
3037 if (!$error) {
3038 $this->db->commit();
3039 return 1;
3040 } else {
3041 foreach ($this->errors as $errmsg) {
3042 dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
3043 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3044 }
3045 $this->db->rollback();
3046 return -1 * $error;
3047 }
3048 } else {
3049 $this->error = $this->db->error();
3050 $this->db->rollback();
3051 return -1;
3052 }
3053 }
3054
3062 public function classifyUnBilled(User $user, $notrigger = 0)
3063 {
3064 $error = 0;
3065
3066 $this->db->begin();
3067
3068 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
3069 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
3070
3071 dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
3072 if ($this->db->query($sql)) {
3073 if (!$error) {
3074 $this->oldcopy = clone $this;
3075 $this->billed = 1;
3076 }
3077
3078 if (!$notrigger && empty($error)) {
3079 // Call trigger
3080 $result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
3081 if ($result < 0) {
3082 $error++;
3083 }
3084 // End call triggers
3085 }
3086
3087 if (!$error) {
3088 $this->billed = 0;
3089
3090 $this->db->commit();
3091 return 1;
3092 } else {
3093 foreach ($this->errors as $errmsg) {
3094 dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
3095 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3096 }
3097 $this->db->rollback();
3098 return -1 * $error;
3099 }
3100 } else {
3101 $this->error = $this->db->error();
3102 $this->db->rollback();
3103 return -1;
3104 }
3105 }
3106
3107
3138 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 = 0, $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $ref_ext = '', $rang = 0)
3139 {
3140 global $conf, $mysoc, $langs, $user;
3141
3142 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");
3143 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3144
3145 if ($this->statut == Commande::STATUS_DRAFT) {
3146 // Clean parameters
3147 if (empty($qty)) {
3148 $qty = 0;
3149 }
3150 if (empty($info_bits)) {
3151 $info_bits = 0;
3152 }
3153 if (empty($txtva)) {
3154 $txtva = 0;
3155 }
3156 if (empty($txlocaltax1)) {
3157 $txlocaltax1 = 0;
3158 }
3159 if (empty($txlocaltax2)) {
3160 $txlocaltax2 = 0;
3161 }
3162 if (empty($remise_percent)) {
3163 $remise_percent = 0;
3164 }
3165 if (empty($special_code) || $special_code == 3) {
3166 $special_code = 0;
3167 }
3168 if (empty($ref_ext)) {
3169 $ref_ext = '';
3170 }
3171
3172 if ($date_start && $date_end && $date_start > $date_end) {
3173 $langs->load("errors");
3174 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3175 return -1;
3176 }
3177
3179 $qty = price2num($qty);
3180 $pu = price2num($pu);
3181 $pa_ht = price2num($pa_ht);
3182 $pu_ht_devise = price2num($pu_ht_devise);
3183 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
3184 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3185 }
3186 $txlocaltax1 = price2num($txlocaltax1);
3187 $txlocaltax2 = price2num($txlocaltax2);
3188
3189 $this->db->begin();
3190
3191 // Calcul du total TTC et de la TVA pour la ligne a partir de
3192 // qty, pu, remise_percent et txtva
3193 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3194 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3195
3196 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3197
3198 // Clean vat code
3199 $vat_src_code = '';
3200 $reg = array();
3201 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3202 $vat_src_code = $reg[1];
3203 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3204 }
3205
3206 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
3207
3208 $total_ht = $tabprice[0];
3209 $total_tva = $tabprice[1];
3210 $total_ttc = $tabprice[2];
3211 $total_localtax1 = $tabprice[9];
3212 $total_localtax2 = $tabprice[10];
3213 $pu_ht = $tabprice[3];
3214 $pu_tva = $tabprice[4];
3215 $pu_ttc = $tabprice[5];
3216
3217 // MultiCurrency
3218 $multicurrency_total_ht = $tabprice[16];
3219 $multicurrency_total_tva = $tabprice[17];
3220 $multicurrency_total_ttc = $tabprice[18];
3221 $pu_ht_devise = $tabprice[19];
3222
3223 // Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3224 $price = $pu_ht;
3225 if ($price_base_type == 'TTC') {
3226 $subprice = $pu_ttc;
3227 } else {
3228 $subprice = $pu_ht;
3229 }
3230 $remise = 0;
3231 if ($remise_percent > 0) {
3232 $remise = round(($pu * $remise_percent / 100), 2);
3233 $price = ($pu - $remise);
3234 }
3235
3236 //Fetch current line from the database and then clone the object and set it in $oldline property
3237 $line = new OrderLine($this->db);
3238 $line->fetch($rowid);
3239 $line->fetch_optionals();
3240
3241 if (!empty($line->fk_product)) {
3242 $product = new Product($this->db);
3243 $result = $product->fetch($line->fk_product);
3244 $product_type = $product->type;
3245
3246 if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) {
3247 $langs->load("errors");
3248 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3249 $this->errors[] = $this->error;
3250
3251 dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3252
3253 $this->db->rollback();
3255 }
3256 }
3257
3258 $staticline = clone $line;
3259
3260 $line->oldline = $staticline;
3261 $this->line = $line;
3262 $this->line->context = $this->context;
3263 $this->line->rang = $rang;
3264
3265 // Reorder if fk_parent_line change
3266 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
3267 $rangmax = $this->line_max($fk_parent_line);
3268 $this->line->rang = $rangmax + 1;
3269 }
3270
3271 $this->line->id = $rowid;
3272 $this->line->label = $label;
3273 $this->line->desc = $desc;
3274 $this->line->qty = $qty;
3275 $this->line->ref_ext = $ref_ext;
3276
3277 $this->line->vat_src_code = $vat_src_code;
3278 $this->line->tva_tx = $txtva;
3279 $this->line->localtax1_tx = $txlocaltax1;
3280 $this->line->localtax2_tx = $txlocaltax2;
3281 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3282 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3283 $this->line->remise_percent = $remise_percent;
3284 $this->line->subprice = $subprice;
3285 $this->line->info_bits = $info_bits;
3286 $this->line->special_code = $special_code;
3287 $this->line->total_ht = $total_ht;
3288 $this->line->total_tva = $total_tva;
3289 $this->line->total_localtax1 = $total_localtax1;
3290 $this->line->total_localtax2 = $total_localtax2;
3291 $this->line->total_ttc = $total_ttc;
3292 $this->line->date_start = $date_start;
3293 $this->line->date_end = $date_end;
3294 $this->line->product_type = $type;
3295 $this->line->fk_parent_line = $fk_parent_line;
3296 $this->line->skip_update_total = $skip_update_total;
3297 $this->line->fk_unit = $fk_unit;
3298
3299 $this->line->fk_fournprice = $fk_fournprice;
3300 $this->line->pa_ht = $pa_ht;
3301
3302 // Multicurrency
3303 $this->line->multicurrency_subprice = $pu_ht_devise;
3304 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3305 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3306 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3307
3308 // TODO deprecated
3309 $this->line->price = $price;
3310
3311 if (is_array($array_options) && count($array_options) > 0) {
3312 // We replace values in this->line->array_options only for entries defined into $array_options
3313 foreach ($array_options as $key => $value) {
3314 $this->line->array_options[$key] = $array_options[$key];
3315 }
3316 }
3317
3318 $result = $this->line->update($user, $notrigger);
3319 if ($result > 0) {
3320 // Reorder if child line
3321 if (!empty($fk_parent_line)) {
3322 $this->line_order(true, 'DESC');
3323 }
3324
3325 // Mise a jour info denormalisees
3326 $this->update_price(1, 'auto');
3327
3328 $this->db->commit();
3329 return $result;
3330 } else {
3331 $this->error = $this->line->error;
3332
3333 $this->db->rollback();
3334 return -1;
3335 }
3336 } else {
3337 $this->error = get_class($this)."::updateline Order status makes operation forbidden";
3338 $this->errors = array('OrderStatusMakeOperationForbidden');
3339 return -2;
3340 }
3341 }
3342
3350 public function update(User $user, $notrigger = 0)
3351 {
3352 global $conf;
3353
3354 $error = 0;
3355
3356 // Clean parameters
3357 if (isset($this->ref)) {
3358 $this->ref = trim($this->ref);
3359 }
3360 if (isset($this->ref_client)) {
3361 $this->ref_client = trim($this->ref_client);
3362 }
3363 if (isset($this->ref_customer)) {
3364 $this->ref_customer = trim($this->ref_customer);
3365 }
3366 if (isset($this->note) || isset($this->note_private)) {
3367 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3368 }
3369 if (isset($this->note_public)) {
3370 $this->note_public = trim($this->note_public);
3371 }
3372 if (isset($this->model_pdf)) {
3373 $this->model_pdf = trim($this->model_pdf);
3374 }
3375 if (isset($this->import_key)) {
3376 $this->import_key = trim($this->import_key);
3377 }
3378 $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
3379
3380 // Check parameters
3381 // Put here code to add control on parameters values
3382
3383 // Update request
3384 $sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3385
3386 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3387 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3388 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3389 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3390 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3391 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3392 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3393 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3394 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3395 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3396 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3397 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3398 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3399 $sql .= " fk_user_valid=".((isset($this->user_valid) && $this->user_valid > 0) ? $this->user_valid : "null").",";
3400 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3401 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3402 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? strval($this->deposit_percent) : "null").",";
3403 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3404 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
3405 $sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
3406 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3407 $sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
3408 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3409 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3410 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
3411 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
3412
3413 $sql .= " WHERE rowid=".((int) $this->id);
3414
3415 $this->db->begin();
3416
3417 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3418 $resql = $this->db->query($sql);
3419 if (!$resql) {
3420 $error++; $this->errors[] = "Error ".$this->db->lasterror();
3421 }
3422
3423 if (!$error) {
3424 $result = $this->insertExtraFields();
3425 if ($result < 0) {
3426 $error++;
3427 }
3428 }
3429
3430 if (!$error && !$notrigger) {
3431 // Call trigger
3432 $result = $this->call_trigger('ORDER_MODIFY', $user);
3433 if ($result < 0) {
3434 $error++;
3435 }
3436 // End call triggers
3437 }
3438
3439 // Commit or rollback
3440 if ($error) {
3441 foreach ($this->errors as $errmsg) {
3442 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3443 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3444 }
3445 $this->db->rollback();
3446 return -1 * $error;
3447 } else {
3448 $this->db->commit();
3449 return 1;
3450 }
3451 }
3452
3460 public function delete($user, $notrigger = 0)
3461 {
3462 global $conf, $langs;
3463 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3464
3465 $error = 0;
3466
3467 dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3468
3469 $this->db->begin();
3470
3471 if (!$notrigger) {
3472 // Call trigger
3473 $result = $this->call_trigger('ORDER_DELETE', $user);
3474 if ($result < 0) {
3475 $error++;
3476 }
3477 // End call triggers
3478 }
3479
3480 // Test we can delete
3481 if ($this->countNbOfShipments() != 0) {
3482 $this->errors[] = $langs->trans('SomeShipmentExists');
3483 $error++;
3484 }
3485
3486 // Delete extrafields of lines and lines
3487 if (!$error && !empty($this->table_element_line)) {
3488 $tabletodelete = $this->table_element_line;
3489 $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).")";
3490 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3491 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3492 $error++;
3493 $this->error = $this->db->lasterror();
3494 $this->errors[] = $this->error;
3495 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3496 }
3497 }
3498
3499 if (!$error) {
3500 // Delete linked object
3501 $res = $this->deleteObjectLinked();
3502 if ($res < 0) {
3503 $error++;
3504 }
3505 }
3506
3507 if (!$error) {
3508 // Delete linked contacts
3509 $res = $this->delete_linked_contact();
3510 if ($res < 0) {
3511 $error++;
3512 }
3513 }
3514
3515 // Removed extrafields of object
3516 if (!$error) {
3517 $result = $this->deleteExtraFields();
3518 if ($result < 0) {
3519 $error++;
3520 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3521 }
3522 }
3523
3524 // Delete main record
3525 if (!$error) {
3526 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3527 $res = $this->db->query($sql);
3528 if (!$res) {
3529 $error++;
3530 $this->error = $this->db->lasterror();
3531 $this->errors[] = $this->error;
3532 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3533 }
3534 }
3535
3536 // Delete record into ECM index and physically
3537 if (!$error) {
3538 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3539 if (!$res) {
3540 $error++;
3541 }
3542 }
3543
3544 if (!$error) {
3545 // We remove directory
3546 $ref = dol_sanitizeFileName($this->ref);
3547 if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref)) {
3548 $dir = $conf->commande->multidir_output[$this->entity]."/".$ref;
3549 $file = $dir."/".$ref.".pdf";
3550 if (file_exists($file)) {
3551 dol_delete_preview($this);
3552
3553 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3554 $this->error = 'ErrorFailToDeleteFile';
3555 $this->errors[] = $this->error;
3556 $this->db->rollback();
3557 return 0;
3558 }
3559 }
3560 if (file_exists($dir)) {
3561 $res = @dol_delete_dir_recursive($dir);
3562 if (!$res) {
3563 $this->error = 'ErrorFailToDeleteDir';
3564 $this->errors[] = $this->error;
3565 $this->db->rollback();
3566 return 0;
3567 }
3568 }
3569 }
3570 }
3571
3572 if (!$error) {
3573 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3574 $this->db->commit();
3575 return 1;
3576 } else {
3577 $this->db->rollback();
3578 return -1;
3579 }
3580 }
3581
3582
3583 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3590 public function load_board($user)
3591 {
3592 // phpcs:enable
3593 global $conf, $langs;
3594
3595 $clause = " WHERE";
3596
3597 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3598 $sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3599 if (empty($user->rights->societe->client->voir) && !$user->socid) {
3600 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3601 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3602 $clause = " AND";
3603 }
3604 $sql .= $clause." c.entity IN (".getEntity('commande').")";
3605 //$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3606 $sql .= " AND ((c.fk_statut IN (".self::STATUS_VALIDATED.",".self::STATUS_SHIPMENTONPROCESS.")) OR (c.fk_statut = ".self::STATUS_CLOSED." AND c.facture = 0))"; // If status is 2 and facture=1, it must be selected
3607 if ($user->socid) {
3608 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3609 }
3610
3611 $resql = $this->db->query($sql);
3612 if ($resql) {
3613 $response = new WorkboardResponse();
3614 $response->warning_delay = $conf->commande->client->warning_delay / 60 / 60 / 24;
3615 $response->label = $langs->trans("OrdersToProcess");
3616 $response->labelShort = $langs->trans("Opened");
3617 $response->url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&mainmenu=commercial&leftmenu=orders';
3618 $response->url_late = DOL_URL_ROOT.'/commande/list.php?search_option=late&mainmenu=commercial&leftmenu=orders';
3619 $response->img = img_object('', "order");
3620
3621 $generic_commande = new Commande($this->db);
3622
3623 while ($obj = $this->db->fetch_object($resql)) {
3624 $response->nbtodo++;
3625 $response->total += $obj->total_ht;
3626
3627 $generic_commande->statut = $obj->fk_statut;
3628 $generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3629 $generic_commande->date = $this->db->jdate($obj->date_commande);
3630 $generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3631 $generic_commande->delivery_date = $this->db->jdate($obj->delivery_date);
3632
3633 if ($generic_commande->hasDelay()) {
3634 $response->nbtodolate++;
3635 }
3636 }
3637
3638 return $response;
3639 } else {
3640 $this->error = $this->db->error();
3641 return -1;
3642 }
3643 }
3644
3650 public function getLabelSource()
3651 {
3652 global $langs;
3653
3654 $label = $langs->trans('OrderSource'.$this->source);
3655
3656 if ($label == 'OrderSource') {
3657 return '';
3658 }
3659 return $label;
3660 }
3661
3668 public function getLibStatut($mode)
3669 {
3670 return $this->LibStatut($this->statut, $this->billed, $mode);
3671 }
3672
3673 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3683 public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3684 {
3685 // phpcs:enable
3686 global $langs, $conf, $hookmanager;
3687
3688 $billedtext = '';
3689 if (empty($donotshowbilled)) {
3690 $billedtext .= ($billed ? ' - '.$langs->transnoentitiesnoconv("Billed") : '');
3691 }
3692
3693 $labelTooltip = '';
3694
3695 if ($status == self::STATUS_CANCELED) {
3696 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderCanceled');
3697 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderCanceledShort');
3698 $statusType = 'status9';
3699 } elseif ($status == self::STATUS_DRAFT) {
3700 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDraft');
3701 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDraftShort');
3702 $statusType = 'status0';
3703 } elseif ($status == self::STATUS_VALIDATED) {
3704 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderValidated').$billedtext;
3705 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderValidatedShort').$billedtext;
3706 $statusType = 'status1';
3707 } elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3708 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderSent').$billedtext;
3709 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderSentShort').$billedtext;
3710 $labelTooltip = $langs->transnoentitiesnoconv("StatusOrderSent");
3711 if (!empty($this->delivery_date)) {
3712 $labelTooltip .= ' - '.$langs->transnoentitiesnoconv("DateDeliveryPlanned").dol_print_date($this->delivery_date, 'day').$billedtext;
3713 }
3714 $statusType = 'status4';
3715 } elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3716 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderToBill'); // translated into Delivered
3717 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderToBillShort'); // translated into Delivered
3718 $statusType = 'status4';
3719 } elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3720 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderProcessed').$billedtext;
3721 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderProcessedShort').$billedtext;
3722 $statusType = 'status6';
3723 } elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3724 $labelStatus = $langs->transnoentitiesnoconv('StatusOrderDelivered');
3725 $labelStatusShort = $langs->transnoentitiesnoconv('StatusOrderDeliveredShort');
3726 $statusType = 'status6';
3727 } else {
3728 $labelStatus = $langs->transnoentitiesnoconv('Unknown');
3729 $labelStatusShort = '';
3730 $statusType = '';
3731 $mode = 0;
3732 }
3733
3734 $parameters = array(
3735 'status' => $status,
3736 'mode' => $mode,
3737 'billed' => $billed,
3738 'donotshowbilled' => $donotshowbilled
3739 );
3740
3741 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3742
3743 if ($reshook > 0) {
3744 return $hookmanager->resPrint;
3745 }
3746
3747 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', array('tooltip' => $labelTooltip));
3748 }
3749
3756 public function getTooltipContentArray($params)
3757 {
3758 global $conf, $langs, $user;
3759
3760 $langs->load('orders');
3761 $datas = [];
3762 $nofetch = !empty($params['nofetch']);
3763
3764 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3765 return ['optimize' => $langs->trans("Order")];
3766 }
3767
3768 if ($user->hasRight('commande', 'lire')) {
3769 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Order").'</u>';
3770 if (isset($this->statut)) {
3771 $datas['status'] = ' '.$this->getLibStatut(5);
3772 }
3773 $datas['Ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3774 if (!$nofetch) {
3775 $langs->load('companies');
3776 if (empty($this->thirdparty)) {
3777 $this->fetch_thirdparty();
3778 }
3779 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3780 }
3781 $datas['RefCustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.(empty($this->ref_customer) ? (empty($this->ref_client) ? '' : $this->ref_client) : $this->ref_customer);
3782 if (!$nofetch) {
3783 $langs->load('project');
3784 if (empty($this->project)) {
3785 $res = $this->fetch_project();
3786 if ($res > 0) {
3787 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3788 }
3789 }
3790 }
3791 if (!empty($this->total_ht)) {
3792 $datas['AmountHT'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3793 }
3794 if (!empty($this->total_tva)) {
3795 $datas['VAT'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3796 }
3797 if (!empty($this->total_ttc)) {
3798 $datas['AmountTTC'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3799 }
3800 if (!empty($this->date)) {
3801 $datas['Date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3802 }
3803 if (!empty($this->delivery_date)) {
3804 $datas['DeliveryDate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3805 }
3806 }
3807
3808 return $datas;
3809 }
3810
3824 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '')
3825 {
3826 global $conf, $langs, $user, $hookmanager;
3827
3828 if (!empty($conf->dol_no_mouse_hover)) {
3829 $notooltip = 1; // Force disable tooltips
3830 }
3831
3832 $result = '';
3833
3834 if (isModEnabled("expedition") && ($option == '1' || $option == '2')) {
3835 $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3836 } else {
3837 $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3838 }
3839
3840 if (!$user->rights->commande->lire) {
3841 $option = 'nolink';
3842 }
3843
3844 if ($option !== 'nolink') {
3845 // Add param to save lastsearch_values or not
3846 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3847 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3848 $add_save_lastsearch_values = 1;
3849 }
3850 if ($add_save_lastsearch_values) {
3851 $url .= '&save_lastsearch_values=1';
3852 }
3853 }
3854
3855 if ($short) {
3856 return $url;
3857 }
3858 $params = [
3859 'id' => $this->id,
3860 'objecttype' => $this->element,
3861 'option' => $option,
3862 'nofetch' => 1,
3863 ];
3864 $classfortooltip = 'classfortooltip';
3865 $dataparams = '';
3866 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3867 $classfortooltip = 'classforajaxtooltip';
3868 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3869 $label = '';
3870 } else {
3871 $label = implode($this->getTooltipContentArray($params));
3872 }
3873
3874 $linkclose = '';
3875 if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
3876 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3877 $label = $langs->trans("Order");
3878 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3879 }
3880 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3881 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3882
3883 $target_value = array('_self', '_blank', '_parent', '_top');
3884 if (in_array($target, $target_value)) {
3885 $linkclose .= ' target="'.dol_escape_htmltag($target).'"';
3886 }
3887 }
3888
3889 $linkstart = '<a href="'.$url.'"';
3890 $linkstart .= $linkclose.'>';
3891 $linkend = '</a>';
3892
3893 if ($option === 'nolink') {
3894 $linkstart = '';
3895 $linkend = '';
3896 }
3897
3898 $result .= $linkstart;
3899 if ($withpicto) {
3900 $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3901 }
3902 if ($withpicto != 2) {
3903 $result .= $this->ref;
3904 }
3905 $result .= $linkend;
3906
3907 if ($addlinktonotes) {
3908 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3909 if ($txttoshow) {
3910 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3911 $result .= ' <span class="note inline-block">';
3912 $result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3913 $result .= img_picto('', 'note');
3914 $result .= '</a>';
3915 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3916 //$result.='</a>';
3917 $result .= '</span>';
3918 }
3919 }
3920
3921 global $action;
3922 $hookmanager->initHooks(array($this->element . 'dao'));
3923 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3924 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3925 if ($reshook > 0) {
3926 $result = $hookmanager->resPrint;
3927 } else {
3928 $result .= $hookmanager->resPrint;
3929 }
3930 return $result;
3931 }
3932
3933
3940 public function info($id)
3941 {
3942 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3943 $sql .= ' date_valid as datev,';
3944 $sql .= ' date_cloture as datecloture,';
3945 $sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3946 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3947 $sql .= ' WHERE c.rowid = '.((int) $id);
3948 $result = $this->db->query($sql);
3949 if ($result) {
3950 if ($this->db->num_rows($result)) {
3951 $obj = $this->db->fetch_object($result);
3952 $this->id = $obj->rowid;
3953 if ($obj->fk_user_author) {
3954 $this->user_creation_id = $obj->fk_user_author;
3955 }
3956 if ($obj->fk_user_valid) {
3957 $this->user_validation_id = $obj->fk_user_valid;
3958 }
3959 if ($obj->fk_user_cloture) {
3960 $this->user_closing_id = $obj->fk_user_cloture;
3961 }
3962
3963 $this->date_creation = $this->db->jdate($obj->datec);
3964 $this->date_modification = $this->db->jdate($obj->datem);
3965 $this->date_validation = $this->db->jdate($obj->datev);
3966 $this->date_cloture = $this->db->jdate($obj->datecloture);
3967 }
3968
3969 $this->db->free($result);
3970 } else {
3971 dol_print_error($this->db);
3972 }
3973 }
3974
3975
3983 public function initAsSpecimen()
3984 {
3985 global $conf, $langs;
3986
3987 dol_syslog(get_class($this)."::initAsSpecimen");
3988
3989 // Load array of products prodids
3990 $num_prods = 0;
3991 $prodids = array();
3992 $sql = "SELECT rowid";
3993 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3994 $sql .= " WHERE entity IN (".getEntity('product').")";
3995 $sql .= $this->db->plimit(100);
3996
3997 $resql = $this->db->query($sql);
3998 if ($resql) {
3999 $num_prods = $this->db->num_rows($resql);
4000 $i = 0;
4001 while ($i < $num_prods) {
4002 $i++;
4003 $row = $this->db->fetch_row($resql);
4004 $prodids[$i] = $row[0];
4005 }
4006 }
4007
4008 // Initialise parametres
4009 $this->id = 0;
4010 $this->ref = 'SPECIMEN';
4011 $this->specimen = 1;
4012 $this->entity = $conf->entity;
4013 $this->socid = 1;
4014 $this->date = time();
4015 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
4016 $this->cond_reglement_code = 'RECEP';
4017 $this->mode_reglement_code = 'CHQ';
4018 $this->availability_code = 'DSP';
4019 $this->demand_reason_code = 'SRC_00';
4020
4021 $this->note_public = 'This is a comment (public)';
4022 $this->note_private = 'This is a comment (private)';
4023
4024 $this->multicurrency_tx = 1;
4025 $this->multicurrency_code = $conf->currency;
4026
4027 // Lines
4028 $nbp = 5;
4029 $xnbp = 0;
4030 while ($xnbp < $nbp) {
4031 $line = new OrderLine($this->db);
4032
4033 $line->desc = $langs->trans("Description")." ".$xnbp;
4034 $line->qty = 1;
4035 $line->subprice = 100;
4036 $line->price = 100;
4037 $line->tva_tx = 20;
4038 if ($xnbp == 2) {
4039 $line->total_ht = 50;
4040 $line->total_ttc = 60;
4041 $line->total_tva = 10;
4042 $line->remise_percent = 50;
4043 } else {
4044 $line->total_ht = 100;
4045 $line->total_ttc = 120;
4046 $line->total_tva = 20;
4047 $line->remise_percent = 0;
4048 }
4049 if ($num_prods > 0) {
4050 $prodid = mt_rand(1, $num_prods);
4051 $line->fk_product = $prodids[$prodid];
4052 $line->product_ref = 'SPECIMEN';
4053 }
4054
4055 $this->lines[$xnbp] = $line;
4056
4057 $this->total_ht += $line->total_ht;
4058 $this->total_tva += $line->total_tva;
4059 $this->total_ttc += $line->total_ttc;
4060
4061 $xnbp++;
4062 }
4063 }
4064
4065
4066 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4072 public function load_state_board()
4073 {
4074 // phpcs:enable
4075 global $user;
4076
4077 $this->nb = array();
4078 $clause = "WHERE";
4079
4080 $sql = "SELECT count(co.rowid) as nb";
4081 $sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
4082 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
4083 if (empty($user->rights->societe->client->voir) && !$user->socid) {
4084 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
4085 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
4086 $clause = "AND";
4087 }
4088 $sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
4089
4090 $resql = $this->db->query($sql);
4091 if ($resql) {
4092 while ($obj = $this->db->fetch_object($resql)) {
4093 $this->nb["orders"] = $obj->nb;
4094 }
4095 $this->db->free($resql);
4096 return 1;
4097 } else {
4098 dol_print_error($this->db);
4099 $this->error = $this->db->error();
4100 return -1;
4101 }
4102 }
4103
4109 public function getLinesArray()
4110 {
4111 return $this->fetch_lines();
4112 }
4113
4125 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4126 {
4127 global $conf, $langs;
4128
4129 $langs->load("orders");
4130 $outputlangs->load("products");
4131
4132 if (!dol_strlen($modele)) {
4133 $modele = 'einstein';
4134
4135 if (!empty($this->model_pdf)) {
4136 $modele = $this->model_pdf;
4137 } elseif (!empty($this->modelpdf)) { // deprecated
4138 $modele = $this->modelpdf;
4139 } elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
4140 $modele = $conf->global->COMMANDE_ADDON_PDF;
4141 }
4142 }
4143
4144 $modelpath = "core/modules/commande/doc/";
4145
4146 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4147 }
4148
4149
4158 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
4159 {
4160 $tables = array(
4161 'commande'
4162 );
4163
4164 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
4165 }
4166
4175 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
4176 {
4177 $tables = array(
4178 'commandedet',
4179 );
4180
4181 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
4182 }
4183
4189 public function hasDelay()
4190 {
4191 global $conf;
4192
4193 if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
4194 return false; // Never late if not inside this status range
4195 }
4196
4197 $now = dol_now();
4198
4199 return max($this->date, $this->delivery_date) < ($now - $conf->commande->client->warning_delay);
4200 }
4201
4207 public function showDelay()
4208 {
4209 global $conf, $langs;
4210
4211 if (empty($this->delivery_date)) {
4212 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
4213 } else {
4214 $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
4215 }
4216 $text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
4217
4218 return $text;
4219 }
4220}
4221
4222
4227{
4231 public $element = 'commandedet';
4232
4233 public $table_element = 'commandedet';
4234
4235 public $oldline;
4236
4241 public $fk_commande;
4242
4249 public $commande_id;
4250
4251 public $fk_parent_line;
4252
4256 public $fk_facture;
4257
4261 public $ref_ext;
4262
4263 public $fk_remise_except;
4264
4268 public $rang = 0;
4269 public $fk_fournprice;
4270
4275 public $pa_ht;
4276 public $marge_tx;
4277 public $marque_tx;
4278
4283 public $remise;
4284
4285 // Start and end date of the line
4286 public $date_start;
4287 public $date_end;
4288
4289 public $skip_update_total; // Skip update price total for special lines
4290
4291
4297 public function __construct($db)
4298 {
4299 $this->db = $db;
4300 }
4301
4308 public function fetch($rowid)
4309 {
4310 $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,';
4311 $sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice, cd.ref_ext,';
4312 $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,';
4313 $sql .= ' cd.fk_unit,';
4314 $sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4315 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch,';
4316 $sql .= ' cd.date_start, cd.date_end, cd.vat_src_code';
4317 $sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4318 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4319 $sql .= ' WHERE cd.rowid = '.((int) $rowid);
4320 $result = $this->db->query($sql);
4321 if ($result) {
4322 $objp = $this->db->fetch_object($result);
4323
4324 if (!$objp) {
4325 $this->error = 'OrderLine with id '. $rowid .' not found sql='.$sql;
4326 return 0;
4327 }
4328
4329 $this->rowid = $objp->rowid;
4330 $this->id = $objp->rowid;
4331 $this->fk_commande = $objp->fk_commande;
4332 $this->fk_parent_line = $objp->fk_parent_line;
4333 $this->label = $objp->custom_label;
4334 $this->desc = $objp->description;
4335 $this->qty = $objp->qty;
4336 $this->price = $objp->price;
4337 $this->subprice = $objp->subprice;
4338 $this->ref_ext = $objp->ref_ext;
4339 $this->vat_src_code = $objp->vat_src_code;
4340 $this->tva_tx = $objp->tva_tx;
4341 $this->localtax1_tx = $objp->localtax1_tx;
4342 $this->localtax2_tx = $objp->localtax2_tx;
4343 $this->remise = $objp->remise;
4344 $this->remise_percent = $objp->remise_percent;
4345 $this->fk_remise_except = $objp->fk_remise_except;
4346 $this->fk_product = $objp->fk_product;
4347 $this->product_type = $objp->product_type;
4348 $this->info_bits = $objp->info_bits;
4349 $this->special_code = $objp->special_code;
4350 $this->total_ht = $objp->total_ht;
4351 $this->total_tva = $objp->total_tva;
4352 $this->total_localtax1 = $objp->total_localtax1;
4353 $this->total_localtax2 = $objp->total_localtax2;
4354 $this->total_ttc = $objp->total_ttc;
4355 $this->fk_fournprice = $objp->fk_fournprice;
4356 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4357 $this->pa_ht = $marginInfos[0];
4358 $this->marge_tx = $marginInfos[1];
4359 $this->marque_tx = $marginInfos[2];
4360 $this->special_code = $objp->special_code;
4361 $this->rang = $objp->rang;
4362
4363 $this->ref = $objp->product_ref; // deprecated
4364
4365 $this->product_ref = $objp->product_ref;
4366 $this->product_label = $objp->product_label;
4367 $this->product_desc = $objp->product_desc;
4368 $this->product_tobatch = $objp->product_tobatch;
4369 $this->fk_unit = $objp->fk_unit;
4370
4371 $this->date_start = $this->db->jdate($objp->date_start);
4372 $this->date_end = $this->db->jdate($objp->date_end);
4373
4374 $this->fk_multicurrency = $objp->fk_multicurrency;
4375 $this->multicurrency_code = $objp->multicurrency_code;
4376 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4377 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4378 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4379 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4380
4381 $this->fetch_optionals();
4382
4383 $this->db->free($result);
4384
4385 return 1;
4386 } else {
4387 $this->error = $this->db->lasterror();
4388 return -1;
4389 }
4390 }
4391
4399 public function delete(User $user, $notrigger = 0)
4400 {
4401 global $conf, $langs;
4402
4403 $error = 0;
4404
4405 if (empty($this->id) && !empty($this->rowid)) { // For backward compatibility
4406 $this->id = $this->rowid;
4407 }
4408
4409 // check if order line is not in a shipment line before deleting
4410 $sqlCheckShipmentLine = "SELECT";
4411 $sqlCheckShipmentLine .= " ed.rowid";
4412 $sqlCheckShipmentLine .= " FROM " . MAIN_DB_PREFIX . "expeditiondet ed";
4413 $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = " . ((int) $this->id);
4414
4415 $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4416 if (!$resqlCheckShipmentLine) {
4417 $error++;
4418 $this->error = $this->db->lasterror();
4419 $this->errors[] = $this->error;
4420 } else {
4421 $langs->load('errors');
4422 $num = $this->db->num_rows($resqlCheckShipmentLine);
4423 if ($num > 0) {
4424 $error++;
4425 $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4426 $this->error = $langs->trans('ErrorRecordAlreadyExists') . ' : ' . $langs->trans('ShipmentLine') . ' ' . $objCheckShipmentLine->rowid;
4427 $this->errors[] = $this->error;
4428 }
4429 $this->db->free($resqlCheckShipmentLine);
4430 }
4431 if ($error) {
4432 dol_syslog(__METHOD__ . 'Error ; ' . $this->error, LOG_ERR);
4433 return -1;
4434 }
4435
4436 $this->db->begin();
4437
4438 if (!$notrigger) {
4439 // Call trigger
4440 $result = $this->call_trigger('LINEORDER_DELETE', $user);
4441 if ($result < 0) {
4442 $error++;
4443 }
4444 // End call triggers
4445 }
4446
4447 if (!$error) {
4448 $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . "commandedet WHERE rowid = " . ((int) $this->id);
4449
4450 dol_syslog("OrderLine::delete", LOG_DEBUG);
4451 $resql = $this->db->query($sql);
4452 if (!$resql) {
4453 $this->error = $this->db->lasterror();
4454 $error++;
4455 }
4456 }
4457
4458 // Remove extrafields
4459 if (!$error) {
4460 $result = $this->deleteExtraFields();
4461 if ($result < 0) {
4462 $error++;
4463 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4464 }
4465 }
4466
4467 if (!$error) {
4468 $this->db->commit();
4469 return 1;
4470 }
4471
4472 foreach ($this->errors as $errmsg) {
4473 dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
4474 $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
4475 }
4476 $this->db->rollback();
4477 return -1 * $error;
4478 }
4479
4487 public function insert($user = null, $notrigger = 0)
4488 {
4489 global $langs, $conf;
4490
4491 $error = 0;
4492
4493 $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'.
4494
4495 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4496
4497 // Clean parameters
4498 if (empty($this->tva_tx)) {
4499 $this->tva_tx = 0;
4500 }
4501 if (empty($this->localtax1_tx)) {
4502 $this->localtax1_tx = 0;
4503 }
4504 if (empty($this->localtax2_tx)) {
4505 $this->localtax2_tx = 0;
4506 }
4507 if (empty($this->localtax1_type)) {
4508 $this->localtax1_type = 0;
4509 }
4510 if (empty($this->localtax2_type)) {
4511 $this->localtax2_type = 0;
4512 }
4513 if (empty($this->total_localtax1)) {
4514 $this->total_localtax1 = 0;
4515 }
4516 if (empty($this->total_localtax2)) {
4517 $this->total_localtax2 = 0;
4518 }
4519 if (empty($this->rang)) {
4520 $this->rang = 0;
4521 }
4522 if (empty($this->remise_percent)) {
4523 $this->remise_percent = 0;
4524 }
4525 if (empty($this->info_bits)) {
4526 $this->info_bits = 0;
4527 }
4528 if (empty($this->special_code)) {
4529 $this->special_code = 0;
4530 }
4531 if (empty($this->fk_parent_line)) {
4532 $this->fk_parent_line = 0;
4533 }
4534 if (empty($this->pa_ht)) {
4535 $this->pa_ht = 0;
4536 }
4537 if (empty($this->ref_ext)) {
4538 $this->ref_ext = '';
4539 }
4540
4541 // if buy price not defined, define buyprice as configured in margin admin
4542 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4543 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4544 if ($result < 0) {
4545 return $result;
4546 } else {
4547 $this->pa_ht = $result;
4548 }
4549 }
4550
4551 // Check parameters
4552 if ($this->product_type < 0) {
4553 return -1;
4554 }
4555
4556 $this->db->begin();
4557
4558 // Insertion dans base de la ligne
4559 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4560 $sql .= ' (fk_commande, fk_parent_line, label, description, qty, ref_ext,';
4561 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4562 $sql .= ' fk_product, product_type, remise_percent, subprice, price, fk_remise_except,';
4563 $sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4564 $sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4565 $sql .= ' fk_unit';
4566 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4567 $sql .= ')';
4568 $sql .= " VALUES (".$this->fk_commande.",";
4569 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4570 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4571 $sql .= " '".$this->db->escape($this->desc)."',";
4572 $sql .= " '".price2num($this->qty)."',";
4573 $sql .= " '".$this->db->escape($this->ref_ext)."',";
4574 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4575 $sql .= " '".price2num($this->tva_tx)."',";
4576 $sql .= " '".price2num($this->localtax1_tx)."',";
4577 $sql .= " '".price2num($this->localtax2_tx)."',";
4578 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4579 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4580 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4581 $sql .= " '".$this->db->escape($this->product_type)."',";
4582 $sql .= " '".price2num($this->remise_percent)."',";
4583 $sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4584 $sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
4585 $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4586 $sql .= ' '.((int) $this->special_code).',';
4587 $sql .= ' '.((int) $this->rang).',';
4588 $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4589 $sql .= ' '.price2num($this->pa_ht).',';
4590 $sql .= " ".((int) $this->info_bits).",";
4591 $sql .= " ".price2num($this->total_ht, 'MT').",";
4592 $sql .= " ".price2num($this->total_tva, 'MT').",";
4593 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4594 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4595 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4596 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4597 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4598 $sql .= ' '.(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit));
4599 $sql .= ", ".(!empty($this->fk_multicurrency) ? ((int) $this->fk_multicurrency) : 'NULL');
4600 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4601 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4602 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4603 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4604 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4605 $sql .= ')';
4606
4607 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4608 $resql = $this->db->query($sql);
4609 if ($resql) {
4610 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4611 $this->rowid = $this->id;
4612
4613 if (!$error) {
4614 $result = $this->insertExtraFields();
4615 if ($result < 0) {
4616 $error++;
4617 }
4618 }
4619
4620 if (!$error && !$notrigger) {
4621 // Call trigger
4622 $result = $this->call_trigger('LINEORDER_INSERT', $user);
4623 if ($result < 0) {
4624 $error++;
4625 }
4626 // End call triggers
4627 }
4628
4629 if (!$error) {
4630 $this->db->commit();
4631 return 1;
4632 }
4633
4634 foreach ($this->errors as $errmsg) {
4635 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4636 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4637 }
4638 $this->db->rollback();
4639 return -1 * $error;
4640 } else {
4641 $this->error = $this->db->error();
4642 $this->db->rollback();
4643 return -2;
4644 }
4645 }
4646
4654 public function update(User $user, $notrigger = 0)
4655 {
4656 global $conf, $langs;
4657
4658 $error = 0;
4659
4660 $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'.
4661
4662 // Clean parameters
4663 if (empty($this->tva_tx)) {
4664 $this->tva_tx = 0;
4665 }
4666 if (empty($this->localtax1_tx)) {
4667 $this->localtax1_tx = 0;
4668 }
4669 if (empty($this->localtax2_tx)) {
4670 $this->localtax2_tx = 0;
4671 }
4672 if (empty($this->localtax1_type)) {
4673 $this->localtax1_type = 0;
4674 }
4675 if (empty($this->localtax2_type)) {
4676 $this->localtax2_type = 0;
4677 }
4678 if (empty($this->qty)) {
4679 $this->qty = 0;
4680 }
4681 if (empty($this->total_localtax1)) {
4682 $this->total_localtax1 = 0;
4683 }
4684 if (empty($this->total_localtax2)) {
4685 $this->total_localtax2 = 0;
4686 }
4687 if (empty($this->marque_tx)) {
4688 $this->marque_tx = 0;
4689 }
4690 if (empty($this->marge_tx)) {
4691 $this->marge_tx = 0;
4692 }
4693 if (empty($this->remise_percent)) {
4694 $this->remise_percent = 0;
4695 }
4696 if (empty($this->remise)) {
4697 $this->remise = 0;
4698 }
4699 if (empty($this->info_bits)) {
4700 $this->info_bits = 0;
4701 }
4702 if (empty($this->special_code)) {
4703 $this->special_code = 0;
4704 }
4705 if (empty($this->product_type)) {
4706 $this->product_type = 0;
4707 }
4708 if (empty($this->fk_parent_line)) {
4709 $this->fk_parent_line = 0;
4710 }
4711 if (empty($this->pa_ht)) {
4712 $this->pa_ht = 0;
4713 }
4714 if (empty($this->ref_ext)) {
4715 $this->ref_ext = '';
4716 }
4717
4718 // if buy price not defined, define buyprice as configured in margin admin
4719 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4720 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
4721 if ($result < 0) {
4722 return $result;
4723 } else {
4724 $this->pa_ht = $result;
4725 }
4726 }
4727
4728 $this->db->begin();
4729
4730 // Mise a jour ligne en base
4731 $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4732 $sql .= " description='".$this->db->escape($this->desc)."'";
4733 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4734 $sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4735 $sql .= " , tva_tx=".price2num($this->tva_tx);
4736 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4737 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4738 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4739 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4740 $sql .= " , qty=".price2num($this->qty);
4741 $sql .= " , ref_ext='".$this->db->escape($this->ref_ext)."'";
4742 $sql .= " , subprice=".price2num($this->subprice);
4743 $sql .= " , remise_percent=".price2num($this->remise_percent);
4744 $sql .= " , price=".price2num($this->price); // TODO A virer
4745 $sql .= " , remise=".price2num($this->remise); // TODO A virer
4746 if (empty($this->skip_update_total)) {
4747 $sql .= " , total_ht=".price2num($this->total_ht);
4748 $sql .= " , total_tva=".price2num($this->total_tva);
4749 $sql .= " , total_ttc=".price2num($this->total_ttc);
4750 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
4751 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
4752 }
4753 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4754 $sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4755 $sql .= " , info_bits=".((int) $this->info_bits);
4756 $sql .= " , special_code=".((int) $this->special_code);
4757 $sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4758 $sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4759 $sql .= " , product_type=".$this->product_type;
4760 $sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4761 if (!empty($this->rang)) {
4762 $sql .= ", rang=".((int) $this->rang);
4763 }
4764 $sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4765
4766 // Multicurrency
4767 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4768 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4769 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4770 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4771
4772 $sql .= " WHERE rowid = ".((int) $this->rowid);
4773
4774 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4775 $resql = $this->db->query($sql);
4776 if ($resql) {
4777 if (!$error) {
4778 $this->id = $this->rowid;
4779 $result = $this->insertExtraFields();
4780 if ($result < 0) {
4781 $error++;
4782 }
4783 }
4784
4785 if (!$error && !$notrigger) {
4786 // Call trigger
4787 $result = $this->call_trigger('LINEORDER_MODIFY', $user);
4788 if ($result < 0) {
4789 $error++;
4790 }
4791 // End call triggers
4792 }
4793
4794 if (!$error) {
4795 $this->db->commit();
4796 return 1;
4797 }
4798
4799 foreach ($this->errors as $errmsg) {
4800 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4801 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4802 }
4803 $this->db->rollback();
4804 return -1 * $error;
4805 } else {
4806 $this->error = $this->db->error();
4807 $this->db->rollback();
4808 return -2;
4809 }
4810 }
4811
4812 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4819 public function update_total()
4820 {
4821 // phpcs:enable
4822 $this->db->begin();
4823
4824 // Clean parameters
4825 if (empty($this->total_localtax1)) {
4826 $this->total_localtax1 = 0;
4827 }
4828 if (empty($this->total_localtax2)) {
4829 $this->total_localtax2 = 0;
4830 }
4831
4832 // Mise a jour ligne en base
4833 $sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4834 $sql .= " total_ht='".price2num($this->total_ht)."'";
4835 $sql .= ",total_tva='".price2num($this->total_tva)."'";
4836 $sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4837 $sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4838 $sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4839 $sql .= " WHERE rowid = ".((int) $this->rowid);
4840
4841 dol_syslog("OrderLine::update_total", LOG_DEBUG);
4842
4843 $resql = $this->db->query($sql);
4844 if ($resql) {
4845 $this->db->commit();
4846 return 1;
4847 } else {
4848 $this->error = $this->db->error();
4849 $this->db->rollback();
4850 return -2;
4851 }
4852 }
4853}
$object ref
Definition info.php:78
Class to manage customers orders.
getNbOfServicesLines()
Return number of line with type service.
getNbOfShipments()
Count number of shipments for this order.
deleteline($user=null, $lineid=0, $id=0)
Delete an order line.
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.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
showDelay()
Show the customer delayed info.
set_date($user, $date, $notrigger=0)
Set the order date.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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.
liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
Return list of orders (eventuelly filtered on a user) into an array.
getLibStatut($mode)
Return status label of Order.
hasDelay()
Is the sales order delayed?
set_remise_absolue($user, $remise, $notrigger=0)
Set a fixed amount discount.
const STATUS_CLOSED
Closed (Sent, billed or not)
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=0, $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $ref_ext='', $rang=0)
Update a line in database.
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.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
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...
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)
Adding 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.
stock_array($filtre_statut=self::STATUS_CANCELED)
Return a array with the pending stock by product.
cloture($user, $notrigger=0)
Close order.
cancel($idwarehouse=-1)
Cancel an order If stock is decremented on order validation, we must reincrement it.
classifyBilled(User $user, $notrigger=0)
Classify the order as invoiced.
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=0, $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)
info($id)
Charge les informations d'ordre info dans l'objet commande.
const STATUS_VALIDATED
Validated status.
countNbOfShipments()
Returns an array with expeditions lines number.
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.
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.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
fetch_project()
Load the project with id $this->fk_project into this->project.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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 third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
print $langs trans("Ref").' m m m statut
Definition index.php:152
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($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:62
dol_delete_preview($object)
Delete all preview files linked to object instance.
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_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
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.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
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...
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...
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e rowid
Definition invoice.php:1632
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
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:86