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