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,
1691 $line->localtax2_tx,
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,
1700 0,
1701 $line->date_start,
1702 $line->date_end,
1703 $line->array_options,
1704 $line->fk_unit,
1705 $line->multicurrency_subprice, // pu_ht_devise
1706 $line->origin, // origin
1707 $line->origin_id, // origin_id
1708 $line->rang, // rang
1709 $line->special_code
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 = 0;
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
2414 // check if not yet received
2415 $dispatchedLines = $this->getDispachedLines();
2416 foreach ($dispatchedLines as $dispatchLine) {
2417 if ($dispatchLine['orderlineid'] == $idline) {
2418 $this->error = "LineAlreadyDispatched";
2419 $this->errors[] = $this->error;
2420 return -3;
2421 }
2422 }
2423
2424 if ($line->delete($user, $notrigger) > 0) {
2425 $this->update_price(1);
2426 return 1;
2427 } else {
2428 $this->setErrorsFromObject($line);
2429 return -1;
2430 }
2431 } else {
2432 return -2;
2433 }
2434 }
2435
2443 public function delete(User $user, $notrigger = 0)
2444 {
2445 global $langs, $conf;
2446 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2447
2448 $error = 0;
2449
2450 $this->db->begin();
2451
2452 if (empty($notrigger)) {
2453 // Call trigger
2454 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2455 if ($result < 0) {
2456 $this->errors[] = 'ErrorWhenRunningTrigger';
2457 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2458 $this->db->rollback();
2459 return -1;
2460 }
2461 // End call triggers
2462 }
2463
2464 // Test we can delete
2465 $this->fetchObjectLinked(null, 'order_supplier');
2466 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2467 foreach ($this->linkedObjects['reception'] as $element) {
2468 if ($element->statut >= 0) {
2469 $this->errors[] = $langs->trans('ReceptionExist');
2470 $error++;
2471 break;
2472 }
2473 }
2474 }
2475
2476 // Remove linked categories.
2477 if (!$error) {
2478 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_order";
2479 $sql .= " WHERE fk_supplier_order = ".((int) $this->id);
2480
2481 $result = $this->db->query($sql);
2482 if (!$result) {
2483 $error++;
2484 $this->errors[] = $this->db->lasterror();
2485 }
2486 }
2487
2488 $main = $this->db->prefix().'commande_fournisseurdet';
2489
2490 if (!$error) {
2491 $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).")";
2492 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2493 if (!$this->db->query($sql1)) {
2494 $error++;
2495 $this->error = $this->db->lasterror();
2496 $this->errors[] = $this->db->lasterror();
2497 }
2498 }
2499
2500 if (!$error) {
2501 $ef = $main."_extrafields";
2502 $sql = "DELETE FROM ".$this->db->sanitize($ef)." WHERE fk_object IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2503 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2504 if (!$this->db->query($sql)) {
2505 $this->error = $this->db->lasterror();
2506 $this->errors[] = $this->db->lasterror();
2507 $error++;
2508 }
2509 }
2510
2511 if (!$error) {
2512 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande = ".((int) $this->id);
2513 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2514 if (!$this->db->query($sql)) {
2515 $this->error = $this->db->lasterror();
2516 $this->errors[] = $this->db->lasterror();
2517 $error++;
2518 }
2519 }
2520
2521 if (!$error) {
2522 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid = ".((int) $this->id);
2523 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2524 if ($resql = $this->db->query($sql)) {
2525 if ($this->db->affected_rows($resql) < 1) {
2526 $this->error = $this->db->lasterror();
2527 $this->errors[] = $this->db->lasterror();
2528 $error++;
2529 }
2530 } else {
2531 $this->error = $this->db->lasterror();
2532 $this->errors[] = $this->db->lasterror();
2533 $error++;
2534 }
2535 }
2536
2537 // Remove extrafields
2538 if (!$error) {
2539 $result = $this->deleteExtraFields();
2540 if ($result < 0) {
2541 $this->error = 'FailToDeleteExtraFields';
2542 $this->errors[] = 'FailToDeleteExtraFields';
2543 $error++;
2544 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2545 }
2546 }
2547
2548 // Delete linked object
2549 $res = $this->deleteObjectLinked();
2550 if ($res < 0) {
2551 $this->error = 'FailToDeleteObjectLinked';
2552 $this->errors[] = 'FailToDeleteObjectLinked';
2553 $error++;
2554 }
2555
2556 if (!$error) {
2557 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2558 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2559 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2560
2561 // We remove directory
2562 $ref = dol_sanitizeFileName($this->ref);
2563 if ($conf->fournisseur->commande->dir_output) {
2564 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2565 $file = $dir."/".$ref.".pdf";
2566 if (file_exists($file)) {
2567 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2568 $this->error = 'ErrorFailToDeleteFile';
2569 $this->errors[] = 'ErrorFailToDeleteFile';
2570 $error++;
2571 }
2572 }
2573 if (file_exists($dir)) {
2574 $res = @dol_delete_dir_recursive($dir);
2575 if (!$res) {
2576 $this->error = 'ErrorFailToDeleteDir';
2577 $this->errors[] = 'ErrorFailToDeleteDir';
2578 $error++;
2579 }
2580 }
2581 }
2582 }
2583
2584 if (!$error) {
2585 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2586 $this->db->commit();
2587 return 1;
2588 } else {
2589 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2590 $this->db->rollback();
2591 return -$error;
2592 }
2593 }
2594
2595
2604 public function getDispachedLines($status = -1)
2605 {
2606 $ret = array();
2607
2608 // List of already dispatched lines
2609 $sql = "SELECT p.ref, p.label,";
2610 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2611 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2612 $sql .= " FROM ".$this->db->prefix()."product as p,";
2613 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2614 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2615 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2616 $sql .= " AND cfd.fk_product = p.rowid";
2617 if ($status >= 0) {
2618 $sql .= " AND cfd.status = ".((int) $status);
2619 }
2620 $sql .= " ORDER BY cfd.rowid ASC";
2621
2622 $resql = $this->db->query($sql);
2623 if ($resql) {
2624 $num = $this->db->num_rows($resql);
2625 $i = 0;
2626
2627 while ($i < $num) {
2628 $objp = $this->db->fetch_object($resql);
2629 if ($objp) {
2630 $ret[] = array(
2631 'id' => $objp->dispatchedlineid,
2632 'productid' => $objp->fk_product,
2633 'warehouseid' => $objp->warehouse_id,
2634 'qty' => $objp->qty,
2635 'orderlineid' => $objp->fk_elementdet
2636 );
2637 }
2638
2639 $i++;
2640 }
2641 } else {
2642 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2643 }
2644
2645 return $ret;
2646 }
2647
2648 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2658 public function Livraison($user, $date, $type, $comment)
2659 {
2660 // phpcs:enable
2661 global $conf, $langs;
2662
2663 $result = 0;
2664 $error = 0;
2665 $dispatchedlinearray = array();
2666
2667 dol_syslog(get_class($this)."::Livraison");
2668
2669 $usercanreceive = 0;
2670 if (!isModEnabled('reception')) {
2671 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2672 } else {
2673 $usercanreceive = $user->hasRight("reception", "creer");
2674 }
2675
2676 if ($usercanreceive) {
2677 // Define the new status
2678 if ($type == 'par') {
2680 } elseif ($type == 'tot') {
2682 } elseif ($type == 'nev') {
2684 } elseif ($type == 'can') {
2686 } else {
2687 $error++;
2688 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2689 return -2;
2690 }
2691
2692 // Some checks to accept the record
2693 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2694 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2695 if (!$error && ($type == 'tot')) {
2696 $dispatchedlinearray = $this->getDispachedLines(0);
2697 if (count($dispatchedlinearray) > 0) {
2698 $result = -1;
2699 $error++;
2700 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2701 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2702 }
2703 }
2704 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)
2705 $dispatcheddenied = $this->getDispachedLines(2);
2706 if (count($dispatchedlinearray) > 0) {
2707 $result = -1;
2708 $error++;
2709 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2710 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2711 }
2712 }
2713 }
2714
2715 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2716
2717 if (empty($error)) {
2718 $this->db->begin();
2719
2720 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2721 $sql .= " SET fk_statut = ".((int) $statut);
2722 $sql .= " WHERE rowid = ".((int) $this->id);
2723 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2724
2725 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2726 $resql = $this->db->query($sql);
2727 if ($resql) {
2728 $result = 1;
2729 $old_statut = $this->status;
2730 $this->status = $statut;
2731 $this->context['actionmsg2'] = $comment;
2732
2733 // Call trigger
2734 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2735 if ($result_trigger < 0) {
2736 $error++;
2737 }
2738 // End call triggers
2739
2740 if (empty($error)) {
2741 $this->db->commit();
2742 } else {
2743 $this->status = $old_statut;
2744 $this->db->rollback();
2745 $this->error = $this->db->lasterror();
2746 $result = -1;
2747 }
2748 } else {
2749 $this->db->rollback();
2750 $this->error = $this->db->lasterror();
2751 $result = -1;
2752 }
2753 }
2754 } else {
2755 $this->error = $langs->trans('NotAuthorized');
2756 $this->errors[] = $langs->trans('NotAuthorized');
2757 dol_syslog(get_class($this)."::Livraison Not Authorized");
2758 $result = -3;
2759 }
2760 return $result;
2761 }
2762
2763 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2773 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2774 {
2775 // phpcs:enable
2776 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2777 }
2778
2787 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2788 {
2789 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2790 $error = 0;
2791
2792 $this->db->begin();
2793
2794 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2795 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2796 $sql .= " WHERE rowid = ".((int) $this->id);
2797
2798 dol_syslog(__METHOD__, LOG_DEBUG);
2799 $resql = $this->db->query($sql);
2800 if (!$resql) {
2801 $this->errors[] = $this->db->error();
2802 $error++;
2803 }
2804
2805 if (!$error) {
2806 $this->oldcopy = clone $this;
2807 $this->delivery_date = $delivery_date;
2808 }
2809
2810 if (!$notrigger && empty($error)) {
2811 // Call trigger
2812 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2813 if ($result < 0) {
2814 $error++;
2815 }
2816 // End call triggers
2817 }
2818
2819 if (!$error) {
2820 $this->db->commit();
2821 return 1;
2822 } else {
2823 foreach ($this->errors as $errmsg) {
2824 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2825 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2826 }
2827 $this->db->rollback();
2828 return -1 * $error;
2829 }
2830 } else {
2831 return -2;
2832 }
2833 }
2834
2841 public function setReopen(User $user): int
2842 {
2843 if (in_array($this->status, [1, 2, 3, 4, 5, 6, 7, 9])) {
2844 if ($this->status == 1) {
2845 $newStatus = 0; // Validated->Draft
2846 } elseif ($this->status == 2) {
2847 $newStatus = 0; // Approved->Draft
2848 } elseif ($this->status == 3) {
2849 $newStatus = 2; // Ordered->Approved
2850 } elseif ($this->status == 4) {
2851 $newStatus = 3;
2852 } elseif ($this->status == 5) {
2853 //$newstatus=2; // Ordered
2854 // TODO Can we set it to submitted ?
2855 //$newstatus=3; // Submitted
2856 // TODO If there is at least one reception, we can set to Received->Received partially
2857 $newStatus = 4; // Received partially
2858 } elseif ($this->status == 6) {
2859 $newStatus = 2; // Canceled->Approved
2860 } elseif ($this->status == 7) {
2861 $newStatus = 3; // Canceled->Process running
2862 } elseif ($this->status == 9) {
2863 $newStatus = 1; // Refused->Validated
2864 } else {
2865 $newStatus = 2;
2866 }
2867
2868 $this->db->begin();
2869
2870 $result = $this->setStatus($user, $newStatus);
2871 if ($result > 0) {
2872 if ($newStatus == 0) {
2873 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2874 $sql .= ' SET fk_user_approve = null, fk_user_approve2 = null, date_approve = null, date_approve2 = null';
2875 $sql .= ' WHERE rowid = '.((int) $this->id);
2876
2877 $this->db->query($sql);
2878 }
2879
2880 $this->db->commit();
2881
2882 return 1;
2883 } else {
2884 $this->db->rollback();
2885
2886 return -1;
2887 }
2888 }
2889
2890 return 0;
2891 }
2892
2893 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2902 public function set_id_projet($user, $id_projet, $notrigger = 0)
2903 {
2904 // phpcs:enable
2905 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2906 $error = 0;
2907
2908 $this->db->begin();
2909
2910 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2911 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2912 $sql .= " WHERE rowid = ".((int) $this->id);
2913
2914 dol_syslog(__METHOD__, LOG_DEBUG);
2915 $resql = $this->db->query($sql);
2916 if (!$resql) {
2917 $this->errors[] = $this->db->error();
2918 $error++;
2919 }
2920
2921 if (!$error) {
2922 $this->oldcopy = clone $this;
2923 $this->fk_projet = $id_projet;
2924 $this->fk_project = $id_projet;
2925 }
2926
2927 if (!$notrigger && empty($error)) {
2928 // Call trigger
2929 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2930 if ($result < 0) {
2931 $error++;
2932 }
2933 // End call triggers
2934 }
2935
2936 if (!$error) {
2937 $this->db->commit();
2938 return 1;
2939 } else {
2940 foreach ($this->errors as $errmsg) {
2941 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2942 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2943 }
2944 $this->db->rollback();
2945 return -1 * $error;
2946 }
2947 } else {
2948 return -2;
2949 }
2950 }
2951
2960 public function updateFromCommandeClient($user, $idc, $comclientid)
2961 {
2962 $comclient = new Commande($this->db);
2963 $comclient->fetch($comclientid);
2964
2965 $this->id = $idc;
2966
2967 $this->lines = array();
2968
2969 $num = count($comclient->lines);
2970 for ($i = 0; $i < $num; $i++) {
2971 $prod = new Product($this->db);
2972 $label = '';
2973 $ref = '';
2974 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2975 $label = $prod->label;
2976 $ref = $prod->ref;
2977 }
2978
2979 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2980 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2981 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2982 $sql .= ",".((int) $comclient->lines[$i]->fk_product).", ".price2num($comclient->lines[$i]->price, 'MU');
2983 $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);
2984 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2985 if ($this->db->query($sql)) {
2986 $this->update_price(1);
2987 }
2988 }
2989
2990 return 1;
2991 }
2992
3000 public function setStatus($user, $status)
3001 {
3002 global $conf, $langs;
3003 $error = 0;
3004
3005 $this->db->begin();
3006
3007 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
3008 $sql .= " SET fk_statut = ".$status;
3009 $sql .= " WHERE rowid = ".((int) $this->id);
3010
3011 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
3012 $resql = $this->db->query($sql);
3013 if ($resql) {
3014 // Trigger names for each status
3015 $triggerName = array();
3016 $triggerName[0] = 'DRAFT';
3017 $triggerName[1] = 'VALIDATED';
3018 $triggerName[2] = 'APPROVED';
3019 $triggerName[3] = 'ORDERED'; // Ordered
3020 $triggerName[4] = 'RECEIVED_PARTIALLY';
3021 $triggerName[5] = 'RECEIVED_COMPLETELY';
3022 $triggerName[6] = 'CANCELED';
3023 $triggerName[7] = 'CANCELED';
3024 $triggerName[9] = 'REFUSED';
3025
3026 // Call trigger
3027 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
3028 if ($result < 0) {
3029 $error++;
3030 }
3031 // End call triggers
3032 } else {
3033 $error++;
3034 $this->error = $this->db->lasterror();
3035 dol_syslog(get_class($this)."::setStatus ".$this->error);
3036 }
3037
3038 if (!$error) {
3039 $this->status = $status;
3040 $this->db->commit();
3041 return 1;
3042 } else {
3043 $this->db->rollback();
3044 return -1;
3045 }
3046 }
3047
3059 public function setCategories($categories)
3060 {
3061 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3062 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_ORDER);
3063 }
3064
3088 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 = '')
3089 {
3090 global $mysoc, $conf, $langs;
3091 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
3092 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3093
3094 $error = 0;
3095
3096 if ($this->status == self::STATUS_DRAFT) {
3097 // Clean parameters
3098 if (empty($qty)) {
3099 $qty = 0;
3100 }
3101 if (empty($info_bits)) {
3102 $info_bits = 0;
3103 }
3104 if (empty($txtva)) {
3105 $txtva = 0;
3106 }
3107 if (empty($txlocaltax1)) {
3108 $txlocaltax1 = 0;
3109 }
3110 if (empty($txlocaltax2)) {
3111 $txlocaltax2 = 0;
3112 }
3113 if (empty($remise_percent)) {
3114 $remise_percent = 0;
3115 }
3116
3117 $remise_percent = (float) price2num($remise_percent);
3118 $qty = price2num($qty);
3119 if (!$qty) {
3120 $qty = 1;
3121 }
3122 $pu = price2num($pu);
3123 $pu_ht_devise = price2num($pu_ht_devise);
3124 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3125 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3126 }
3127 $txlocaltax1 = (float) price2num($txlocaltax1);
3128 $txlocaltax2 = (float) price2num($txlocaltax2);
3129
3130 // Check parameters
3131 if ($type < 0) {
3132 return -1;
3133 }
3134 if ($date_start && $date_end && $date_start > $date_end) {
3135 $langs->load("errors");
3136 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3137 return -1;
3138 }
3139
3140 $this->db->begin();
3141
3142 // Calcul du total TTC et de la TVA pour la ligne a partir de
3143 // qty, pu, remise_percent et txtva
3144 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3145 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3146
3147 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
3148
3149 // Clean vat code
3150 $reg = array();
3151 $vat_src_code = '';
3152 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3153 $vat_src_code = $reg[1];
3154 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3155 }
3156
3157 $tabprice = calcul_price_total($qty, (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);
3158 $total_ht = $tabprice[0];
3159 $total_tva = $tabprice[1];
3160 $total_ttc = $tabprice[2];
3161 $total_localtax1 = $tabprice[9];
3162 $total_localtax2 = $tabprice[10];
3163 $pu_ht = $tabprice[3];
3164 $pu_tva = $tabprice[4];
3165 $pu_ttc = $tabprice[5];
3166
3167 // MultiCurrency
3168 $multicurrency_total_ht = $tabprice[16];
3169 $multicurrency_total_tva = $tabprice[17];
3170 $multicurrency_total_ttc = $tabprice[18];
3171 $pu_ht_devise = $tabprice[19];
3172
3173 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3174 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3175
3176 // Fetch current line from the database and then clone the object and set it in $oldline property
3177 $this->line = new CommandeFournisseurLigne($this->db);
3178 $this->line->fetch($rowid);
3179
3180 $oldline = clone $this->line;
3181 $this->line->oldline = $oldline;
3182
3183 $this->line->context = $this->context;
3184
3185 $this->line->fk_commande = $this->id;
3186 //$this->line->label=$label;
3187 $this->line->desc = $desc;
3188
3189 // redefine quantity according to packaging
3190 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3191 if ($qty < $this->line->packaging) {
3192 $qty = $this->line->packaging;
3193 } else {
3194 if (!empty($this->line->packaging) && is_numeric($this->line->packaging) && (float) $this->line->packaging > 0 && (fmod((float) $qty, (float) $this->line->packaging) > 0)) {
3195 $coeff = intval($qty / $this->line->packaging) + 1;
3196 $qty = $this->line->packaging * $coeff;
3197 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3198 }
3199 }
3200 }
3201
3202 $this->line->qty = $qty;
3203 $this->line->ref_supplier = $ref_supplier;
3204
3205 $this->line->vat_src_code = $vat_src_code;
3206 $this->line->tva_tx = $txtva;
3207 $this->line->localtax1_tx = $txlocaltax1;
3208 $this->line->localtax2_tx = $txlocaltax2;
3209 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3210 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3211 $this->line->remise_percent = $remise_percent;
3212 $this->line->subprice = (float) $pu_ht;
3213 $this->line->info_bits = $info_bits;
3214 $this->line->total_ht = (float) $total_ht;
3215 $this->line->total_tva = (float) $total_tva;
3216 $this->line->total_localtax1 = (float) $total_localtax1;
3217 $this->line->total_localtax2 = (float) $total_localtax2;
3218 $this->line->total_ttc = (float) $total_ttc;
3219 $this->line->product_type = $type;
3220 $this->line->special_code = $oldline->special_code;
3221 $this->line->rang = $oldline->rang;
3222 $this->line->origin = $this->origin;
3223 $this->line->fk_unit = $fk_unit;
3224
3225 $this->line->date_start = $date_start;
3226 $this->line->date_end = $date_end;
3227
3228 // Multicurrency
3229 $this->line->fk_multicurrency = $this->fk_multicurrency;
3230 $this->line->multicurrency_code = $this->multicurrency_code;
3231 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3232 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3233 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3234 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3235
3236 $this->line->subprice = (float) $pu_ht;
3237 $this->line->price = $this->line->subprice;
3238
3239 $this->line->remise_percent = $remise_percent;
3240
3241 if (is_array($array_options) && count($array_options) > 0) {
3242 // We replace values in this->line->array_options only for entries defined into $array_options
3243 foreach ($array_options as $key => $value) {
3244 $this->line->array_options[$key] = $array_options[$key];
3245 }
3246 }
3247
3248 $result = $this->line->update($notrigger);
3249
3250
3251 // Mise a jour info denormalisees au niveau facture
3252 if ($result >= 0) {
3253 $this->update_price(1, 'auto');
3254 $this->db->commit();
3255 return $result;
3256 } else {
3257 $this->errors[] = $this->line->error;
3258 $this->errors = array_merge($this->errors, $this->line->errors);
3259 $this->error = $this->db->lasterror();
3260 $this->db->rollback();
3261 return -1;
3262 }
3263 } else {
3264 $this->error = "Order status makes operation forbidden";
3265 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3266 return -2;
3267 }
3268 }
3269
3270
3278 public function initAsSpecimen()
3279 {
3280 global $user, $langs, $conf;
3281
3282 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3283
3284 dol_syslog(get_class($this)."::initAsSpecimen");
3285
3286 $now = dol_now();
3287
3288 // Find first product
3289 $prodid = 0;
3290 $product = new ProductFournisseur($this->db);
3291 $sql = "SELECT rowid";
3292 $sql .= " FROM ".$this->db->prefix()."product";
3293 $sql .= " WHERE entity IN (".getEntity('product').")";
3294 $sql .= $this->db->order("rowid", "ASC");
3295 $sql .= $this->db->plimit(1);
3296 $resql = $this->db->query($sql);
3297 if ($resql && $this->db->num_rows($resql)) {
3298 $obj = $this->db->fetch_object($resql);
3299 $prodid = $obj->rowid;
3300 }
3301
3302 // Initialise parameters
3303 $this->id = 0;
3304 $this->ref = 'SPECIMEN';
3305 $this->specimen = 1;
3306 $this->socid = 1;
3307 $this->date = $now;
3308 $this->date_commande = $now;
3309 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3310 $this->cond_reglement_code = 'RECEP';
3311 $this->mode_reglement_code = 'CHQ';
3312
3313 $this->note_public = 'This is a comment (public)';
3314 $this->note_private = 'This is a comment (private)';
3315
3316 $this->multicurrency_tx = 1;
3317 $this->multicurrency_code = $conf->currency;
3318
3319 $this->statut = 0; // deprecated
3320 $this->status = 0;
3321
3322 // Lines
3323 $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)
3324 $xnbp = 0;
3325 while ($xnbp < $nbp) {
3326 $line = new CommandeFournisseurLigne($this->db);
3327 $line->desc = $langs->trans("Description")." ".$xnbp;
3328 $line->qty = 1;
3329 $line->subprice = 100;
3330 $line->tva_tx = 19.6;
3331 $line->localtax1_tx = 0;
3332 $line->localtax2_tx = 0;
3333 if ($xnbp == 2) {
3334 $line->total_ht = 50;
3335 $line->total_ttc = 59.8;
3336 $line->total_tva = 9.8;
3337 $line->remise_percent = 50;
3338 } else {
3339 $line->total_ht = 100;
3340 $line->total_ttc = 119.6;
3341 $line->total_tva = 19.6;
3342 $line->remise_percent = 00;
3343 }
3344 $line->fk_product = $prodid;
3345
3346 $this->lines[$xnbp] = $line;
3347
3348 $this->total_ht += $line->total_ht;
3349 $this->total_tva += $line->total_tva;
3350 $this->total_ttc += $line->total_ttc;
3351
3352 $xnbp++;
3353 }
3354
3355 return 1;
3356 }
3357
3364 public function info($id)
3365 {
3366 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3367 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3368 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3369 $sql .= ' WHERE c.rowid = '.((int) $id);
3370
3371 $result = $this->db->query($sql);
3372 if ($result) {
3373 if ($this->db->num_rows($result)) {
3374 $obj = $this->db->fetch_object($result);
3375
3376 $this->id = $obj->rowid;
3377
3378 $this->user_creation_id = $obj->fk_user_author;
3379 $this->user_validation_id = $obj->fk_user_valid;
3380 $this->user_modification_id = $obj->fk_user_modif;
3381 $this->user_approve_id = $obj->fk_user_approve;
3382 $this->user_approve_id2 = $obj->fk_user_approve2;
3383
3384 $this->date_creation = $this->db->jdate($obj->datec);
3385 $this->date_modification = $this->db->jdate($obj->datem);
3386 $this->date_approve = $this->db->jdate($obj->datea);
3387 $this->date_approve2 = $this->db->jdate($obj->datea2);
3388 $this->date_validation = $this->db->jdate($obj->date_validation);
3389 }
3390 $this->db->free($result);
3391 } else {
3392 dol_print_error($this->db);
3393 }
3394 }
3395
3401 public function loadStateBoard()
3402 {
3403 global $conf, $user;
3404
3405 $this->nb = array();
3406 $clause = "WHERE";
3407
3408 $sql = "SELECT count(co.rowid) as nb";
3409 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3410 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3411 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3412 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3413 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3414 $clause = "AND";
3415 }
3416 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3417
3418 $resql = $this->db->query($sql);
3419 if ($resql) {
3420 while ($obj = $this->db->fetch_object($resql)) {
3421 $this->nb["supplier_orders"] = $obj->nb;
3422 }
3423 $this->db->free($resql);
3424 return 1;
3425 } else {
3426 dol_print_error($this->db);
3427 $this->error = $this->db->error();
3428 return -1;
3429 }
3430 }
3431
3432 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3440 public function load_board($user, $mode = 'opened')
3441 {
3442 // phpcs:enable
3443 global $conf, $langs;
3444
3445 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3446 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3447 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3448 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3449 }
3450 $sql .= " WHERE c.entity = ".$conf->entity;
3451 if ($mode === 'awaiting') {
3452 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3453 } else {
3454 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3455 }
3456 if ($user->socid) {
3457 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3458 }
3459
3460 $resql = $this->db->query($sql);
3461 if ($resql) {
3462 $commandestatic = new CommandeFournisseur($this->db);
3463
3464 $response = new WorkboardResponse();
3465 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3466 $response->label = $langs->trans("SuppliersOrdersToProcess");
3467 $response->labelShort = $langs->trans("Opened");
3468 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3469 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=late';
3470 $response->img = img_object('', "order");
3471
3472 if ($mode === 'awaiting') {
3473 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3474 $response->labelShort = $langs->trans("AwaitingReception");
3475 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3476 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=recv_late';
3477 }
3478
3479 while ($obj = $this->db->fetch_object($resql)) {
3480 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3481 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3482 $commandestatic->statut = $obj->fk_statut; // deprecated
3483 $commandestatic->status = $obj->fk_statut;
3484
3485 $response->nbtodo++;
3486 $response->total += $obj->total_ht;
3487
3488 if ($commandestatic->hasDelay()) {
3489 $response->nbtodolate++;
3490 }
3491 }
3492
3493 return $response;
3494 } else {
3495 $this->error = $this->db->error();
3496 return -1;
3497 }
3498 }
3499
3506 public function getInputMethod()
3507 {
3508 global $langs;
3509
3510 if ($this->methode_commande_id > 0) {
3511 $sql = "SELECT rowid, code, libelle as label";
3512 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3513 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3514
3515 $resql = $this->db->query($sql);
3516 if ($resql) {
3517 if ($this->db->num_rows($resql)) {
3518 $obj = $this->db->fetch_object($resql);
3519
3520 $string = $langs->trans($obj->code);
3521 if ($string == $obj->code) {
3522 $string = $obj->label != '-' ? $obj->label : '';
3523 }
3524 return $string;
3525 }
3526 } else {
3527 dol_print_error($this->db);
3528 }
3529 }
3530
3531 return '';
3532 }
3533
3545 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3546 {
3547 global $langs;
3548
3549 if (!dol_strlen($modele)) {
3550 $modele = ''; // No doc template/generation by default
3551
3552 if (!empty($this->model_pdf)) {
3553 $modele = $this->model_pdf;
3554 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3555 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3556 }
3557 }
3558
3559 if (empty($modele)) {
3560 return 0;
3561 } else {
3562 $langs->load("suppliers");
3563 $outputlangs->load("products");
3564
3565 $modelpath = "core/modules/supplier_order/doc/";
3566 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3567 return $result;
3568 }
3569 }
3570
3577 public function getMaxDeliveryTimeDay($langs)
3578 {
3579 if (empty($this->lines)) {
3580 return '';
3581 }
3582
3583 $tmpproductfourn = new ProductFournisseur($this->db);
3584
3585 $nb = 0;
3586 foreach ($this->lines as $line) {
3587 if ($line->fk_product > 0) {
3588 // Load delivery_time_days, return id into product_fournisseur_price
3589 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3590 if ($idp > 0) {
3591 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3592 if ($tmpproductfourn->delivery_time_days > $nb) {
3593 $nb = $tmpproductfourn->delivery_time_days;
3594 }
3595 }
3596 }
3597 }
3598
3599 if ($nb === 0) {
3600 return '';
3601 } else {
3602 return $nb.' '.$langs->trans('days');
3603 }
3604 }
3605
3611 public function getRights()
3612 {
3613 global $user;
3614
3615 return $user->hasRight("fournisseur", "commande");
3616 }
3617
3618
3627 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3628 {
3629 $tables = array(
3630 'commande_fournisseur'
3631 );
3632
3633 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3634 }
3635
3644 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3645 {
3646 $tables = array(
3647 'commande_fournisseurdet'
3648 );
3649
3650 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3651 }
3652
3660 public function hasDelay()
3661 {
3662 global $conf;
3663
3664 if ($this->status == self::STATUS_ORDERSENT || $this->status == self::STATUS_RECEIVED_PARTIALLY) {
3665 $now = dol_now();
3666 if (!empty($this->delivery_date)) {
3667 $date_to_test = $this->delivery_date;
3668 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3669 } else {
3670 //$date_to_test = $this->date_commande;
3671 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3672 return false;
3673 }
3674 } else {
3675 $now = dol_now();
3676 $date_to_test = $this->date_commande;
3677
3678 return ($this->status > 0 && $this->status < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3679 }
3680 }
3681
3689 public function showDelay()
3690 {
3691 global $conf, $langs;
3692
3693 $langs->load('orders');
3694
3695 $text = '';
3696
3697 if ($this->status == self::STATUS_ORDERSENT || $this->status == self::STATUS_RECEIVED_PARTIALLY) {
3698 if (!empty($this->delivery_date)) {
3699 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3700 } else {
3701 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3702 }
3703 } else {
3704 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3705 }
3706 if ($text) {
3707 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3708 }
3709
3710 return $text;
3711 }
3712
3713
3722 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3723 {
3724 if (isModEnabled("supplier_order")) {
3725 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3726
3727 $qtydelivered = array();
3728 $qtywished = array();
3729
3730 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3731
3732 $filter = array('t.fk_element' => $this->id);
3733 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3734 $filter['t.status'] = 1; // Restrict to lines with status validated
3735 }
3736
3737 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3738 if ($ret < 0) {
3739 $this->error = $supplierorderdispatch->error;
3740 $this->errors = $supplierorderdispatch->errors;
3741 return $ret;
3742 } else {
3743 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3744 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3745 $date_liv = dol_now();
3746
3747 // Build array with quantity deliverd by product
3748 foreach ($supplierorderdispatch->lines as $line) {
3749 if (array_key_exists($line->fk_product, $qtydelivered)) {
3750 $qtydelivered[$line->fk_product] += $line->qty;
3751 } else {
3752 $qtydelivered[$line->fk_product] = $line->qty;
3753 }
3754 }
3755 foreach ($this->lines as $line) {
3756 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3757 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3758 continue;
3759 }
3760 if (array_key_exists($line->fk_product, $qtywished)) {
3761 $qtywished[$line->fk_product] += $line->qty;
3762 } else {
3763 $qtywished[$line->fk_product] = $line->qty;
3764 }
3765 }
3766
3767 //Compare array
3768 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3769 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3770 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3771 //var_dump(array_keys($qtydelivered));
3772 //var_dump(array_keys($qtywished));
3773 //var_dump($diff_array);
3774 //var_dump($keysinwishednotindelivered);
3775 //var_dump($keysindeliverednotinwished);
3776 //exit;
3777
3778 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3779 if ($closeopenorder) {
3780 //$ret=$this->setStatus($user,5);
3781 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3782 if ($ret < 0) {
3783 return -1;
3784 }
3785 return 5;
3786 } else {
3787 //Diff => received partially
3788 //$ret=$this->setStatus($user,4);
3789 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3790 if ($ret < 0) {
3791 return -1;
3792 }
3793 return 4;
3794 }
3795 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3796 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3797
3798 $close = 0;
3799
3800 if (count($diff_array) > 0) {
3801 //there are some difference between the two arrays
3802
3803 //scan the array of results
3804 foreach ($diff_array as $key => $value) {
3805 //if the quantity delivered is greater or equal to wish quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
3806 if ($qtydelivered[$key] >= $qtywished[$key]) {
3807 $close++;
3808 }
3809 }
3810 }
3811
3812
3813 if ($close == count($diff_array)) {
3814 //all the products are received equal or more than the wished quantity
3815 if ($closeopenorder) {
3816 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3817 if ($ret < 0) {
3818 return -1;
3819 }
3820 return 5;
3821 } else {
3822 //Diff => received partially
3823 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3824 if ($ret < 0) {
3825 return -1;
3826 }
3827 return 4;
3828 }
3829 } else {
3830 //all the products are not received
3831 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3832 if ($ret < 0) {
3833 return -1;
3834 }
3835 return 4;
3836 }
3837 } else {
3838 //Diff => received partially
3839 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3840 if ($ret < 0) {
3841 return -1;
3842 }
3843 return 4;
3844 }
3845 }
3846 return 1;
3847 }
3848 }
3849 return 0;
3850 }
3851
3859 public function loadReceptions($filtre_statut = -1)
3860 {
3861 $this->receptions = array();
3862
3863 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3864
3865 $sql = 'SELECT cd.rowid, cd.fk_product,';
3866 $sql .= ' sum(cfd.qty) as qty';
3867 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3868 if ($filtre_statut >= 0) {
3869 $sql .= ' '.$this->db->prefix().'reception as e,';
3870 }
3871 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3872 $sql .= ' WHERE';
3873 if ($filtre_statut >= 0) {
3874 $sql .= ' cfd.fk_reception = e.rowid AND';
3875 }
3876 $sql .= ' cfd.fk_elementdet = cd.rowid';
3877 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3878 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3879 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3880 }
3881 if ($filtre_statut >= 0) {
3882 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3883 }
3884 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3885
3886 $resql = $this->db->query($sql);
3887 if ($resql) {
3888 $num = $this->db->num_rows($resql);
3889 $i = 0;
3890 while ($i < $num) {
3891 $obj = $this->db->fetch_object($resql);
3892 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3893 $i++;
3894 }
3895 $this->db->free($resql);
3896
3897 return $num;
3898 } else {
3899 $this->error = $this->db->lasterror();
3900 return -1;
3901 }
3902 }
3903
3911 public function getKanbanView($option = '', $arraydata = null)
3912 {
3913 global $langs;
3914
3915 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3916
3917 $return = '<div class="box-flex-item box-flex-grow-zero">';
3918 $return .= '<div class="info-box info-box-sm">';
3919 $return .= '<span class="info-box-icon bg-infobox-action">';
3920 $return .= img_picto('', $this->picto);
3921 $return .= '</span>';
3922 $return .= '<div class="info-box-content">';
3923 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3924 if ($selected >= 0) {
3925 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3926 }
3927 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3928 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3929 }
3930 if (property_exists($this, 'billed')) {
3931 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3932 }
3933 if (method_exists($this, 'getLibStatut')) {
3934 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3935 }
3936 $return .= '</div>';
3937 $return .= '</div>';
3938 $return .= '</div>';
3939 return $return;
3940 }
3941}
$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