dolibarr 22.0.5
fournisseur.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-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
8 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
13 * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2021 Josep Lluís Amador <joseplluis@lliuretic.cat>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 Solution Libre SAS <contact@solution-libre.fr>
17 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
18 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
19 * Copyright (C) 2025 Noé Cendrier <noe.cendrier@altairis.fr>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
42require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
43require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.orderline.class.php';
44require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
45if (isModEnabled('productbatch')) {
46 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
47}
48
49
54{
58 public $element = 'order_supplier';
59
63 public $table_element = 'commande_fournisseur';
64
68 public $table_element_line = 'commande_fournisseurdet';
69
73 public $class_element_line = 'CommandeFournisseurLigne';
74
78 public $fk_element = 'fk_commande';
79
83 public $picto = 'supplier_order';
84
89 public $restrictiononfksoc = 1;
90
94 protected $table_ref_field = 'ref';
95
99 public $id;
100
104 public $ref;
105
109 public $ref_supplier;
110
116 public $ref_fourn;
117
123 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
124 // -> 7=Canceled/Never received -> (reopen) 3=Process running
125 // -> 6=Canceled -> (reopen) 2=Approved
126 // -> 9=Refused -> (reopen) 1=Validated
127 // Note: billed or not is on another field "billed"
128
132 public $billed;
133
137 public $socid;
138
142 public $fourn_id;
143
147 public $date;
148
152 public $date_valid;
153
157 public $date_approve;
158
163 public $date_approve2;
164
168 public $date_commande;
169
174 public $remise_percent;
178 public $methode_commande_id;
182 public $methode_commande;
183
187 public $delivery_date;
188
192 public $total_ht;
193
197 public $total_tva;
198
202 public $total_localtax1;
203
207 public $total_localtax2;
208
212 public $total_ttc;
213
217 public $source;
218
222 public $fk_project;
223
227 public $cond_reglement_id;
228
232 public $cond_reglement_code;
233
237 public $cond_reglement_label;
238
242 public $cond_reglement_doc;
243
247 public $fk_account;
248
252 public $mode_reglement_id;
253
257 public $mode_reglement_code;
258
262 public $mode_reglement;
263
267 public $user_author_id;
268
272 public $user_approve_id;
273
278 public $user_approve_id2;
279
283 public $refuse_note;
284
288 public $extraparams = array();
289
293 public $lines = array();
294
298 public $line;
299
303 public $origin;
307 public $origin_id;
308 public $linked_objects = array();
309
313 public $date_lim_reglement;
317 public $receptions = array();
318
319 // Multicurrency
323 public $fk_multicurrency;
324
328 public $multicurrency_code;
329
333 public $multicurrency_tx;
334
338 public $multicurrency_total_ht;
339
343 public $multicurrency_total_tva;
344
348 public $multicurrency_total_ttc;
349
377 public $fields = array(
378 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
379 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
380 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 35),
381 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
382 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
383 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
384 'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
385 'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
386 'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
387 'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
388 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
389 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
390 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
391 'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
392 'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
393 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
394 'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
395 'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
396 'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
397 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
398 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
399 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
400 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
401 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
402 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
403 'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
404 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
405 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
406 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
407 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
408 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
409 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
410 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
411 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
412 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
413 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
414 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
415 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
416 'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
417 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
418 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
419 'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
420 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
421 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
422 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
423 );
424
425
429 const STATUS_DRAFT = 0;
430
435
440
445
450
455
460
465
469 const STATUS_REFUSED = 9;
470
471
476
482 public function __construct($db)
483 {
484 $this->db = $db;
485
486 $this->ismultientitymanaged = 1;
487 }
488
489
497 public function fetch($id, $ref = '')
498 {
499 // Check parameters
500 if (empty($id) && empty($ref)) {
501 return -1;
502 }
503
504 $sql = "SELECT c.rowid, c.entity, c.ref, ref_supplier, c.fk_soc, c.fk_statut as status, c.amount_ht, c.total_ht, c.total_ttc, c.total_tva,";
505 $sql .= " c.localtax1, c.localtax2, ";
506 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
507 $sql .= " c.fk_user_author as user_author_id, c.fk_user_valid as user_validation_id, c.fk_user_approve as user_approve_id, c.fk_user_approve2 as user_approve_id2,";
508 $sql .= " c.date_commande as date_commande, c.date_livraison as delivery_date, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_projet as fk_project, c.remise_percent, c.source, c.fk_input_method,";
509 $sql .= " c.fk_account,";
510 $sql .= " c.note_private, c.note_public, c.model_pdf, c.last_main_doc, c.extraparams, c.billed,";
511 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
512 $sql .= " cm.libelle as methode_commande,";
513 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
514 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
515 $sql .= ', c.fk_incoterms, c.location_incoterms';
516 $sql .= ', c.last_main_doc';
517 $sql .= ', i.libelle as label_incoterms';
518 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
519 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
520 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
521 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
522 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
523
524 if (empty($id)) {
525 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
526 } else {
527 $sql .= " WHERE c.rowid=".((int) $id);
528 }
529
530 if ($ref) {
531 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
532 }
533
534 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
535 $resql = $this->db->query($sql);
536 if ($resql) {
537 $obj = $this->db->fetch_object($resql);
538 if (!$obj) {
539 $this->error = 'Bill with id '.$id.' not found';
540 dol_syslog(get_class($this).'::fetch '.$this->error);
541 return 0;
542 }
543
544 $this->id = $obj->rowid;
545 $this->entity = $obj->entity;
546
547 $this->ref = $obj->ref;
548 $this->ref_supplier = $obj->ref_supplier;
549
550 $this->socid = $obj->fk_soc;
551 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
552
553 $this->fourn_id = $obj->fk_soc;
554 $this->statut = $obj->status; // deprecated
555 $this->status = $obj->status;
556 $this->billed = $obj->billed;
557 $this->last_main_doc = $obj->last_main_doc;
558 $this->user_author_id = $obj->user_author_id;
559 $this->user_validation_id = $obj->user_validation_id;
560 $this->user_approve_id = $obj->user_approve_id;
561 $this->user_approve_id2 = $obj->user_approve_id2;
562 $this->total_ht = $obj->total_ht;
563 $this->total_tva = $obj->total_tva;
564 $this->total_localtax1 = $obj->localtax1;
565 $this->total_localtax2 = $obj->localtax2;
566 $this->total_ttc = $obj->total_ttc;
567 $this->date_creation = $this->db->jdate($obj->date_creation);
568 $this->date_valid = $this->db->jdate($obj->date_valid);
569 $this->date_approve = $this->db->jdate($obj->date_approve);
570 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
571 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
572 if (isset($obj->date_commande)) {
573 $this->date = $this->date_commande;
574 } else {
575 $this->date = $this->date_creation;
576 }
577 $this->delivery_date = $this->db->jdate($obj->delivery_date);
578 $this->remise_percent = $obj->remise_percent;
579 $this->methode_commande_id = $obj->fk_input_method;
580 $this->methode_commande = $obj->methode_commande;
581
582 $this->source = $obj->source;
583 $this->fk_project = $obj->fk_project;
584 $this->cond_reglement_id = $obj->fk_cond_reglement;
585 $this->cond_reglement_code = $obj->cond_reglement_code;
586 $this->cond_reglement_label = $obj->cond_reglement_label;
587 $this->cond_reglement_doc = $obj->cond_reglement_doc;
588 $this->fk_account = $obj->fk_account;
589 $this->mode_reglement_id = $obj->fk_mode_reglement;
590 $this->mode_reglement_code = $obj->mode_reglement_code;
591 $this->mode_reglement = $obj->mode_reglement_libelle;
592 $this->note = $obj->note_private; // deprecated
593 $this->note_private = $obj->note_private;
594 $this->note_public = $obj->note_public;
595 $this->model_pdf = $obj->model_pdf;
596 $this->last_main_doc = $obj->last_main_doc;
597
598 //Incoterms
599 $this->fk_incoterms = $obj->fk_incoterms;
600 $this->location_incoterms = $obj->location_incoterms;
601 $this->label_incoterms = $obj->label_incoterms;
602
603 // Multicurrency
604 $this->fk_multicurrency = $obj->fk_multicurrency;
605 $this->multicurrency_code = $obj->multicurrency_code;
606 $this->multicurrency_tx = $obj->multicurrency_tx;
607 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
608 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
609 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
610
611 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
612
613 $this->db->free($resql);
614
615 // Retrieve all extrafield
616 // fetch optionals attributes and labels
617 $this->fetch_optionals();
618
619 // Lines
620 $result = $this->fetch_lines();
621
622 if ($result < 0) {
623 return -1;
624 } else {
625 return 1;
626 }
627 } else {
628 $this->error = $this->db->error()." sql=".$sql;
629 return -1;
630 }
631 }
632
633 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
640 public function fetch_lines($only_product = 0)
641 {
642 // phpcs:enable
643
644 $this->lines = array();
645
646 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
647 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
648 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
649 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
650 $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch, p.barcode as product_barcode,";
651 $sql .= " l.fk_unit, l.extraparams,";
652 $sql .= " l.date_start, l.date_end,";
653 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
654 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
655 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
656 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
657 if ($only_product) {
658 $sql .= ' AND p.fk_product_type = 0';
659 }
660 $sql .= " ORDER BY l.rang, l.rowid";
661 //print $sql;
662
663 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
664
665 $result = $this->db->query($sql);
666 if ($result) {
667 $num = $this->db->num_rows($result);
668 $i = 0;
669
670 while ($i < $num) {
671 $objp = $this->db->fetch_object($result);
672
673 $line = new CommandeFournisseurLigne($this->db);
674
675 $line->id = $objp->rowid;
676 $line->fk_commande = $objp->fk_commande;
677 $line->desc = $objp->description;
678 $line->description = $objp->description;
679 $line->qty = $objp->qty;
680 $line->tva_tx = $objp->tva_tx;
681 $line->localtax1_tx = $objp->localtax1_tx;
682 $line->localtax2_tx = $objp->localtax2_tx;
683 $line->localtax1_type = $objp->localtax1_type;
684 $line->localtax2_type = $objp->localtax2_type;
685 $line->subprice = $objp->subprice;
686 $line->pu_ht = $objp->subprice;
687 $line->remise_percent = $objp->remise_percent;
688
689 $line->vat_src_code = $objp->vat_src_code;
690 $line->total_ht = $objp->total_ht;
691 $line->total_tva = $objp->total_tva;
692 $line->total_localtax1 = $objp->total_localtax1;
693 $line->total_localtax2 = $objp->total_localtax2;
694 $line->total_ttc = $objp->total_ttc;
695 $line->product_type = $objp->product_type;
696
697 $line->fk_product = $objp->fk_product;
698
699 $line->libelle = $objp->product_label; // deprecated
700 $line->product_label = $objp->product_label;
701 $line->product_desc = $objp->product_desc;
702 $line->product_tobatch = $objp->product_tobatch;
703 $line->product_barcode = $objp->product_barcode;
704
705 $line->ref = $objp->product_ref; // Ref of product
706 $line->product_ref = $objp->product_ref; // Ref of product
707 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
708 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
709
710 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
711 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
712 // Move this into another method and call it when required.
713
714 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
715 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
716 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
717 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
718 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
719 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
720 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
721 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
722 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
723 $sqlsearchpackage .= " LIMIT 1";
724
725 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
726 if ($resqlsearchpackage) {
727 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
728 if ($objsearchpackage) {
729 $line->fk_fournprice = $objsearchpackage->rowid;
730 $line->packaging = (float) $objsearchpackage->packaging;
731 }
732 } else {
733 $this->error = $this->db->lasterror();
734 return -1;
735 }
736 }
737
738 $line->date_start = $this->db->jdate($objp->date_start);
739 $line->date_end = $this->db->jdate($objp->date_end);
740 $line->fk_unit = $objp->fk_unit;
741
742 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
743
744 // Multicurrency
745 $line->fk_multicurrency = $objp->fk_multicurrency;
746 $line->multicurrency_code = $objp->multicurrency_code;
747 $line->multicurrency_subprice = $objp->multicurrency_subprice;
748 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
749 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
750 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
751
752 $line->info_bits = $objp->info_bits;
753 $line->special_code = $objp->special_code;
754 $line->fk_parent_line = $objp->fk_parent_line;
755
756 $line->rang = $objp->rang;
757
758 // Retrieve all extrafield
759 // fetch optionals attributes and labels
760 $line->fetch_optionals();
761
762 $this->lines[$i] = $line;
763
764 $i++;
765 }
766 $this->db->free($result);
767
768 return $num;
769 } else {
770 $this->error = $this->db->error()." sql=".$sql;
771 return -1;
772 }
773 }
774
783 public function valid($user, $idwarehouse = 0, $notrigger = 0)
784 {
785 global $conf;
786 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
787
788 $error = 0;
789
790 dol_syslog(get_class($this)."::valid");
791 $result = 0;
792 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
793 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
794 $this->db->begin();
795
796 // Definition of supplier order numbering model name
797 $soc = new Societe($this->db);
798 $soc->fetch($this->fourn_id);
799
800 // Check if object has a temporary ref
801 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
802 $num = $this->getNextNumRef($soc);
803 } else {
804 $num = $this->ref;
805 }
806 $this->newref = dol_sanitizeFileName($num);
807
808 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
809 $sql .= " SET ref='".$this->db->escape($num)."',";
810 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
811 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
812 $sql .= " fk_user_valid = ".((int) $user->id);
813 $sql .= " WHERE rowid = ".((int) $this->id);
814 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
815
816 $resql = $this->db->query($sql);
817 if (!$resql) {
818 dol_print_error($this->db);
819 $error++;
820 }
821
822 if (!$error && !$notrigger) {
823 // Call trigger
824 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
825 if ($result < 0) {
826 $error++;
827 }
828 // End call triggers
829 }
830
831 if (!$error) {
832 $this->oldref = $this->ref;
833
834 // Rename directory if dir was a temporary ref
835 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
836 // Now we rename also files into index
837 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
838 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
839 $resql = $this->db->query($sql);
840 if (!$resql) {
841 $error++;
842 $this->error = $this->db->lasterror();
843 }
844 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
845 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
846 $resql = $this->db->query($sql);
847 if (!$resql) {
848 $error++;
849 $this->error = $this->db->lasterror();
850 }
851
852 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
853 $oldref = dol_sanitizeFileName($this->ref);
854 $newref = dol_sanitizeFileName($num);
855 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
856 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
857 if (!$error && file_exists($dirsource)) {
858 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
859
860 if (@rename($dirsource, $dirdest)) {
861 dol_syslog("Rename ok");
862 // Rename docs starting with $oldref with $newref
863 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
864 foreach ($listoffiles as $fileentry) {
865 $dirsource = $fileentry['name'];
866 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
867 $dirsource = $fileentry['path'].'/'.$dirsource;
868 $dirdest = $fileentry['path'].'/'.$dirdest;
869 @rename($dirsource, $dirdest);
870 }
871 }
872 }
873 }
874 }
875
876 if (!$error) {
877 $result = 1;
879 $this->statut = self::STATUS_VALIDATED; // deprecated
880 $this->ref = $num;
881 }
882
883 if (!$error) {
884 $this->db->commit();
885 return 1;
886 } else {
887 $this->db->rollback();
888 return -1;
889 }
890 } else {
891 $this->error = 'NotAuthorized';
892 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
893 return -1;
894 }
895 }
896
903 public function getLibStatut($mode = 0)
904 {
905 return $this->LibStatut(isset($this->status) ? $this->status : $this->statut, $mode, $this->billed);
906 }
907
908 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
917 public function LibStatut($status, $mode = 0, $billed = 0)
918 {
919 // phpcs:enable
920 global $langs, $hookmanager;
921
922 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
923 $langs->load('orders');
924
925 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
926 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
927 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
928 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
929 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
930 } else {
931 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
932 }
933 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
934 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
935 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
936 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
937 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
938
939 // List of language codes for status
940 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
941 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
942 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
943 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
944 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
945 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
946 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
947 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
948 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
949 }
950
951 $statustrans = array(
952 0 => 'status0',
953 1 => 'status1b',
954 2 => 'status1',
955 3 => 'status4',
956 4 => 'status4b',
957 5 => 'status6',
958 6 => 'status9',
959 7 => 'status9',
960 9 => 'status9',
961 );
962
963 $statusClass = 'status0';
964 if (!empty($statustrans[$status])) {
965 $statusClass = $statustrans[$status];
966 }
967
968 $billedtext = '';
969 if ($billed) {
970 $billedtext = ' - '.$langs->trans("Billed");
971 }
972 if ($status == 5 && $billed) {
973 $statusClass = 'status6';
974 }
975
976 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
977 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
978
979 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
980 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
981 if ($reshook > 0) {
982 return $hookmanager->resPrint;
983 }
984
985 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
986 }
987
994 public function getTooltipContentArray($params)
995 {
996 global $conf, $langs, $user;
997
998 $langs->loadLangs(['bills', 'orders']);
999
1000 $datas = [];
1001 $nofetch = !empty($params['nofetch']);
1002
1003 if ($user->hasRight("fournisseur", "commande", "read")) {
1004 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
1005 if ($this->status) {
1006 $datas['picto'] .= ' '.$this->getLibStatut(5);
1007 }
1008 if (!empty($this->ref)) {
1009 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1010 }
1011 if (!empty($this->ref_supplier)) {
1012 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1013 }
1014 if (!$nofetch) {
1015 $langs->load('companies');
1016 if (empty($this->thirdparty)) {
1017 $this->fetch_thirdparty();
1018 }
1019 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
1020 }
1021 if (!empty($this->total_ht)) {
1022 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1023 }
1024 if (!empty($this->total_tva)) {
1025 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1026 }
1027 if (!empty($this->total_ttc)) {
1028 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1029 }
1030 if (!empty($this->date)) {
1031 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
1032 }
1033 if (!empty($this->delivery_date)) {
1034 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
1035 }
1036 }
1037 return $datas;
1038 }
1039
1050 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1051 {
1052 global $langs, $user, $hookmanager;
1053
1054 $result = '';
1055 $params = [
1056 'id' => $this->id,
1057 'objecttype' => $this->element,
1058 'option' => $option,
1059 'nofetch' => 1
1060 ];
1061 $classfortooltip = 'classfortooltip';
1062 $dataparams = '';
1063 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1064 $classfortooltip = 'classforajaxtooltip';
1065 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1066 $label = '';
1067 } else {
1068 $label = implode($this->getTooltipContentArray($params));
1069 }
1070
1071 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1072
1073 if ($option !== 'nolink') {
1074 // Add param to save lastsearch_values or not
1075 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1076 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1077 $add_save_lastsearch_values = 1;
1078 }
1079 if ($add_save_lastsearch_values) {
1080 $url .= '&save_lastsearch_values=1';
1081 }
1082 }
1083
1084 $linkclose = '';
1085 if (empty($notooltip)) {
1086 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1087 $label = $langs->trans("ShowOrder");
1088 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1089 }
1090 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
1091 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1092 }
1093
1094 $linkstart = '<a href="'.$url.'"';
1095 $linkstart .= $linkclose.'>';
1096 $linkend = '</a>';
1097
1098 $result .= $linkstart;
1099 if ($withpicto) {
1100 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1101 }
1102 if ($withpicto != 2) {
1103 $result .= $this->ref;
1104 }
1105 $result .= $linkend;
1106
1107 if ($addlinktonotes) {
1108 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1109 if ($txttoshow) {
1110 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1111 $result .= ' <span class="note inline-block">';
1112 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1113 $result .= img_picto('', 'note');
1114 $result .= '</a>';
1115 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1116 //$result.='</a>';
1117 $result .= '</span>';
1118 }
1119 }
1120
1121 global $action;
1122 $hookmanager->initHooks(array($this->element . 'dao'));
1123 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1124 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1125 if ($reshook > 0) {
1126 $result = $hookmanager->resPrint;
1127 } else {
1128 $result .= $hookmanager->resPrint;
1129 }
1130 return $result;
1131 }
1132
1133
1141 public function getNextNumRef($soc)
1142 {
1143 global $langs, $conf;
1144 $langs->load("orders");
1145
1146 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1147 $mybool = false;
1148
1149 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1150 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1151
1152 // Include file with class
1153 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1154
1155 foreach ($dirmodels as $reldir) {
1156 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1157
1158 // Load file with numbering class (if found)
1159 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1160 }
1161
1162 if (!$mybool) {
1163 dol_print_error(null, "Failed to include file ".$file);
1164 return '';
1165 }
1166
1167 $obj = new $classname();
1168 '@phan-var-force ModeleNumRefSuppliersOrders $obj';
1170 $numref = $obj->getNextValue($soc, $this);
1171
1172 if ($numref != "") {
1173 return $numref;
1174 } else {
1175 $this->error = $obj->error;
1176 return -1;
1177 }
1178 } else {
1179 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1180 return -2;
1181 }
1182 }
1183
1190 public function classifyBilled(User $user)
1191 {
1192 $error = 0;
1193
1194 if ($this->billed) {
1195 return 0;
1196 }
1197
1198 $this->db->begin();
1199
1200 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1201 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1202
1203 if ($this->db->query($sql)) {
1204 if (!$error) {
1205 // Call trigger
1206 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1207 if ($result < 0) {
1208 $error++;
1209 }
1210 // End call triggers
1211 }
1212
1213 if (!$error) {
1214 $this->billed = 1;
1215
1216 $this->db->commit();
1217 return 1;
1218 } else {
1219 $this->db->rollback();
1220 return -1;
1221 }
1222 } else {
1223 dol_print_error($this->db);
1224
1225 $this->db->rollback();
1226 return -1;
1227 }
1228 }
1229
1230
1237 public function classifyUnBilled(User $user)
1238 {
1239 if (empty($this->billed)) {
1240 return 0;
1241 }
1242
1243 $this->db->begin();
1244
1245 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 0';
1246 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1247 ;
1248
1249 if (!$this->db->query($sql)) {
1250 dol_print_error($this->db);
1251 $this->db->rollback();
1252 return -1;
1253 }
1254
1255 // Call trigger
1256 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_UNBILLED', $user);
1257 if ($result < 0) {
1258 $this->db->rollback();
1259 return -1;
1260 }
1261 // End call triggers
1262
1263 $this->billed = 1;
1264 $this->db->commit();
1265 return 1;
1266 }
1267
1268
1277 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1278 {
1279 global $langs, $conf;
1280 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1281
1282 $error = 0;
1283
1284 dol_syslog(get_class($this)."::approve");
1285
1286 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1287 $now = dol_now();
1288
1289 $this->db->begin();
1290
1291 // Definition of order numbering model name
1292 $soc = new Societe($this->db);
1293 $soc->fetch($this->fourn_id);
1294
1295 // Check if object has a temporary ref
1296 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1297 $num = $this->getNextNumRef($soc);
1298 } else {
1299 $num = $this->ref;
1300 }
1301 $this->newref = dol_sanitizeFileName($num);
1302
1303 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1304 $movetoapprovestatus = true;
1305 $comment = '';
1306
1307 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1308 $sql .= " SET ref='".$this->db->escape($num)."',";
1309 if (empty($secondlevel)) { // standard or first level approval
1310 $sql .= " date_approve='".$this->db->idate($now)."',";
1311 $sql .= " fk_user_approve = ".((int) $user->id);
1312 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= getDolGlobalFloat('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED')) {
1313 if (empty($this->user_approve_id2)) {
1314 $movetoapprovestatus = false; // second level approval not done
1315 $comment = ' (first level)';
1316 }
1317 }
1318 } else { // request a second level approval
1319 $sql .= " date_approve2='".$this->db->idate($now)."',";
1320 $sql .= " fk_user_approve2 = ".((int) $user->id);
1321 if (empty($this->user_approve_id)) {
1322 $movetoapprovestatus = false; // first level approval not done
1323 }
1324 $comment = ' (second level)';
1325 }
1326 // If double approval is required and first approval, we keep status to 1 = validated
1327 if ($movetoapprovestatus) {
1328 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1329 } else {
1330 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1331 }
1332 $sql .= " WHERE rowid = ".((int) $this->id);
1333 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1334
1335 if ($this->db->query($sql)) {
1336 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1337 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1338 if ($result < 0 && $result != -2) { // -2 means already exists
1339 $error++;
1340 }
1341 }
1342
1343 // If stock is incremented on validate order, we must increment it
1344 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1345 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1346 $langs->load("agenda");
1347
1348 $cpt = count($this->lines);
1349 for ($i = 0; $i < $cpt; $i++) {
1350 // Product with reference
1351 if ($this->lines[$i]->fk_product > 0) {
1352 $this->line = $this->lines[$i];
1353 $mouvP = new MouvementStock($this->db);
1354 $mouvP->origin = &$this;
1355 $mouvP->setOrigin($this->element, $this->id);
1356 // We decrement stock of product (and sub-products)
1357 $up_ht_disc = $this->lines[$i]->subprice;
1358 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1359 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1360 }
1361 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1362 if ($result < 0) {
1363 $error++;
1364 }
1365 unset($this->line);
1366 }
1367 }
1368 }
1369
1370 if (!$error) {
1371 // Call trigger
1372 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1373 if ($result < 0) {
1374 $error++;
1375 }
1376 // End call triggers
1377 }
1378
1379 if (!$error) {
1380 $this->ref = $this->newref;
1381
1382 if ($movetoapprovestatus) {
1383 $this->statut = self::STATUS_ACCEPTED; // deprecated
1385 } else {
1386 $this->statut = self::STATUS_VALIDATED; // deprecated
1388 }
1389 if (empty($secondlevel)) { // standard or first level approval
1390 $this->date_approve = $now;
1391 $this->user_approve_id = $user->id;
1392 } else { // request a second level approval
1393 $this->date_approve2 = $now;
1394 $this->user_approve_id2 = $user->id;
1395 }
1396
1397 $this->db->commit();
1398 return 1;
1399 } else {
1400 $this->db->rollback();
1401 return -1;
1402 }
1403 } else {
1404 $this->db->rollback();
1405 $this->error = $this->db->lasterror();
1406 return -1;
1407 }
1408 } else {
1409 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1410 }
1411 return -1;
1412 }
1413
1420 public function refuse($user)
1421 {
1422 global $conf, $langs;
1423
1424 $error = 0;
1425
1426 dol_syslog(get_class($this)."::refuse");
1427 $result = 0;
1428 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1429 $this->db->begin();
1430
1431 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1432 $sql .= " WHERE rowid = ".((int) $this->id);
1433
1434 if ($this->db->query($sql)) {
1435 $result = 0;
1436
1437 if ($error == 0) {
1438 // Call trigger
1439 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1440 if ($result < 0) {
1441 $error++;
1442 $this->db->rollback();
1443 } else {
1444 $this->db->commit();
1445 }
1446 // End call triggers
1447 }
1448 } else {
1449 $this->db->rollback();
1450 $this->error = $this->db->lasterror();
1451 dol_syslog(get_class($this)."::refuse Error -1");
1452 $result = -1;
1453 }
1454 } else {
1455 dol_syslog(get_class($this)."::refuse Not Authorized");
1456 }
1457 return $result;
1458 }
1459
1460 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1469 public function Cancel($user, $idwarehouse = -1)
1470 {
1471 // phpcs:enable
1472 global $langs, $conf;
1473
1474 $error = 0;
1475
1476 //dol_syslog("CommandeFournisseur::Cancel");
1477 $result = 0;
1478 if ($user->hasRight("fournisseur", "commande", "commander")) {
1479 $statut = self::STATUS_CANCELED;
1480
1481 $this->db->begin();
1482
1483 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1484 $sql .= " WHERE rowid = ".((int) $this->id);
1485 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1486 if ($this->db->query($sql)) {
1487 $result = 0;
1488
1489 // Call trigger
1490 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1491 if ($result < 0) {
1492 $error++;
1493 }
1494 // End call triggers
1495
1496 if ($error == 0) {
1497 $this->db->commit();
1498 return 1;
1499 } else {
1500 $this->db->rollback();
1501 return -1;
1502 }
1503 } else {
1504 $this->db->rollback();
1505 $this->error = $this->db->lasterror();
1506 dol_syslog(get_class($this)."::cancel ".$this->error);
1507 return -1;
1508 }
1509 } else {
1510 dol_syslog(get_class($this)."::cancel Not Authorized");
1511 return -1;
1512 }
1513 }
1514
1524 public function commande($user, $date, $methode, $comment = '')
1525 {
1526 global $langs;
1527 dol_syslog(get_class($this)."::commande");
1528 $error = 0;
1529 if ($user->hasRight("fournisseur", "commande", "commander")) {
1530 $this->db->begin();
1531
1532 $newnoteprivate = $this->note_private;
1533 if ($comment) {
1534 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1535 }
1536
1537 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1538 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1539 $sql .= " note_private='".$this->db->escape((string) $newnoteprivate)."'";
1540 $sql .= " WHERE rowid=".((int) $this->id);
1541
1542 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1543 if ($this->db->query($sql)) {
1544 $this->statut = self::STATUS_ORDERSENT; // deprecated
1546 $this->methode_commande_id = $methode;
1547 $this->date_commande = $date;
1548 $this->context['comments'] = $comment;
1549
1550 // Call trigger
1551 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1552 if ($result < 0) {
1553 $error++;
1554 }
1555 // End call triggers
1556 } else {
1557 $error++;
1558 $this->error = $this->db->lasterror();
1559 $this->errors[] = $this->db->lasterror();
1560 }
1561
1562 if (!$error) {
1563 $this->db->commit();
1564 } else {
1565 $this->db->rollback();
1566 }
1567 } else {
1568 $error++;
1569 $this->error = $langs->trans('NotAuthorized');
1570 $this->errors[] = $langs->trans('NotAuthorized');
1571 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1572 }
1573
1574 return ($error ? -1 : 1);
1575 }
1576
1584 public function create($user, $notrigger = 0)
1585 {
1586 global $langs, $conf, $hookmanager;
1587
1588 $this->db->begin();
1589
1590 $error = 0;
1591 $now = dol_now();
1592
1593 // set tmp vars
1594 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1595 if (empty($date)) {
1596 $date = $now;
1597 }
1598 $delivery_date = $this->delivery_date;
1599
1600 // Clean parameters
1601 if (empty($this->source)) {
1602 $this->source = 0;
1603 }
1604
1605 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1606 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1607 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1608 } else {
1609 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1610 }
1611 if (empty($this->fk_multicurrency)) {
1612 $this->multicurrency_code = $conf->currency;
1613 $this->fk_multicurrency = 0;
1614 $this->multicurrency_tx = 1;
1615 }
1616 $this->entity = setEntity($this);
1617
1618 // We set order into draft status
1619 $this->statut = self::STATUS_DRAFT; // deprecated
1620 $this->status = self::STATUS_DRAFT;
1621
1622 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1623 $sql .= "ref";
1624 $sql .= ", ref_supplier";
1625 $sql .= ", note_private";
1626 $sql .= ", note_public";
1627 $sql .= ", entity";
1628 $sql .= ", fk_soc";
1629 $sql .= ", fk_projet";
1630 $sql .= ", date_creation";
1631 $sql .= ", date_livraison";
1632 $sql .= ", fk_user_author";
1633 $sql .= ", fk_statut";
1634 $sql .= ", source";
1635 $sql .= ", model_pdf";
1636 $sql .= ", fk_mode_reglement";
1637 $sql .= ", fk_cond_reglement";
1638 $sql .= ", fk_account";
1639 $sql .= ", fk_incoterms, location_incoterms";
1640 $sql .= ", fk_multicurrency";
1641 $sql .= ", multicurrency_code";
1642 $sql .= ", multicurrency_tx";
1643 $sql .= ") ";
1644 $sql .= " VALUES (";
1645 $sql .= "'(PROV)'";
1646 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1647 $sql .= ", '".$this->db->escape($this->note_private)."'";
1648 $sql .= ", '".$this->db->escape($this->note_public)."'";
1649 $sql .= ", ".((int) $this->entity);
1650 $sql .= ", ".((int) $this->socid);
1651 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1652 $sql .= ", '".$this->db->idate($date)."'";
1653 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1654 $sql .= ", ".((int) $user->id);
1655 $sql .= ", ".self::STATUS_DRAFT;
1656 $sql .= ", ".((int) $this->source);
1657 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1658 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1659 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1660 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1661 $sql .= ", ".(int) $this->fk_incoterms;
1662 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1663 $sql .= ", ".(int) $this->fk_multicurrency;
1664 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1665 $sql .= ", ".(float) $this->multicurrency_tx;
1666 $sql .= ")";
1667
1668 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1669 if ($this->db->query($sql)) {
1670 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1671
1672 if ($this->id) {
1673 $num = count($this->lines);
1674
1675 // insert products details into database
1676 for ($i = 0; $i < $num; $i++) {
1677 $line = $this->lines[$i];
1678 if (!is_object($line)) {
1679 $line = (object) $line;
1680 }
1681
1682 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1683
1684 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1685 $result = $this->addline(
1686 $line->desc,
1687 $line->subprice,
1688 $line->qty,
1689 $line->tva_tx,
1690 $line->localtax1_tx ?? 0.0,
1691 $line->localtax2_tx ?? 0.0,
1692 $line->fk_product,
1693 0,
1694 $line->ref_fourn ?? '', // $line->ref_fourn comes from field ref into table of lines. Value may ba a ref that does not exists anymore, so we first try with value of product
1695 $line->remise_percent,
1696 'HT',
1697 0,
1698 $line->product_type,
1699 $line->info_bits ?? 0,
1700 0,
1701 $line->date_start ?? null,
1702 $line->date_end ?? null,
1703 $line->array_options ?? [],
1704 $line->fk_unit ?? null,
1705 $line->multicurrency_subprice ?? 0, // pu_ht_devise
1706 $line->origin ?? '', // origin
1707 $line->origin_id ?? 0, // origin_id
1708 $line->rang ?? -1, // rang
1709 $line->special_code ?? 0
1710 );
1711 if ($result < 0) {
1712 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1713 $this->db->rollback();
1714 return -1;
1715 }
1716 }
1717
1718 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1719 $sql .= " SET ref='(PROV".$this->id.")'";
1720 $sql .= " WHERE rowid=".((int) $this->id);
1721
1722 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1723 if ($this->db->query($sql)) {
1724 // Add link with price request and supplier order
1725 if ($this->id) {
1726 $this->ref = "(PROV".$this->id.")";
1727
1728 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1729 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1730 }
1731
1732 // Add object linked
1733 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1734 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1735 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, ...))
1736 foreach ($tmp_origin_id as $origin_id) {
1737 $ret = $this->add_object_linked($origin, $origin_id);
1738 if (!$ret) {
1739 dol_print_error($this->db);
1740 $error++;
1741 }
1742 }
1743 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1744 $origin_id = $tmp_origin_id;
1745 $ret = $this->add_object_linked($origin, $origin_id);
1746 if (!$ret) {
1747 dol_print_error($this->db);
1748 $error++;
1749 }
1750 }
1751 }
1752 }
1753 }
1754
1755 if (!$error) {
1756 $result = $this->insertExtraFields();
1757 if ($result < 0) {
1758 $error++;
1759 }
1760 }
1761
1762 if (!$error && !$notrigger) {
1763 // Call trigger
1764 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1765 if ($result < 0) {
1766 $this->db->rollback();
1767
1768 return -1;
1769 }
1770 // End call triggers
1771 }
1772
1773 $this->db->commit();
1774 return $this->id;
1775 } else {
1776 $this->error = $this->db->lasterror();
1777 $this->db->rollback();
1778
1779 return -2;
1780 }
1781 } else {
1782 $this->error = 'Failed to get ID of inserted line';
1783
1784 return -1;
1785 }
1786 } else {
1787 $this->error = $this->db->lasterror();
1788 $this->db->rollback();
1789
1790 return -1;
1791 }
1792 }
1793
1801 public function update(User $user, $notrigger = 0)
1802 {
1803 global $conf;
1804
1805 $error = 0;
1806
1807 // Clean parameters
1808 if (isset($this->ref)) {
1809 $this->ref = trim($this->ref);
1810 }
1811 if (isset($this->ref_supplier)) {
1812 $this->ref_supplier = trim($this->ref_supplier);
1813 }
1814 if (isset($this->note_private)) {
1815 $this->note_private = trim($this->note_private);
1816 }
1817 if (isset($this->note_public)) {
1818 $this->note_public = trim($this->note_public);
1819 }
1820 if (isset($this->model_pdf)) {
1821 $this->model_pdf = trim($this->model_pdf);
1822 }
1823 if (isset($this->import_key)) {
1824 $this->import_key = trim($this->import_key);
1825 }
1826
1827 // Update request
1828 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1829
1830 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1831 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1832 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1833 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1834 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1835 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1836 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1837 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1838 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1839 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1840 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1841 $sql .= " fk_statut=".(isset($this->status) ? $this->status : "null").",";
1842 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1843 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1844 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1845 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1846 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1847 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1848 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1849 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1850 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1851 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1852 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1853 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1854 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1855
1856 $sql .= " WHERE rowid=".((int) $this->id);
1857
1858 $this->db->begin();
1859
1860 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1861 $resql = $this->db->query($sql);
1862 if (!$resql) {
1863 $error++;
1864 $this->errors[] = "Error ".$this->db->lasterror();
1865 }
1866
1867 if (!$error) {
1868 $result = $this->insertExtraFields();
1869 if ($result < 0) {
1870 $error++;
1871 }
1872 }
1873
1874 if (!$error && !$notrigger) {
1875 // Call trigger
1876 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1877 if ($result < 0) {
1878 $error++;
1879 }
1880 // End call triggers
1881 }
1882
1883 // Commit or rollback
1884 if ($error) {
1885 foreach ($this->errors as $errmsg) {
1886 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1887 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1888 }
1889 $this->db->rollback();
1890 return -1 * $error;
1891 } else {
1892 $this->db->commit();
1893 return 1;
1894 }
1895 }
1896
1905 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1906 {
1907 global $conf, $user, $hookmanager;
1908
1909 $error = 0;
1910
1911 $this->db->begin();
1912
1913 // get extrafields so they will be clone
1914 foreach ($this->lines as $line) {
1915 $line->fetch_optionals();
1916 }
1917
1918 // Load source object
1919 $objFrom = clone $this;
1920
1921 // Change socid if needed
1922 if (!empty($socid) && $socid != $this->socid) {
1923 $objsoc = new Societe($this->db);
1924
1925 if ($objsoc->fetch($socid) > 0) {
1926 $this->socid = $objsoc->id;
1927 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1928 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1929 $this->fk_project = 0;
1930 $this->fk_delivery_address = 0;
1931 }
1932
1933 // TODO Change product price if multi-prices
1934 }
1935
1936 $this->id = 0;
1937 $this->statut = self::STATUS_DRAFT; // deprecated
1938 $this->status = self::STATUS_DRAFT;
1939
1940 // Clear fields
1941 $this->user_author_id = $user->id;
1942 $this->user_validation_id = 0;
1943
1944 $this->date = dol_now();
1945 $this->date_creation = 0;
1946 $this->date_validation = 0;
1947 $this->date_commande = null;
1948 $this->ref_supplier = '';
1949 $this->user_approve_id = 0;
1950 $this->user_approve_id2 = 0;
1951 $this->date_approve = 0;
1952 $this->date_approve2 = 0;
1953
1954 // Create clone
1955 $this->context['createfromclone'] = 'createfromclone';
1956 $result = $this->create($user, $notrigger);
1957 if ($result < 0) {
1958 $error++;
1959 }
1960
1961 if (!$error) {
1962 // Hook of thirdparty module
1963 if (is_object($hookmanager)) {
1964 $parameters = array('objFrom' => $objFrom);
1965 $action = '';
1966 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1967 if ($reshook < 0) {
1968 $this->setErrorsFromObject($hookmanager);
1969 $error++;
1970 }
1971 }
1972 }
1973
1974 unset($this->context['createfromclone']);
1975
1976 // End
1977 if (!$error) {
1978 $this->db->commit();
1979 return $this->id;
1980 } else {
1981 $this->db->rollback();
1982 return -1;
1983 }
1984 }
1985
2015 public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $fk_prod_fourn_price = 0, $ref_supplier = '', $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $type = 0, $info_bits = 0, $notrigger = 0, $date_start = null, $date_end = null, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0, $rang = -1, $special_code = 0)
2016 {
2017 global $langs, $mysoc;
2018
2019 dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $fk_prod_fourn_price, $ref_supplier, $remise_percent, $price_base_type, $pu_ttc, $type, $info_bits, $notrigger, $date_start, $date_end, $fk_unit, $pu_ht_devise, $origin, $origin_id");
2020 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2021
2022 if ($this->status == self::STATUS_DRAFT) {
2023 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2024
2025 // Clean parameters
2026 if (empty($qty)) {
2027 $qty = 0;
2028 }
2029 if (!$info_bits) {
2030 $info_bits = 0;
2031 }
2032 if (empty($txtva)) {
2033 $txtva = 0;
2034 }
2035 if (empty($rang)) {
2036 $rang = 0;
2037 }
2038 if (empty($txlocaltax1)) {
2039 $txlocaltax1 = 0;
2040 }
2041 if (empty($txlocaltax2)) {
2042 $txlocaltax2 = 0;
2043 }
2044 if (empty($remise_percent)) {
2045 $remise_percent = 0;
2046 }
2047
2048 $remise_percent = price2num($remise_percent);
2049 $qty = price2num($qty);
2050 $pu_ht = price2num($pu_ht);
2051 $pu_ht_devise = price2num($pu_ht_devise);
2052 $pu_ttc = price2num($pu_ttc);
2053 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2054 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2055 }
2056 $txlocaltax1 = price2num($txlocaltax1);
2057 $txlocaltax2 = price2num($txlocaltax2);
2058 if ($price_base_type == 'HT') {
2059 $pu = $pu_ht;
2060 } else {
2061 $pu = $pu_ttc;
2062 }
2063 $desc = trim($desc);
2064
2065 // Check parameters
2066 if ($qty < 0 && !$fk_product) {
2067 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
2068 return -1;
2069 }
2070 if ($type < 0) {
2071 return -1;
2072 }
2073 if ($date_start && $date_end && $date_start > $date_end) {
2074 $langs->load("errors");
2075 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2076 return -1;
2077 }
2078
2079
2080 $this->db->begin();
2081
2082 $product_type = $type;
2083 $label = ''; // deprecated
2084
2085 if ($fk_product > 0) {
2086 if (getDolGlobalInt('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2087 // Check quantity is enough
2088 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_prod_fourn_price=".$fk_prod_fourn_price." qty=".$qty." ref_supplier=".$ref_supplier);
2089 $prod = new ProductFournisseur($this->db);
2090 if ($prod->fetch($fk_product) > 0) {
2091 $product_type = $prod->type;
2092 $label = $prod->label;
2093
2094 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2095 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2096 $result = $prod->get_buyprice($fk_prod_fourn_price, (float) $qty, $fk_product, 'none', (isset($this->fk_soc) ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
2097
2098 // If supplier order created from sales order, we take best supplier price
2099 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2100 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2101 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2102 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2103 // is remise percent not keyed but present for the product we add it
2104 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2105 $remise_percent = $prod->remise_percent;
2106 }
2107 }
2108 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2109 $langs->load("errors");
2110 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2111 $this->db->rollback();
2112 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2113 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2114 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2115 return -1;
2116 }
2117 if ($result == -1) {
2118 $langs->load("errors");
2119 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2120 $this->db->rollback();
2121 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2122 return -1;
2123 }
2124 if ($result < -1) {
2125 $this->error = $prod->error;
2126 $this->db->rollback();
2127 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2128 return -1;
2129 }
2130 } else {
2131 $this->error = $prod->error;
2132 $this->db->rollback();
2133 return -1;
2134 }
2135 }
2136
2137 // Predefine quantity according to packaging
2138 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2139 $prod = new Product($this->db);
2140 $prod->get_buyprice($fk_prod_fourn_price, (float) $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2141
2142 if ($qty < $prod->packaging) {
2143 $qty = (float) $prod->packaging;
2144 } else {
2145 if (!empty($prod->packaging) && (fmod((float) $qty, (float) $prod->packaging) > 0.000001)) {
2146 $coeff = intval((float) $qty / $prod->packaging) + 1;
2147 $qty = (float) $prod->packaging * $coeff;
2148 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2149 }
2150 }
2151 }
2152 }
2153
2154 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2155 $pu = 0;
2156 }
2157
2158 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2159
2160 // Clean vat code
2161 $reg = array();
2162 $vat_src_code = '';
2163 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2164 $vat_src_code = $reg[1];
2165 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2166 }
2167
2168 // Calcul du total TTC et de la TVA pour la ligne a partir de
2169 // qty, pu, remise_percent et txtva
2170 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2171 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2172
2173 $tabprice = calcul_price_total((float) $qty, $pu, $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_ht_devise);
2174
2175 $total_ht = $tabprice[0];
2176 $total_tva = $tabprice[1];
2177 $total_ttc = $tabprice[2];
2178 $total_localtax1 = $tabprice[9];
2179 $total_localtax2 = $tabprice[10];
2180 $pu = $pu_ht = $tabprice[3];
2181
2182 // MultiCurrency
2183 $multicurrency_total_ht = $tabprice[16];
2184 $multicurrency_total_tva = $tabprice[17];
2185 $multicurrency_total_ttc = $tabprice[18];
2186 $pu_ht_devise = $tabprice[19];
2187
2188 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2189 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2190
2191 if ($rang < 0) {
2192 $rangmax = $this->line_max();
2193 $rang = $rangmax + 1;
2194 }
2195
2196 // Insert line
2197 $this->line = new CommandeFournisseurLigne($this->db);
2198
2199 $this->line->context = $this->context;
2200
2201 $this->line->fk_commande = $this->id;
2202 $this->line->label = $label;
2203 $this->line->ref_fourn = $ref_supplier;
2204 $this->line->ref_supplier = $ref_supplier;
2205 $this->line->desc = $desc;
2206 $this->line->qty = $qty;
2207 $this->line->tva_tx = $txtva;
2208 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2209 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2210 $this->line->localtax1_type = $localtax1_type;
2211 $this->line->localtax2_type = $localtax2_type;
2212 $this->line->fk_product = $fk_product;
2213 $this->line->product_type = $product_type;
2214 $this->line->remise_percent = $remise_percent;
2215 $this->line->subprice = (float) $pu_ht;
2216 $this->line->rang = $rang;
2217 $this->line->info_bits = $info_bits;
2218
2219 $this->line->vat_src_code = $vat_src_code;
2220 $this->line->total_ht = (float) $total_ht;
2221 $this->line->total_tva = (float) $total_tva;
2222 $this->line->total_localtax1 = (float) $total_localtax1;
2223 $this->line->total_localtax2 = (float) $total_localtax2;
2224 $this->line->total_ttc = (float) $total_ttc;
2225 $this->line->product_type = $type;
2226 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2227 $this->line->origin = $origin;
2228 $this->line->origin_type = $origin;
2229 $this->line->origin_id = $origin_id;
2230 $this->line->fk_unit = $fk_unit;
2231
2232 $this->line->date_start = $date_start;
2233 $this->line->date_end = $date_end;
2234
2235 // Multicurrency
2236 $this->line->fk_multicurrency = $this->fk_multicurrency;
2237 $this->line->multicurrency_code = $this->multicurrency_code;
2238 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
2239 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2240 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2241 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2242
2243 $this->line->subprice = (float) $pu_ht;
2244 $this->line->price = $this->line->subprice;
2245
2246 $this->line->remise_percent = $remise_percent;
2247
2248 if (is_array($array_options) && count($array_options) > 0) {
2249 $this->line->array_options = $array_options;
2250 }
2251
2252 $result = $this->line->insert($notrigger);
2253 if ($result > 0) {
2254 // Update denormalized fields at the order level
2255 $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2256
2257 if ($result > 0) {
2258 if (!isset($this->context['createfromclone'])) {
2259 if (!empty($this->line->fk_parent_line)) {
2260 // Always reorder if child line
2261 $this->line_order(true, 'DESC');
2262 } elseif ($rang > 0 && $rang <= count($this->lines)) {
2263 // Update all rank of all other lines starting from the same $ranktouse
2264 $linecount = count($this->lines);
2265 for ($ii = $rang; $ii <= $linecount; $ii++) {
2266 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2267 }
2268 }
2269
2270 $this->lines[] = $this->line;
2271 }
2272
2273 $this->db->commit();
2274 return $this->line->id;
2275 } else {
2276 $this->db->rollback();
2277 return -1;
2278 }
2279 } else {
2280 $this->setErrorsFromObject($this->line);
2281 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2282 $this->db->rollback();
2283 return -1;
2284 }
2285 }
2286 return -1;
2287 }
2288
2289
2307 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2308 {
2309 global $conf, $langs;
2310
2311 $error = 0;
2312 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2313
2314 // Check parameters (if test are wrong here, there is bug into caller)
2315 if ($entrepot <= 0) {
2316 $this->error = 'ErrorBadValueForParameterWarehouse';
2317 return -1;
2318 }
2319 if ($qty == 0) {
2320 $this->error = 'ErrorBadValueForParameterQty';
2321 return -1;
2322 }
2323
2324 $dispatchstatus = 1;
2325 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2326 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2327 }
2328
2329 $now = dol_now();
2330
2331 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2332
2333 if (($this->status == self::STATUS_ORDERSENT || $this->status == self::STATUS_RECEIVED_PARTIALLY || $this->status == self::STATUS_RECEIVED_COMPLETELY)) {
2334 $this->db->begin();
2335
2336 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2337 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2338 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2339 $sql .= ($eatby ? "'".$this->db->idate($eatby)."'" : "null").", ".($sellby ? "'".$this->db->idate($sellby)."'" : "null").", ".($batch ? "'".$this->db->escape($batch)."'" : "null").", ".($fk_reception > 0 ? "'".$this->db->escape((string) $fk_reception)."'" : "null");
2340 $sql .= ")";
2341
2342 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2343 $resql = $this->db->query($sql);
2344 if ($resql) {
2345 if (!$notrigger) {
2346 global $conf, $langs, $user;
2347 // Call trigger
2348 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2349 if ($result < 0) {
2350 $error++;
2351 }
2352 // End call triggers
2353 }
2354 } else {
2355 $this->error = $this->db->lasterror();
2356 $error++;
2357 }
2358
2359 // If module stock is enabled and the stock increase is done on purchase order dispatching
2360 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2361 $mouv = new MouvementStock($this->db);
2362 if ($product > 0) {
2363 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2364 $mouv->origin = &$this;
2365 $mouv->setOrigin($this->element, $this->id);
2366
2367 // Method change if qty < 0
2368 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2369 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2370 } else {
2371 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2372 }
2373
2374 if ($result < 0) {
2375 $this->error = $mouv->error;
2376 $this->errors = $mouv->errors;
2377 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2378 $error++;
2379 }
2380 }
2381 }
2382
2383 if ($error == 0) {
2384 $this->db->commit();
2385 return 1;
2386 } else {
2387 $this->db->rollback();
2388 return -1;
2389 }
2390 } else {
2391 $this->error = 'BadStatusForObject';
2392 return -2;
2393 }
2394 }
2395
2403 public function deleteLine($idline, $notrigger = 0)
2404 {
2405 global $user;
2406
2407 if ($this->status == 0) {
2408 $line = new CommandeFournisseurLigne($this->db);
2409
2410 if ($line->fetch($idline) <= 0) {
2411 return 0;
2412 }
2413 if ($this->id > 0 && (int) $line->fk_commande !== (int) $this->id) {
2414 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
2415 return -1;
2416 }
2417
2418 // check if not yet received
2419 $dispatchedLines = $this->getDispachedLines();
2420 foreach ($dispatchedLines as $dispatchLine) {
2421 if ($dispatchLine['orderlineid'] == $idline) {
2422 $this->error = "LineAlreadyDispatched";
2423 $this->errors[] = $this->error;
2424 return -3;
2425 }
2426 }
2427
2428 if ($line->delete($user, $notrigger) > 0) {
2429 $this->update_price(1);
2430 return 1;
2431 } else {
2432 $this->setErrorsFromObject($line);
2433 return -1;
2434 }
2435 } else {
2436 return -2;
2437 }
2438 }
2439
2447 public function delete(User $user, $notrigger = 0)
2448 {
2449 global $langs, $conf;
2450 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2451
2452 $error = 0;
2453
2454 $this->db->begin();
2455
2456 if (empty($notrigger)) {
2457 // Call trigger
2458 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2459 if ($result < 0) {
2460 $this->errors[] = 'ErrorWhenRunningTrigger';
2461 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2462 $this->db->rollback();
2463 return -1;
2464 }
2465 // End call triggers
2466 }
2467
2468 // Test we can delete
2469 $this->fetchObjectLinked(null, 'order_supplier');
2470 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2471 foreach ($this->linkedObjects['reception'] as $element) {
2472 if ($element->statut >= 0) {
2473 $this->errors[] = $langs->trans('ReceptionExist');
2474 $error++;
2475 break;
2476 }
2477 }
2478 }
2479
2480 // Remove linked categories.
2481 if (!$error) {
2482 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_order";
2483 $sql .= " WHERE fk_supplier_order = ".((int) $this->id);
2484
2485 $result = $this->db->query($sql);
2486 if (!$result) {
2487 $error++;
2488 $this->errors[] = $this->db->lasterror();
2489 }
2490 }
2491
2492 $main = $this->db->prefix().'commande_fournisseurdet';
2493
2494 if (!$error) {
2495 $sql1 = "UPDATE ".$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2496 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2497 if (!$this->db->query($sql1)) {
2498 $error++;
2499 $this->error = $this->db->lasterror();
2500 $this->errors[] = $this->db->lasterror();
2501 }
2502 }
2503
2504 if (!$error) {
2505 $ef = $main."_extrafields";
2506 $sql = "DELETE FROM ".$this->db->sanitize($ef)." WHERE fk_object IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2507 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2508 if (!$this->db->query($sql)) {
2509 $this->error = $this->db->lasterror();
2510 $this->errors[] = $this->db->lasterror();
2511 $error++;
2512 }
2513 }
2514
2515 if (!$error) {
2516 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande = ".((int) $this->id);
2517 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2518 if (!$this->db->query($sql)) {
2519 $this->error = $this->db->lasterror();
2520 $this->errors[] = $this->db->lasterror();
2521 $error++;
2522 }
2523 }
2524
2525 if (!$error) {
2526 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid = ".((int) $this->id);
2527 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2528 if ($resql = $this->db->query($sql)) {
2529 if ($this->db->affected_rows($resql) < 1) {
2530 $this->error = $this->db->lasterror();
2531 $this->errors[] = $this->db->lasterror();
2532 $error++;
2533 }
2534 } else {
2535 $this->error = $this->db->lasterror();
2536 $this->errors[] = $this->db->lasterror();
2537 $error++;
2538 }
2539 }
2540
2541 // Remove extrafields
2542 if (!$error) {
2543 $result = $this->deleteExtraFields();
2544 if ($result < 0) {
2545 $this->error = 'FailToDeleteExtraFields';
2546 $this->errors[] = 'FailToDeleteExtraFields';
2547 $error++;
2548 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2549 }
2550 }
2551
2552 // Delete linked object
2553 $res = $this->deleteObjectLinked();
2554 if ($res < 0) {
2555 $this->error = 'FailToDeleteObjectLinked';
2556 $this->errors[] = 'FailToDeleteObjectLinked';
2557 $error++;
2558 }
2559
2560 if (!$error) {
2561 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2562 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2563 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2564
2565 // We remove directory
2566 $ref = dol_sanitizeFileName($this->ref);
2567 if ($conf->fournisseur->commande->dir_output && !empty($ref)) {
2568 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2569 $file = $dir."/".$ref.".pdf";
2570 if (file_exists($file)) {
2571 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2572 $this->error = 'ErrorFailToDeleteFile';
2573 $this->errors[] = 'ErrorFailToDeleteFile';
2574 $error++;
2575 }
2576 }
2577 if (file_exists($dir)) {
2578 $res = @dol_delete_dir_recursive($dir);
2579 if (!$res) {
2580 $this->error = 'ErrorFailToDeleteDir';
2581 $this->errors[] = 'ErrorFailToDeleteDir';
2582 $error++;
2583 }
2584 }
2585 }
2586 }
2587
2588 if (!$error) {
2589 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2590 $this->db->commit();
2591 return 1;
2592 } else {
2593 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2594 $this->db->rollback();
2595 return -$error;
2596 }
2597 }
2598
2599
2608 public function getDispachedLines($status = -1)
2609 {
2610 $ret = array();
2611
2612 // List of already dispatched lines
2613 $sql = "SELECT p.ref, p.label,";
2614 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2615 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2616 $sql .= " FROM ".$this->db->prefix()."product as p,";
2617 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2618 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2619 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2620 $sql .= " AND cfd.fk_product = p.rowid";
2621 if ($status >= 0) {
2622 $sql .= " AND cfd.status = ".((int) $status);
2623 }
2624 $sql .= " ORDER BY cfd.rowid ASC";
2625
2626 $resql = $this->db->query($sql);
2627 if ($resql) {
2628 $num = $this->db->num_rows($resql);
2629 $i = 0;
2630
2631 while ($i < $num) {
2632 $objp = $this->db->fetch_object($resql);
2633 if ($objp) {
2634 $ret[] = array(
2635 'id' => $objp->dispatchedlineid,
2636 'productid' => $objp->fk_product,
2637 'warehouseid' => $objp->warehouse_id,
2638 'qty' => $objp->qty,
2639 'orderlineid' => $objp->fk_elementdet
2640 );
2641 }
2642
2643 $i++;
2644 }
2645 } else {
2646 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2647 }
2648
2649 return $ret;
2650 }
2651
2652 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2662 public function Livraison($user, $date, $type, $comment)
2663 {
2664 // phpcs:enable
2665 global $conf, $langs;
2666
2667 $result = 0;
2668 $error = 0;
2669 $dispatchedlinearray = array();
2670
2671 dol_syslog(get_class($this)."::Livraison");
2672
2673 $usercanreceive = 0;
2674 if (!isModEnabled('reception')) {
2675 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2676 } else {
2677 $usercanreceive = $user->hasRight("reception", "creer");
2678 }
2679
2680 if ($usercanreceive) {
2681 // Define the new status
2682 if ($type == 'par') {
2684 } elseif ($type == 'tot') {
2686 } elseif ($type == 'nev') {
2688 } elseif ($type == 'can') {
2690 } else {
2691 $error++;
2692 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2693 return -2;
2694 }
2695
2696 // Some checks to accept the record
2697 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2698 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2699 if (!$error && ($type == 'tot')) {
2700 $dispatchedlinearray = $this->getDispachedLines(0);
2701 if (count($dispatchedlinearray) > 0) {
2702 $result = -1;
2703 $error++;
2704 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2705 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2706 }
2707 }
2708 if (!$error && getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS_NEED_APPROVE') && ($type == 'tot')) { // Accept to move to reception done, only if status of all line are ok (refuse denied)
2709 $dispatcheddenied = $this->getDispachedLines(2);
2710 if (count($dispatchedlinearray) > 0) {
2711 $result = -1;
2712 $error++;
2713 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2714 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2715 }
2716 }
2717 }
2718
2719 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2720
2721 if (empty($error)) {
2722 $this->db->begin();
2723
2724 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2725 $sql .= " SET fk_statut = ".((int) $statut);
2726 $sql .= " WHERE rowid = ".((int) $this->id);
2727 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2728
2729 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2730 $resql = $this->db->query($sql);
2731 if ($resql) {
2732 $result = 1;
2733 $old_statut = $this->status;
2734 $this->status = $statut;
2735 $this->context['actionmsg2'] = $comment;
2736
2737 // Call trigger
2738 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2739 if ($result_trigger < 0) {
2740 $error++;
2741 }
2742 // End call triggers
2743
2744 if (empty($error)) {
2745 $this->db->commit();
2746 } else {
2747 $this->status = $old_statut;
2748 $this->db->rollback();
2749 $this->error = $this->db->lasterror();
2750 $result = -1;
2751 }
2752 } else {
2753 $this->db->rollback();
2754 $this->error = $this->db->lasterror();
2755 $result = -1;
2756 }
2757 }
2758 } else {
2759 $this->error = $langs->trans('NotAuthorized');
2760 $this->errors[] = $langs->trans('NotAuthorized');
2761 dol_syslog(get_class($this)."::Livraison Not Authorized");
2762 $result = -3;
2763 }
2764 return $result;
2765 }
2766
2767 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2777 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2778 {
2779 // phpcs:enable
2780 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2781 }
2782
2791 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2792 {
2793 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2794 $error = 0;
2795
2796 $this->db->begin();
2797
2798 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2799 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2800 $sql .= " WHERE rowid = ".((int) $this->id);
2801
2802 dol_syslog(__METHOD__, LOG_DEBUG);
2803 $resql = $this->db->query($sql);
2804 if (!$resql) {
2805 $this->errors[] = $this->db->error();
2806 $error++;
2807 }
2808
2809 if (!$error) {
2810 $this->oldcopy = clone $this;
2811 $this->delivery_date = $delivery_date;
2812 }
2813
2814 if (!$notrigger && empty($error)) {
2815 // Call trigger
2816 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2817 if ($result < 0) {
2818 $error++;
2819 }
2820 // End call triggers
2821 }
2822
2823 if (!$error) {
2824 $this->db->commit();
2825 return 1;
2826 } else {
2827 foreach ($this->errors as $errmsg) {
2828 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2829 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2830 }
2831 $this->db->rollback();
2832 return -1 * $error;
2833 }
2834 } else {
2835 return -2;
2836 }
2837 }
2838
2845 public function setReopen(User $user): int
2846 {
2847 if (in_array($this->status, [1, 2, 3, 4, 5, 6, 7, 9])) {
2848 if ($this->status == 1) {
2849 $newStatus = 0; // Validated->Draft
2850 } elseif ($this->status == 2) {
2851 $newStatus = 0; // Approved->Draft
2852 } elseif ($this->status == 3) {
2853 $newStatus = 2; // Ordered->Approved
2854 } elseif ($this->status == 4) {
2855 $newStatus = 3;
2856 } elseif ($this->status == 5) {
2857 //$newstatus=2; // Ordered
2858 // TODO Can we set it to submitted ?
2859 //$newstatus=3; // Submitted
2860 // TODO If there is at least one reception, we can set to Received->Received partially
2861 $newStatus = 4; // Received partially
2862 } elseif ($this->status == 6) {
2863 $newStatus = 2; // Canceled->Approved
2864 } elseif ($this->status == 7) {
2865 $newStatus = 3; // Canceled->Process running
2866 } elseif ($this->status == 9) {
2867 $newStatus = 1; // Refused->Validated
2868 } else {
2869 $newStatus = 2;
2870 }
2871
2872 $this->db->begin();
2873
2874 $result = $this->setStatus($user, $newStatus);
2875 if ($result > 0) {
2876 if ($newStatus == 0) {
2877 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2878 $sql .= ' SET fk_user_approve = null, fk_user_approve2 = null, date_approve = null, date_approve2 = null';
2879 $sql .= ' WHERE rowid = '.((int) $this->id);
2880
2881 $this->db->query($sql);
2882 }
2883
2884 $this->db->commit();
2885
2886 return 1;
2887 } else {
2888 $this->db->rollback();
2889
2890 return -1;
2891 }
2892 }
2893
2894 return 0;
2895 }
2896
2897 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2906 public function set_id_projet($user, $id_projet, $notrigger = 0)
2907 {
2908 // phpcs:enable
2909 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2910 $error = 0;
2911
2912 $this->db->begin();
2913
2914 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2915 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2916 $sql .= " WHERE rowid = ".((int) $this->id);
2917
2918 dol_syslog(__METHOD__, LOG_DEBUG);
2919 $resql = $this->db->query($sql);
2920 if (!$resql) {
2921 $this->errors[] = $this->db->error();
2922 $error++;
2923 }
2924
2925 if (!$error) {
2926 $this->oldcopy = clone $this;
2927 $this->fk_projet = $id_projet;
2928 $this->fk_project = $id_projet;
2929 }
2930
2931 if (!$notrigger && empty($error)) {
2932 // Call trigger
2933 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2934 if ($result < 0) {
2935 $error++;
2936 }
2937 // End call triggers
2938 }
2939
2940 if (!$error) {
2941 $this->db->commit();
2942 return 1;
2943 } else {
2944 foreach ($this->errors as $errmsg) {
2945 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2946 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2947 }
2948 $this->db->rollback();
2949 return -1 * $error;
2950 }
2951 } else {
2952 return -2;
2953 }
2954 }
2955
2964 public function updateFromCommandeClient($user, $idc, $comclientid)
2965 {
2966 $comclient = new Commande($this->db);
2967 $comclient->fetch($comclientid);
2968
2969 $this->id = $idc;
2970
2971 $this->lines = array();
2972
2973 $num = count($comclient->lines);
2974 for ($i = 0; $i < $num; $i++) {
2975 $prod = new Product($this->db);
2976 $label = '';
2977 $ref = '';
2978 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2979 $label = $prod->label;
2980 $ref = $prod->ref;
2981 }
2982
2983 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2984 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2985 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2986 $sql .= ",".((int) $comclient->lines[$i]->fk_product).", ".price2num($comclient->lines[$i]->price, 'MU');
2987 $sql .= ", ".price2num($comclient->lines[$i]->qty, 'MS').", ".price2num($comclient->lines[$i]->tva_tx, 5).", ".price2num($comclient->lines[$i]->localtax1_tx, 5).", ".price2num($comclient->lines[$i]->localtax2_tx, 5).", ".price2num($comclient->lines[$i]->remise_percent, 3);
2988 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2989 if ($this->db->query($sql)) {
2990 $this->update_price(1);
2991 }
2992 }
2993
2994 return 1;
2995 }
2996
3004 public function setStatus($user, $status)
3005 {
3006 global $conf, $langs;
3007 $error = 0;
3008
3009 $this->db->begin();
3010
3011 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
3012 $sql .= " SET fk_statut = ".$status;
3013 $sql .= " WHERE rowid = ".((int) $this->id);
3014
3015 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
3016 $resql = $this->db->query($sql);
3017 if ($resql) {
3018 // Trigger names for each status
3019 $triggerName = array();
3020 $triggerName[0] = 'DRAFT';
3021 $triggerName[1] = 'VALIDATED';
3022 $triggerName[2] = 'APPROVED';
3023 $triggerName[3] = 'ORDERED'; // Ordered
3024 $triggerName[4] = 'RECEIVED_PARTIALLY';
3025 $triggerName[5] = 'RECEIVED_COMPLETELY';
3026 $triggerName[6] = 'CANCELED';
3027 $triggerName[7] = 'CANCELED';
3028 $triggerName[9] = 'REFUSED';
3029
3030 // Call trigger
3031 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
3032 if ($result < 0) {
3033 $error++;
3034 }
3035 // End call triggers
3036 } else {
3037 $error++;
3038 $this->error = $this->db->lasterror();
3039 dol_syslog(get_class($this)."::setStatus ".$this->error);
3040 }
3041
3042 if (!$error) {
3043 $this->status = $status;
3044 $this->db->commit();
3045 return 1;
3046 } else {
3047 $this->db->rollback();
3048 return -1;
3049 }
3050 }
3051
3063 public function setCategories($categories)
3064 {
3065 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3066 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_ORDER);
3067 }
3068
3092 public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $notrigger = 0, $date_start = 0, $date_end = 0, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
3093 {
3094 global $mysoc, $conf, $langs;
3095 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
3096 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3097
3098 $error = 0;
3099
3100 if ($this->status == self::STATUS_DRAFT) {
3101 // Clean parameters
3102 if (empty($qty)) {
3103 $qty = 0;
3104 }
3105 if (empty($info_bits)) {
3106 $info_bits = 0;
3107 }
3108 if (empty($txtva)) {
3109 $txtva = 0;
3110 }
3111 if (empty($txlocaltax1)) {
3112 $txlocaltax1 = 0;
3113 }
3114 if (empty($txlocaltax2)) {
3115 $txlocaltax2 = 0;
3116 }
3117 if (empty($remise_percent)) {
3118 $remise_percent = 0;
3119 }
3120
3121 $remise_percent = (float) price2num($remise_percent);
3122 $qty = price2num($qty);
3123 if (!$qty) {
3124 $qty = 1;
3125 }
3126 $pu = price2num($pu);
3127 $pu_ht_devise = price2num($pu_ht_devise);
3128 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3129 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3130 }
3131 $txlocaltax1 = (float) price2num($txlocaltax1);
3132 $txlocaltax2 = (float) price2num($txlocaltax2);
3133
3134 // Check parameters
3135 if ($type < 0) {
3136 return -1;
3137 }
3138 if ($date_start && $date_end && $date_start > $date_end) {
3139 $langs->load("errors");
3140 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3141 return -1;
3142 }
3143
3144 $this->db->begin();
3145
3146 // Calcul du total TTC et de la TVA pour la ligne a partir de
3147 // qty, pu, remise_percent et txtva
3148 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3149 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3150
3151 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
3152
3153 // Clean vat code
3154 $reg = array();
3155 $vat_src_code = '';
3156 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3157 $vat_src_code = $reg[1];
3158 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3159 }
3160
3161 $tabprice = calcul_price_total($qty, (float) $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_ht_devise);
3162 $total_ht = $tabprice[0];
3163 $total_tva = $tabprice[1];
3164 $total_ttc = $tabprice[2];
3165 $total_localtax1 = $tabprice[9];
3166 $total_localtax2 = $tabprice[10];
3167 $pu_ht = $tabprice[3];
3168 $pu_tva = $tabprice[4];
3169 $pu_ttc = $tabprice[5];
3170
3171 // MultiCurrency
3172 $multicurrency_total_ht = $tabprice[16];
3173 $multicurrency_total_tva = $tabprice[17];
3174 $multicurrency_total_ttc = $tabprice[18];
3175 $pu_ht_devise = $tabprice[19];
3176
3177 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3178 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3179
3180 // Fetch current line from the database and then clone the object and set it in $oldline property
3181 $this->line = new CommandeFournisseurLigne($this->db);
3182 $this->line->fetch($rowid);
3183
3184 $oldline = clone $this->line;
3185 $this->line->oldline = $oldline;
3186
3187 $this->line->context = $this->context;
3188
3189 $this->line->fk_commande = $this->id;
3190 //$this->line->label=$label;
3191 $this->line->desc = $desc;
3192
3193 // redefine quantity according to packaging
3194 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3195 if ($qty < $this->line->packaging) {
3196 $qty = $this->line->packaging;
3197 } else {
3198 if (!empty($this->line->packaging) && is_numeric($this->line->packaging) && (float) $this->line->packaging > 0 && (fmod((float) $qty, (float) $this->line->packaging) > 0)) {
3199 $coeff = intval($qty / $this->line->packaging) + 1;
3200 $qty = $this->line->packaging * $coeff;
3201 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3202 }
3203 }
3204 }
3205
3206 $this->line->qty = $qty;
3207 $this->line->ref_supplier = $ref_supplier;
3208
3209 $this->line->vat_src_code = $vat_src_code;
3210 $this->line->tva_tx = $txtva;
3211 $this->line->localtax1_tx = $txlocaltax1;
3212 $this->line->localtax2_tx = $txlocaltax2;
3213 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3214 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3215 $this->line->remise_percent = $remise_percent;
3216 $this->line->subprice = (float) $pu_ht;
3217 $this->line->info_bits = $info_bits;
3218 $this->line->total_ht = (float) $total_ht;
3219 $this->line->total_tva = (float) $total_tva;
3220 $this->line->total_localtax1 = (float) $total_localtax1;
3221 $this->line->total_localtax2 = (float) $total_localtax2;
3222 $this->line->total_ttc = (float) $total_ttc;
3223 $this->line->product_type = $type;
3224 $this->line->special_code = $oldline->special_code;
3225 $this->line->rang = $oldline->rang;
3226 $this->line->origin = $this->origin;
3227 $this->line->fk_unit = $fk_unit;
3228
3229 $this->line->date_start = $date_start;
3230 $this->line->date_end = $date_end;
3231
3232 // Multicurrency
3233 $this->line->fk_multicurrency = $this->fk_multicurrency;
3234 $this->line->multicurrency_code = $this->multicurrency_code;
3235 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3236 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3237 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3238 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3239
3240 $this->line->subprice = (float) $pu_ht;
3241 $this->line->price = $this->line->subprice;
3242
3243 $this->line->remise_percent = $remise_percent;
3244
3245 if (is_array($array_options) && count($array_options) > 0) {
3246 // We replace values in this->line->array_options only for entries defined into $array_options
3247 foreach ($array_options as $key => $value) {
3248 $this->line->array_options[$key] = $array_options[$key];
3249 }
3250 }
3251
3252 $result = $this->line->update($notrigger);
3253
3254
3255 // Mise a jour info denormalisees au niveau facture
3256 if ($result >= 0) {
3257 $this->update_price(1, 'auto');
3258 $this->db->commit();
3259 return $result;
3260 } else {
3261 $this->errors[] = $this->line->error;
3262 $this->errors = array_merge($this->errors, $this->line->errors);
3263 $this->error = $this->db->lasterror();
3264 $this->db->rollback();
3265 return -1;
3266 }
3267 } else {
3268 $this->error = "Order status makes operation forbidden";
3269 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3270 return -2;
3271 }
3272 }
3273
3274
3282 public function initAsSpecimen()
3283 {
3284 global $user, $langs, $conf;
3285
3286 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3287
3288 dol_syslog(get_class($this)."::initAsSpecimen");
3289
3290 $now = dol_now();
3291
3292 // Find first product
3293 $prodid = 0;
3294 $product = new ProductFournisseur($this->db);
3295 $sql = "SELECT rowid";
3296 $sql .= " FROM ".$this->db->prefix()."product";
3297 $sql .= " WHERE entity IN (".getEntity('product').")";
3298 $sql .= $this->db->order("rowid", "ASC");
3299 $sql .= $this->db->plimit(1);
3300 $resql = $this->db->query($sql);
3301 if ($resql && $this->db->num_rows($resql)) {
3302 $obj = $this->db->fetch_object($resql);
3303 $prodid = $obj->rowid;
3304 }
3305
3306 // Initialise parameters
3307 $this->id = 0;
3308 $this->ref = 'SPECIMEN';
3309 $this->specimen = 1;
3310 $this->socid = 1;
3311 $this->date = $now;
3312 $this->date_commande = $now;
3313 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3314 $this->cond_reglement_code = 'RECEP';
3315 $this->mode_reglement_code = 'CHQ';
3316
3317 $this->note_public = 'This is a comment (public)';
3318 $this->note_private = 'This is a comment (private)';
3319
3320 $this->multicurrency_tx = 1;
3321 $this->multicurrency_code = $conf->currency;
3322
3323 $this->statut = 0; // deprecated
3324 $this->status = 0;
3325
3326 // Lines
3327 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
3328 $xnbp = 0;
3329 while ($xnbp < $nbp) {
3330 $line = new CommandeFournisseurLigne($this->db);
3331 $line->desc = $langs->trans("Description")." ".$xnbp;
3332 $line->qty = 1;
3333 $line->subprice = 100;
3334 $line->tva_tx = 19.6;
3335 $line->localtax1_tx = 0;
3336 $line->localtax2_tx = 0;
3337 if ($xnbp == 2) {
3338 $line->total_ht = 50;
3339 $line->total_ttc = 59.8;
3340 $line->total_tva = 9.8;
3341 $line->remise_percent = 50;
3342 } else {
3343 $line->total_ht = 100;
3344 $line->total_ttc = 119.6;
3345 $line->total_tva = 19.6;
3346 $line->remise_percent = 00;
3347 }
3348 $line->fk_product = $prodid;
3349
3350 $this->lines[$xnbp] = $line;
3351
3352 $this->total_ht += $line->total_ht;
3353 $this->total_tva += $line->total_tva;
3354 $this->total_ttc += $line->total_ttc;
3355
3356 $xnbp++;
3357 }
3358
3359 return 1;
3360 }
3361
3368 public function info($id)
3369 {
3370 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3371 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3372 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3373 $sql .= ' WHERE c.rowid = '.((int) $id);
3374
3375 $result = $this->db->query($sql);
3376 if ($result) {
3377 if ($this->db->num_rows($result)) {
3378 $obj = $this->db->fetch_object($result);
3379
3380 $this->id = $obj->rowid;
3381
3382 $this->user_creation_id = $obj->fk_user_author;
3383 $this->user_validation_id = $obj->fk_user_valid;
3384 $this->user_modification_id = $obj->fk_user_modif;
3385 $this->user_approve_id = $obj->fk_user_approve;
3386 $this->user_approve_id2 = $obj->fk_user_approve2;
3387
3388 $this->date_creation = $this->db->jdate($obj->datec);
3389 $this->date_modification = $this->db->jdate($obj->datem);
3390 $this->date_approve = $this->db->jdate($obj->datea);
3391 $this->date_approve2 = $this->db->jdate($obj->datea2);
3392 $this->date_validation = $this->db->jdate($obj->date_validation);
3393 }
3394 $this->db->free($result);
3395 } else {
3396 dol_print_error($this->db);
3397 }
3398 }
3399
3405 public function loadStateBoard()
3406 {
3407 global $conf, $user;
3408
3409 $this->nb = array();
3410 $clause = "WHERE";
3411
3412 $sql = "SELECT count(co.rowid) as nb";
3413 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3414 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3415 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3416 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3417 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3418 $clause = "AND";
3419 }
3420 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3421
3422 $resql = $this->db->query($sql);
3423 if ($resql) {
3424 while ($obj = $this->db->fetch_object($resql)) {
3425 $this->nb["supplier_orders"] = $obj->nb;
3426 }
3427 $this->db->free($resql);
3428 return 1;
3429 } else {
3430 dol_print_error($this->db);
3431 $this->error = $this->db->error();
3432 return -1;
3433 }
3434 }
3435
3436 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3444 public function load_board($user, $mode = 'opened')
3445 {
3446 // phpcs:enable
3447 global $conf, $langs;
3448
3449 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3450 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3451 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3452 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3453 }
3454 $sql .= " WHERE c.entity = ".$conf->entity;
3455 if ($mode === 'awaiting') {
3456 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3457 } else {
3458 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3459 }
3460 if ($user->socid) {
3461 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3462 }
3463
3464 $resql = $this->db->query($sql);
3465 if ($resql) {
3466 $commandestatic = new CommandeFournisseur($this->db);
3467
3468 $response = new WorkboardResponse();
3469 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3470 $response->label = $langs->trans("SuppliersOrdersToProcess");
3471 $response->labelShort = $langs->trans("Opened");
3472 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3473 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=late';
3474 $response->img = img_object('', "order");
3475
3476 if ($mode === 'awaiting') {
3477 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3478 $response->labelShort = $langs->trans("AwaitingReception");
3479 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3480 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=recv_late';
3481 }
3482
3483 while ($obj = $this->db->fetch_object($resql)) {
3484 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3485 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3486 $commandestatic->statut = $obj->fk_statut; // deprecated
3487 $commandestatic->status = $obj->fk_statut;
3488
3489 $response->nbtodo++;
3490 $response->total += $obj->total_ht;
3491
3492 if ($commandestatic->hasDelay()) {
3493 $response->nbtodolate++;
3494 }
3495 }
3496
3497 return $response;
3498 } else {
3499 $this->error = $this->db->error();
3500 return -1;
3501 }
3502 }
3503
3510 public function getInputMethod()
3511 {
3512 global $langs;
3513
3514 if ($this->methode_commande_id > 0) {
3515 $sql = "SELECT rowid, code, libelle as label";
3516 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3517 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3518
3519 $resql = $this->db->query($sql);
3520 if ($resql) {
3521 if ($this->db->num_rows($resql)) {
3522 $obj = $this->db->fetch_object($resql);
3523
3524 $string = $langs->trans($obj->code);
3525 if ($string == $obj->code) {
3526 $string = $obj->label != '-' ? $obj->label : '';
3527 }
3528 return $string;
3529 }
3530 } else {
3531 dol_print_error($this->db);
3532 }
3533 }
3534
3535 return '';
3536 }
3537
3549 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3550 {
3551 global $langs;
3552
3553 if (!dol_strlen($modele)) {
3554 $modele = ''; // No doc template/generation by default
3555
3556 if (!empty($this->model_pdf)) {
3557 $modele = $this->model_pdf;
3558 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3559 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3560 }
3561 }
3562
3563 if (empty($modele)) {
3564 return 0;
3565 } else {
3566 $langs->load("suppliers");
3567 $outputlangs->load("products");
3568
3569 $modelpath = "core/modules/supplier_order/doc/";
3570 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3571 return $result;
3572 }
3573 }
3574
3581 public function getMaxDeliveryTimeDay($langs)
3582 {
3583 if (empty($this->lines)) {
3584 return '';
3585 }
3586
3587 $tmpproductfourn = new ProductFournisseur($this->db);
3588
3589 $nb = 0;
3590 foreach ($this->lines as $line) {
3591 if ($line->fk_product > 0) {
3592 // Load delivery_time_days, return id into product_fournisseur_price
3593 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3594 if ($idp > 0) {
3595 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3596 if ($tmpproductfourn->delivery_time_days > $nb) {
3597 $nb = $tmpproductfourn->delivery_time_days;
3598 }
3599 }
3600 }
3601 }
3602
3603 if ($nb === 0) {
3604 return '';
3605 } else {
3606 return $nb.' '.$langs->trans('days');
3607 }
3608 }
3609
3615 public function getRights()
3616 {
3617 global $user;
3618
3619 return $user->hasRight("fournisseur", "commande");
3620 }
3621
3622
3631 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3632 {
3633 $tables = array(
3634 'commande_fournisseur'
3635 );
3636
3637 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3638 }
3639
3648 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3649 {
3650 $tables = array(
3651 'commande_fournisseurdet'
3652 );
3653
3654 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3655 }
3656
3664 public function hasDelay()
3665 {
3666 global $conf;
3667
3668 if ($this->status == self::STATUS_ORDERSENT || $this->status == self::STATUS_RECEIVED_PARTIALLY) {
3669 $now = dol_now();
3670 if (!empty($this->delivery_date)) {
3671 $date_to_test = $this->delivery_date;
3672 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3673 } else {
3674 //$date_to_test = $this->date_commande;
3675 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3676 return false;
3677 }
3678 } else {
3679 $now = dol_now();
3680 $date_to_test = $this->date_commande;
3681
3682 return ($this->status > 0 && $this->status < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3683 }
3684 }
3685
3693 public function showDelay()
3694 {
3695 global $conf, $langs;
3696
3697 $langs->load('orders');
3698
3699 $text = '';
3700
3701 if ($this->status == self::STATUS_ORDERSENT || $this->status == self::STATUS_RECEIVED_PARTIALLY) {
3702 if (!empty($this->delivery_date)) {
3703 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3704 } else {
3705 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3706 }
3707 } else {
3708 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3709 }
3710 if ($text) {
3711 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3712 }
3713
3714 return $text;
3715 }
3716
3717
3726 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3727 {
3728 if (isModEnabled("supplier_order")) {
3729 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3730
3731 $qtydelivered = array();
3732 $qtywished = array();
3733
3734 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3735
3736 $filter = array('t.fk_element' => $this->id);
3737 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3738 $filter['t.status'] = 1; // Restrict to lines with status validated
3739 }
3740
3741 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3742 if ($ret < 0) {
3743 $this->error = $supplierorderdispatch->error;
3744 $this->errors = $supplierorderdispatch->errors;
3745 return $ret;
3746 } else {
3747 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3748 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3749 $date_liv = dol_now();
3750
3751 // Build array with quantity deliverd by product
3752 foreach ($supplierorderdispatch->lines as $line) {
3753 if (array_key_exists($line->fk_product, $qtydelivered)) {
3754 $qtydelivered[$line->fk_product] += $line->qty;
3755 } else {
3756 $qtydelivered[$line->fk_product] = $line->qty;
3757 }
3758 }
3759 foreach ($this->lines as $line) {
3760 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3761 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3762 continue;
3763 }
3764 if (array_key_exists($line->fk_product, $qtywished)) {
3765 $qtywished[$line->fk_product] += $line->qty;
3766 } else {
3767 $qtywished[$line->fk_product] = $line->qty;
3768 }
3769 }
3770
3771 //Compare array
3772 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3773 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3774 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3775 //var_dump(array_keys($qtydelivered));
3776 //var_dump(array_keys($qtywished));
3777 //var_dump($diff_array);
3778 //var_dump($keysinwishednotindelivered);
3779 //var_dump($keysindeliverednotinwished);
3780 //exit;
3781
3782 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3783 if ($closeopenorder) {
3784 //$ret=$this->setStatus($user,5);
3785 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3786 if ($ret < 0) {
3787 return -1;
3788 }
3789 return 5;
3790 } else {
3791 //Diff => received partially
3792 //$ret=$this->setStatus($user,4);
3793 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3794 if ($ret < 0) {
3795 return -1;
3796 }
3797 return 4;
3798 }
3799 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3800 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3801
3802 $close = 0;
3803
3804 if (count($diff_array) > 0) {
3805 //there are some difference between the two arrays
3806
3807 //scan the array of results
3808 foreach ($diff_array as $key => $value) {
3809 //if the quantity delivered is greater or equal to wish quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
3810 if ($qtydelivered[$key] >= $qtywished[$key]) {
3811 $close++;
3812 }
3813 }
3814 }
3815
3816
3817 if ($close == count($diff_array)) {
3818 //all the products are received equal or more than the wished quantity
3819 if ($closeopenorder) {
3820 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3821 if ($ret < 0) {
3822 return -1;
3823 }
3824 return 5;
3825 } else {
3826 //Diff => received partially
3827 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3828 if ($ret < 0) {
3829 return -1;
3830 }
3831 return 4;
3832 }
3833 } else {
3834 //all the products are not received
3835 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3836 if ($ret < 0) {
3837 return -1;
3838 }
3839 return 4;
3840 }
3841 } else {
3842 //Diff => received partially
3843 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3844 if ($ret < 0) {
3845 return -1;
3846 }
3847 return 4;
3848 }
3849 }
3850 return 1;
3851 }
3852 }
3853 return 0;
3854 }
3855
3863 public function loadReceptions($filtre_statut = -1)
3864 {
3865 $this->receptions = array();
3866
3867 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3868
3869 $sql = 'SELECT cd.rowid, cd.fk_product,';
3870 $sql .= ' sum(cfd.qty) as qty';
3871 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3872 if ($filtre_statut >= 0) {
3873 $sql .= ' '.$this->db->prefix().'reception as e,';
3874 }
3875 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3876 $sql .= ' WHERE';
3877 if ($filtre_statut >= 0) {
3878 $sql .= ' cfd.fk_reception = e.rowid AND';
3879 }
3880 $sql .= ' cfd.fk_elementdet = cd.rowid';
3881 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3882 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3883 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3884 }
3885 if ($filtre_statut >= 0) {
3886 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3887 }
3888 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3889
3890 $resql = $this->db->query($sql);
3891 if ($resql) {
3892 $num = $this->db->num_rows($resql);
3893 $i = 0;
3894 while ($i < $num) {
3895 $obj = $this->db->fetch_object($resql);
3896 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3897 $i++;
3898 }
3899 $this->db->free($resql);
3900
3901 return $num;
3902 } else {
3903 $this->error = $this->db->lasterror();
3904 return -1;
3905 }
3906 }
3907
3915 public function getKanbanView($option = '', $arraydata = null)
3916 {
3917 global $langs;
3918
3919 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3920
3921 $return = '<div class="box-flex-item box-flex-grow-zero">';
3922 $return .= '<div class="info-box info-box-sm">';
3923 $return .= '<span class="info-box-icon bg-infobox-action">';
3924 $return .= img_picto('', $this->picto);
3925 $return .= '</span>';
3926 $return .= '<div class="info-box-content">';
3927 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3928 if ($selected >= 0) {
3929 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3930 }
3931 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3932 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3933 }
3934 if (property_exists($this, 'billed')) {
3935 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3936 }
3937 if (method_exists($this, 'getLibStatut')) {
3938 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3939 }
3940 $return .= '</div>';
3941 $return .= '</div>';
3942 $return .= '</div>';
3943 return $return;
3944 }
3945}
$object ref
Definition info.php:90
Class to manage table ReceptionLineBatch.
Class to manage predefined suppliers products.
const STATUS_CANCELED_AFTER_ORDER
Order canceled/never received.
const STATUS_RECEIVED_PARTIALLY
Received partially.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
updateFromCommandeClient($user, $idc, $comclientid)
Update a supplier order from a sales order.
getNomUrl($withpicto=0, $option='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable name (with picto eventually)
loadReceptions($filtre_statut=-1)
Load array this->receptions of lines of shipments with nb of products sent for each order line Note: ...
static replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a product id with another one.
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
info($id)
Charge les information d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
fetch_lines($only_product=0)
Load array lines.
getInputMethod()
Returns the translated input method of object (defined if $this->methode_commande_id > 0).
const STATUS_VALIDATED
Validated status.
const STATUS_RECEIVED_COMPLETELY
Received completely.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $fk_prod_fourn_price=0, $ref_supplier='', $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $type=0, $info_bits=0, $notrigger=0, $date_start=null, $date_end=null, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
setReopen(User $user)
Reopen supplier order.
loadStateBoard()
Load the indicators this->nb for the state board.
calcAndSetStatusDispatch(User $user, $closeopenorder=1, $comment='')
Calc status regarding to dispatched stock.
set_id_projet($user, $id_projet, $notrigger=0)
Set the id projet.
showDelay()
Show the customer delayed info.
getTooltipContentArray($params)
getTooltipContentArray
classifyUnBilled(User $user)
Classify not billed.
approve($user, $idwarehouse=0, $secondlevel=0)
Approve a supplier order.
Cancel($user, $idwarehouse=-1)
Cancel an approved order.
dispatchProduct($user, $product, $qty, $entrepot, $price=0, $comment='', $eatby='', $sellby='', $batch='', $fk_commandefourndet=0, $notrigger=0, $fk_reception=0)
Save a receiving into the tracking table of receiving (receptiondet_batch) and add product into stock...
valid($user, $idwarehouse=0, $notrigger=0)
Validate an order.
create($user, $notrigger=0)
Create order with draft status.
update(User $user, $notrigger=0)
Update Supplier Order.
createFromClone(User $user, $socid=0, $notrigger=0)
Load an object from its id and create a new one in database.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $price_base_type='HT', $info_bits=0, $type=0, $notrigger=0, $date_start=0, $date_end=0, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $ref_supplier='')
Update line.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
const STATUS_ORDERSENT
Order sent, shipment on process.
commande($user, $date, $methode, $comment='')
Submit a supplier order to supplier.
getRights()
Returns the rights used for this class.
load_board($user, $mode='opened')
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
deleteLine($idline, $notrigger=0)
Delete line.
getMaxDeliveryTimeDay($langs)
Return the max number delivery delay in day.
fetch($id, $ref='')
Get object and lines from database.
Livraison($user, $date, $type, $comment)
Set a delivery in database for this supplier order.
getDispachedLines($status=-1)
Return array of dispatched lines waiting to be approved for this order.
classifyBilled(User $user)
Class invoiced the supplier order.
initAsSpecimen()
Initialise an instance with random values.
const SOURCE_ID_REPLENISHMENT
The constant used into source field to track the order was generated by the replenishement feature.
setCategories($categories)
Sets object to given categories.
setStatus($user, $status)
Tag order with a particular status.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLibStatut($mode=0)
Return label of the status of object.
hasDelay()
Is the supplier order delayed? We suppose a purchase ordered as late if a the purchase order has been...
LibStatut($status, $mode=0, $billed=0)
Return label of a status.
Class to manage line orders.
Class to manage customers orders.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteExtraFields()
Delete all extra fields values for the current object.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
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.
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.
Class to manage Dolibarr database access.
Class to manage stock movements.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
static getIdFromCode($dbs, $code)
Get id of currency from code.
Class to manage predefined suppliers products.
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 titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
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_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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 '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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...
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90