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