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