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