dolibarr 21.0.0-alpha
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-2024 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 MDW <mdeweerd@users.noreply.github.com>
18 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <https://www.gnu.org/licenses/>.
32 */
33
40require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
41require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
42require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.orderline.class.php';
43require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
44if (isModEnabled('productbatch')) {
45 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
46}
47
48
53{
57 public $element = 'order_supplier';
58
62 public $table_element = 'commande_fournisseur';
63
67 public $table_element_line = 'commande_fournisseurdet';
68
72 public $class_element_line = 'CommandeFournisseurLigne';
73
77 public $fk_element = 'fk_commande';
78
82 public $picto = 'supplier_order';
83
88 public $restrictiononfksoc = 1;
89
93 protected $table_ref_field = 'ref';
94
98 public $id;
99
103 public $ref;
104
108 public $ref_supplier;
109
115 public $ref_fourn;
116
120 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
121 // -> 7=Canceled/Never received -> (reopen) 3=Process running
122 // -> 6=Canceled -> (reopen) 2=Approved
123 // -> 9=Refused -> (reopen) 1=Validated
124 // Note: billed or not is on another field "billed"
125
126 public $billed;
127
131 public $socid;
132
136 public $fourn_id;
137
141 public $date;
142
146 public $date_valid;
147
151 public $date_approve;
152
157 public $date_approve2;
158
162 public $date_commande;
163
164 //For backward compatibility
165 public $remise_percent;
166 public $methode_commande_id;
167 public $methode_commande;
168
172 public $delivery_date;
173
177 public $total_ht;
178
182 public $total_tva;
183
187 public $total_localtax1;
188
192 public $total_localtax2;
193
197 public $total_ttc;
198
199 public $source;
200
204 public $fk_project;
205
209 public $cond_reglement_id;
210
214 public $cond_reglement_code;
215
219 public $cond_reglement_label;
220
224 public $cond_reglement_doc;
225
229 public $fk_account;
230
234 public $mode_reglement_id;
235
239 public $mode_reglement_code;
240
244 public $mode_reglement;
245
249 public $user_author_id;
250
254 public $user_approve_id;
255
260 public $user_approve_id2;
261
262 public $refuse_note;
263
267 public $extraparams = array();
268
272 public $lines = array();
273
277 public $line;
278
279 // Add for supplier_proposal
280 public $origin;
281 public $origin_id;
282 public $linked_objects = array();
283
287 public $date_lim_reglement;
291 public $receptions = array();
292
293 // Multicurrency
297 public $fk_multicurrency;
298
302 public $multicurrency_code;
303
307 public $multicurrency_tx;
308
312 public $multicurrency_total_ht;
313
317 public $multicurrency_total_tva;
318
322 public $multicurrency_total_ttc;
323
351 public $fields = array(
352 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
353 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
354 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => 0, 'position' => 35),
355 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
356 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
357 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
358 'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
359 'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
360 'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
361 'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
362 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
363 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
364 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
365 'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
366 'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
367 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
368 'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
369 'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
370 'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
371 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
372 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
373 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
374 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
375 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
376 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
377 'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
378 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
379 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
380 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
381 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
382 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
383 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
384 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
385 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
386 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
387 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
388 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
389 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
390 'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
391 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
392 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
393 'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
394 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
395 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
396 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
397 );
398
399
403 const STATUS_DRAFT = 0;
404
409
414
419
424
429
434
439
443 const STATUS_REFUSED = 9;
444
445
450
456 public function __construct($db)
457 {
458 $this->db = $db;
459
460 $this->ismultientitymanaged = 1;
461 }
462
463
471 public function fetch($id, $ref = '')
472 {
473 // Check parameters
474 if (empty($id) && empty($ref)) {
475 return -1;
476 }
477
478 $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,";
479 $sql .= " c.localtax1, c.localtax2, ";
480 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
481 $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,";
482 $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,";
483 $sql .= " c.fk_account,";
484 $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
485 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
486 $sql .= " cm.libelle as methode_commande,";
487 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
488 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
489 $sql .= ', c.fk_incoterms, c.location_incoterms';
490 $sql .= ', i.libelle as label_incoterms';
491 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
492 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
493 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
494 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
495 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
496
497 if (empty($id)) {
498 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
499 } else {
500 $sql .= " WHERE c.rowid=".((int) $id);
501 }
502
503 if ($ref) {
504 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
505 }
506
507 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
508 $resql = $this->db->query($sql);
509 if ($resql) {
510 $obj = $this->db->fetch_object($resql);
511 if (!$obj) {
512 $this->error = 'Bill with id '.$id.' not found';
513 dol_syslog(get_class($this).'::fetch '.$this->error);
514 return 0;
515 }
516
517 $this->id = $obj->rowid;
518 $this->entity = $obj->entity;
519
520 $this->ref = $obj->ref;
521 $this->ref_supplier = $obj->ref_supplier;
522 $this->socid = $obj->fk_soc;
523 $this->fourn_id = $obj->fk_soc;
524 $this->statut = $obj->status; // deprecated
525 $this->status = $obj->status;
526 $this->billed = $obj->billed;
527 $this->user_author_id = $obj->user_author_id;
528 $this->user_validation_id = $obj->user_validation_id;
529 $this->user_approve_id = $obj->user_approve_id;
530 $this->user_approve_id2 = $obj->user_approve_id2;
531 $this->total_ht = $obj->total_ht;
532 $this->total_tva = $obj->total_tva;
533 $this->total_localtax1 = $obj->localtax1;
534 $this->total_localtax2 = $obj->localtax2;
535 $this->total_ttc = $obj->total_ttc;
536 $this->date_creation = $this->db->jdate($obj->date_creation);
537 $this->date_valid = $this->db->jdate($obj->date_valid);
538 $this->date_approve = $this->db->jdate($obj->date_approve);
539 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
540 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
541 if (isset($obj->date_commande)) {
542 $this->date = $this->date_commande;
543 } else {
544 $this->date = $this->date_creation;
545 }
546 $this->delivery_date = $this->db->jdate($obj->delivery_date);
547 $this->remise_percent = $obj->remise_percent;
548 $this->methode_commande_id = $obj->fk_input_method;
549 $this->methode_commande = $obj->methode_commande;
550
551 $this->source = $obj->source;
552 $this->fk_project = $obj->fk_project;
553 $this->cond_reglement_id = $obj->fk_cond_reglement;
554 $this->cond_reglement_code = $obj->cond_reglement_code;
555 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
556 $this->cond_reglement_label = $obj->cond_reglement_label;
557 $this->cond_reglement_doc = $obj->cond_reglement_doc;
558 $this->fk_account = $obj->fk_account;
559 $this->mode_reglement_id = $obj->fk_mode_reglement;
560 $this->mode_reglement_code = $obj->mode_reglement_code;
561 $this->mode_reglement = $obj->mode_reglement_libelle;
562 $this->note = $obj->note_private; // deprecated
563 $this->note_private = $obj->note_private;
564 $this->note_public = $obj->note_public;
565 $this->model_pdf = $obj->model_pdf;
566
567 //Incoterms
568 $this->fk_incoterms = $obj->fk_incoterms;
569 $this->location_incoterms = $obj->location_incoterms;
570 $this->label_incoterms = $obj->label_incoterms;
571
572 // Multicurrency
573 $this->fk_multicurrency = $obj->fk_multicurrency;
574 $this->multicurrency_code = $obj->multicurrency_code;
575 $this->multicurrency_tx = $obj->multicurrency_tx;
576 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
577 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
578 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
579
580 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
581
582 $this->db->free($resql);
583
584 // Retrieve all extrafield
585 // fetch optionals attributes and labels
586 $this->fetch_optionals();
587
588 // Lines
589 $result = $this->fetch_lines();
590
591 if ($result < 0) {
592 return -1;
593 } else {
594 return 1;
595 }
596 } else {
597 $this->error = $this->db->error()." sql=".$sql;
598 return -1;
599 }
600 }
601
602 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
609 public function fetch_lines($only_product = 0)
610 {
611 // phpcs:enable
612
613 $this->lines = array();
614
615 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
616 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
617 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
618 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
619 $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,";
620 $sql .= " l.fk_unit,";
621 $sql .= " l.date_start, l.date_end,";
622 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
623 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
624 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
625 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
626 if ($only_product) {
627 $sql .= ' AND p.fk_product_type = 0';
628 }
629 $sql .= " ORDER BY l.rang, l.rowid";
630 //print $sql;
631
632 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
633
634 $result = $this->db->query($sql);
635 if ($result) {
636 $num = $this->db->num_rows($result);
637 $i = 0;
638
639 while ($i < $num) {
640 $objp = $this->db->fetch_object($result);
641
642 $line = new CommandeFournisseurLigne($this->db);
643
644 $line->id = $objp->rowid;
645 $line->fk_commande = $objp->fk_commande;
646 $line->desc = $objp->description;
647 $line->description = $objp->description;
648 $line->qty = $objp->qty;
649 $line->tva_tx = $objp->tva_tx;
650 $line->localtax1_tx = $objp->localtax1_tx;
651 $line->localtax2_tx = $objp->localtax2_tx;
652 $line->localtax1_type = $objp->localtax1_type;
653 $line->localtax2_type = $objp->localtax2_type;
654 $line->subprice = $objp->subprice;
655 $line->pu_ht = $objp->subprice;
656 $line->remise_percent = $objp->remise_percent;
657
658 $line->vat_src_code = $objp->vat_src_code;
659 $line->total_ht = $objp->total_ht;
660 $line->total_tva = $objp->total_tva;
661 $line->total_localtax1 = $objp->total_localtax1;
662 $line->total_localtax2 = $objp->total_localtax2;
663 $line->total_ttc = $objp->total_ttc;
664 $line->product_type = $objp->product_type;
665
666 $line->fk_product = $objp->fk_product;
667
668 $line->libelle = $objp->product_label; // deprecated
669 $line->product_label = $objp->product_label;
670 $line->product_desc = $objp->product_desc;
671 $line->product_tobatch = $objp->product_tobatch;
672 $line->product_barcode = $objp->product_barcode;
673
674 $line->ref = $objp->product_ref; // Ref of product
675 $line->product_ref = $objp->product_ref; // Ref of product
676 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
677 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
678
679 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
680 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
681 // Move this into another method and call it when required.
682
683 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
684 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
685 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
686 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
687 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
688 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
689 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
690 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
691 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
692 $sqlsearchpackage .= " LIMIT 1";
693
694 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
695 if ($resqlsearchpackage) {
696 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
697 if ($objsearchpackage) {
698 $line->fk_fournprice = $objsearchpackage->rowid;
699 $line->packaging = $objsearchpackage->packaging;
700 }
701 } else {
702 $this->error = $this->db->lasterror();
703 return -1;
704 }
705 }
706
707 $line->date_start = $this->db->jdate($objp->date_start);
708 $line->date_end = $this->db->jdate($objp->date_end);
709 $line->fk_unit = $objp->fk_unit;
710
711 // Multicurrency
712 $line->fk_multicurrency = $objp->fk_multicurrency;
713 $line->multicurrency_code = $objp->multicurrency_code;
714 $line->multicurrency_subprice = $objp->multicurrency_subprice;
715 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
716 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
717 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
718
719 $line->info_bits = $objp->info_bits;
720 $line->special_code = $objp->special_code;
721 $line->fk_parent_line = $objp->fk_parent_line;
722
723 $line->rang = $objp->rang;
724
725 // Retrieve all extrafield
726 // fetch optionals attributes and labels
727 $line->fetch_optionals();
728
729 $this->lines[$i] = $line;
730
731 $i++;
732 }
733 $this->db->free($result);
734
735 return $num;
736 } else {
737 $this->error = $this->db->error()." sql=".$sql;
738 return -1;
739 }
740 }
741
750 public function valid($user, $idwarehouse = 0, $notrigger = 0)
751 {
752 global $conf;
753 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
754
755 $error = 0;
756
757 dol_syslog(get_class($this)."::valid");
758 $result = 0;
759 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
760 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
761 $this->db->begin();
762
763 // Definition of supplier order numbering model name
764 $soc = new Societe($this->db);
765 $soc->fetch($this->fourn_id);
766
767 // Check if object has a temporary ref
768 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
769 $num = $this->getNextNumRef($soc);
770 } else {
771 $num = $this->ref;
772 }
773 $this->newref = dol_sanitizeFileName($num);
774
775 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
776 $sql .= " SET ref='".$this->db->escape($num)."',";
777 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
778 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
779 $sql .= " fk_user_valid = ".((int) $user->id);
780 $sql .= " WHERE rowid = ".((int) $this->id);
781 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
782
783 $resql = $this->db->query($sql);
784 if (!$resql) {
785 dol_print_error($this->db);
786 $error++;
787 }
788
789 if (!$error && !$notrigger) {
790 // Call trigger
791 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
792 if ($result < 0) {
793 $error++;
794 }
795 // End call triggers
796 }
797
798 if (!$error) {
799 $this->oldref = $this->ref;
800
801 // Rename directory if dir was a temporary ref
802 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
803 // Now we rename also files into index
804 $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)."'";
805 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
806 $resql = $this->db->query($sql);
807 if (!$resql) {
808 $error++;
809 $this->error = $this->db->lasterror();
810 }
811 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
812 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
813 $resql = $this->db->query($sql);
814 if (!$resql) {
815 $error++;
816 $this->error = $this->db->lasterror();
817 }
818
819 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
820 $oldref = dol_sanitizeFileName($this->ref);
821 $newref = dol_sanitizeFileName($num);
822 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
823 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
824 if (!$error && file_exists($dirsource)) {
825 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
826
827 if (@rename($dirsource, $dirdest)) {
828 dol_syslog("Rename ok");
829 // Rename docs starting with $oldref with $newref
830 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
831 foreach ($listoffiles as $fileentry) {
832 $dirsource = $fileentry['name'];
833 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
834 $dirsource = $fileentry['path'].'/'.$dirsource;
835 $dirdest = $fileentry['path'].'/'.$dirdest;
836 @rename($dirsource, $dirdest);
837 }
838 }
839 }
840 }
841 }
842
843 if (!$error) {
844 $result = 1;
846 $this->statut = self::STATUS_VALIDATED; // deprecated
847 $this->ref = $num;
848 }
849
850 if (!$error) {
851 $this->db->commit();
852 return 1;
853 } else {
854 $this->db->rollback();
855 return -1;
856 }
857 } else {
858 $this->error = 'NotAuthorized';
859 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
860 return -1;
861 }
862 }
863
870 public function getLibStatut($mode = 0)
871 {
872 return $this->LibStatut($this->statut, $mode, $this->billed);
873 }
874
875 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
884 public function LibStatut($status, $mode = 0, $billed = 0)
885 {
886 // phpcs:enable
887 global $langs, $hookmanager;
888
889 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
890 $langs->load('orders');
891
892 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
893 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
894 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
895 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
896 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
897 } else {
898 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
899 }
900 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
901 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
902 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
903 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
904 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
905
906 // List of language codes for status
907 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
908 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
909 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
910 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
911 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
912 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
913 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
914 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
915 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
916 }
917
918 $statustrans = array(
919 0 => 'status0',
920 1 => 'status1b',
921 2 => 'status1',
922 3 => 'status4',
923 4 => 'status4b',
924 5 => 'status6',
925 6 => 'status9',
926 7 => 'status9',
927 9 => 'status9',
928 );
929
930 $statusClass = 'status0';
931 if (!empty($statustrans[$status])) {
932 $statusClass = $statustrans[$status];
933 }
934
935 $billedtext = '';
936 if ($billed) {
937 $billedtext = ' - '.$langs->trans("Billed");
938 }
939 if ($status == 5 && $billed) {
940 $statusClass = 'status6';
941 }
942
943 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
944 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
945
946 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
947 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
948 if ($reshook > 0) {
949 return $hookmanager->resPrint;
950 }
951
952 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
953 }
954
961 public function getTooltipContentArray($params)
962 {
963 global $conf, $langs, $user;
964
965 $langs->loadLangs(['bills', 'orders']);
966
967 $datas = [];
968 $nofetch = !empty($params['nofetch']);
969
970 if ($user->hasRight("fournisseur", "commande", "read")) {
971 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
972 if (isset($this->statut)) {
973 $datas['picto'] .= ' '.$this->getLibStatut(5);
974 }
975 if (!empty($this->ref)) {
976 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
977 }
978 if (!empty($this->ref_supplier)) {
979 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
980 }
981 if (!$nofetch) {
982 $langs->load('companies');
983 if (empty($this->thirdparty)) {
984 $this->fetch_thirdparty();
985 }
986 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
987 }
988 if (!empty($this->total_ht)) {
989 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
990 }
991 if (!empty($this->total_tva)) {
992 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
993 }
994 if (!empty($this->total_ttc)) {
995 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
996 }
997 if (!empty($this->date)) {
998 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
999 }
1000 if (!empty($this->delivery_date)) {
1001 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
1002 }
1003 }
1004 return $datas;
1005 }
1006
1017 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1018 {
1019 global $langs, $user, $hookmanager;
1020
1021 $result = '';
1022 $params = [
1023 'id' => $this->id,
1024 'objecttype' => $this->element,
1025 'option' => $option,
1026 'nofetch' => 1
1027 ];
1028 $classfortooltip = 'classfortooltip';
1029 $dataparams = '';
1030 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1031 $classfortooltip = 'classforajaxtooltip';
1032 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1033 $label = '';
1034 } else {
1035 $label = implode($this->getTooltipContentArray($params));
1036 }
1037
1038 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1039
1040 if ($option !== 'nolink') {
1041 // Add param to save lastsearch_values or not
1042 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1043 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1044 $add_save_lastsearch_values = 1;
1045 }
1046 if ($add_save_lastsearch_values) {
1047 $url .= '&save_lastsearch_values=1';
1048 }
1049 }
1050
1051 $linkclose = '';
1052 if (empty($notooltip)) {
1053 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1054 $label = $langs->trans("ShowOrder");
1055 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1056 }
1057 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1058 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1059 }
1060
1061 $linkstart = '<a href="'.$url.'"';
1062 $linkstart .= $linkclose.'>';
1063 $linkend = '</a>';
1064
1065 $result .= $linkstart;
1066 if ($withpicto) {
1067 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1068 }
1069 if ($withpicto != 2) {
1070 $result .= $this->ref;
1071 }
1072 $result .= $linkend;
1073
1074 if ($addlinktonotes) {
1075 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1076 if ($txttoshow) {
1077 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1078 $result .= ' <span class="note inline-block">';
1079 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1080 $result .= img_picto('', 'note');
1081 $result .= '</a>';
1082 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1083 //$result.='</a>';
1084 $result .= '</span>';
1085 }
1086 }
1087
1088 global $action;
1089 $hookmanager->initHooks(array($this->element . 'dao'));
1090 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1091 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1092 if ($reshook > 0) {
1093 $result = $hookmanager->resPrint;
1094 } else {
1095 $result .= $hookmanager->resPrint;
1096 }
1097 return $result;
1098 }
1099
1100
1108 public function getNextNumRef($soc)
1109 {
1110 global $langs, $conf;
1111 $langs->load("orders");
1112
1113 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1114 $mybool = false;
1115
1116 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1117 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1118
1119 // Include file with class
1120 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1121
1122 foreach ($dirmodels as $reldir) {
1123 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1124
1125 // Load file with numbering class (if found)
1126 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1127 }
1128
1129 if (!$mybool) {
1130 dol_print_error(null, "Failed to include file ".$file);
1131 return '';
1132 }
1133
1134 $obj = new $classname();
1135 '@phan-var-force ModeleNumRefSuppliersOrders $obj';
1136 $numref = $obj->getNextValue($soc, $this);
1137
1138 if ($numref != "") {
1139 return $numref;
1140 } else {
1141 $this->error = $obj->error;
1142 return -1;
1143 }
1144 } else {
1145 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1146 return -2;
1147 }
1148 }
1155 public function classifyBilled(User $user)
1156 {
1157 $error = 0;
1158
1159 if ($this->billed) {
1160 return 0;
1161 }
1162
1163 $this->db->begin();
1164
1165 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1166 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1167
1168 if ($this->db->query($sql)) {
1169 if (!$error) {
1170 // Call trigger
1171 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1172 if ($result < 0) {
1173 $error++;
1174 }
1175 // End call triggers
1176 }
1177
1178 if (!$error) {
1179 $this->billed = 1;
1180
1181 $this->db->commit();
1182 return 1;
1183 } else {
1184 $this->db->rollback();
1185 return -1;
1186 }
1187 } else {
1188 dol_print_error($this->db);
1189
1190 $this->db->rollback();
1191 return -1;
1192 }
1193 }
1194
1195
1202 public function classifyUnBilled(User $user)
1203 {
1204 if (empty($this->billed)) {
1205 return 0;
1206 }
1207
1208 $this->db->begin();
1209
1210 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 0';
1211 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1212 ;
1213
1214 if (!$this->db->query($sql)) {
1215 dol_print_error($this->db);
1216 $this->db->rollback();
1217 return -1;
1218 }
1219
1220 // Call trigger
1221 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_UNBILLED', $user);
1222 if ($result < 0) {
1223 $this->db->rollback();
1224 return -1;
1225 }
1226 // End call triggers
1227
1228 $this->billed = 1;
1229 $this->db->commit();
1230 return 1;
1231 }
1232
1233
1242 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1243 {
1244 global $langs, $conf;
1245 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1246
1247 $error = 0;
1248
1249 dol_syslog(get_class($this)."::approve");
1250
1251 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1252 $now = dol_now();
1253
1254 $this->db->begin();
1255
1256 // Definition of order numbering model name
1257 $soc = new Societe($this->db);
1258 $soc->fetch($this->fourn_id);
1259
1260 // Check if object has a temporary ref
1261 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1262 $num = $this->getNextNumRef($soc);
1263 } else {
1264 $num = $this->ref;
1265 }
1266 $this->newref = dol_sanitizeFileName($num);
1267
1268 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1269 $movetoapprovestatus = true;
1270 $comment = '';
1271
1272 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1273 $sql .= " SET ref='".$this->db->escape($num)."',";
1274 if (empty($secondlevel)) { // standard or first level approval
1275 $sql .= " date_approve='".$this->db->idate($now)."',";
1276 $sql .= " fk_user_approve = ".((int) $user->id);
1277 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= getDolGlobalFloat('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED')) {
1278 if (empty($this->user_approve_id2)) {
1279 $movetoapprovestatus = false; // second level approval not done
1280 $comment = ' (first level)';
1281 }
1282 }
1283 } else { // request a second level approval
1284 $sql .= " date_approve2='".$this->db->idate($now)."',";
1285 $sql .= " fk_user_approve2 = ".((int) $user->id);
1286 if (empty($this->user_approve_id)) {
1287 $movetoapprovestatus = false; // first level approval not done
1288 }
1289 $comment = ' (second level)';
1290 }
1291 // If double approval is required and first approval, we keep status to 1 = validated
1292 if ($movetoapprovestatus) {
1293 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1294 } else {
1295 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1296 }
1297 $sql .= " WHERE rowid = ".((int) $this->id);
1298 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1299
1300 if ($this->db->query($sql)) {
1301 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1302 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1303 if ($result < 0 && $result != -2) { // -2 means already exists
1304 $error++;
1305 }
1306 }
1307
1308 // If stock is incremented on validate order, we must increment it
1309 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1310 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1311 $langs->load("agenda");
1312
1313 $cpt = count($this->lines);
1314 for ($i = 0; $i < $cpt; $i++) {
1315 // Product with reference
1316 if ($this->lines[$i]->fk_product > 0) {
1317 $this->line = $this->lines[$i];
1318 $mouvP = new MouvementStock($this->db);
1319 $mouvP->origin = &$this;
1320 $mouvP->setOrigin($this->element, $this->id);
1321 // We decrement stock of product (and sub-products)
1322 $up_ht_disc = $this->lines[$i]->subprice;
1323 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1324 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1325 }
1326 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1327 if ($result < 0) {
1328 $error++;
1329 }
1330 unset($this->line);
1331 }
1332 }
1333 }
1334
1335 if (!$error) {
1336 // Call trigger
1337 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1338 if ($result < 0) {
1339 $error++;
1340 }
1341 // End call triggers
1342 }
1343
1344 if (!$error) {
1345 $this->ref = $this->newref;
1346
1347 if ($movetoapprovestatus) {
1348 $this->statut = self::STATUS_ACCEPTED;
1349 } else {
1350 $this->statut = self::STATUS_VALIDATED;
1351 }
1352 if (empty($secondlevel)) { // standard or first level approval
1353 $this->date_approve = $now;
1354 $this->user_approve_id = $user->id;
1355 } else { // request a second level approval
1356 $this->date_approve2 = $now;
1357 $this->user_approve_id2 = $user->id;
1358 }
1359
1360 $this->db->commit();
1361 return 1;
1362 } else {
1363 $this->db->rollback();
1364 return -1;
1365 }
1366 } else {
1367 $this->db->rollback();
1368 $this->error = $this->db->lasterror();
1369 return -1;
1370 }
1371 } else {
1372 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1373 }
1374 return -1;
1375 }
1376
1383 public function refuse($user)
1384 {
1385 global $conf, $langs;
1386
1387 $error = 0;
1388
1389 dol_syslog(get_class($this)."::refuse");
1390 $result = 0;
1391 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1392 $this->db->begin();
1393
1394 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1395 $sql .= " WHERE rowid = ".((int) $this->id);
1396
1397 if ($this->db->query($sql)) {
1398 $result = 0;
1399
1400 if ($error == 0) {
1401 // Call trigger
1402 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1403 if ($result < 0) {
1404 $error++;
1405 $this->db->rollback();
1406 } else {
1407 $this->db->commit();
1408 }
1409 // End call triggers
1410 }
1411 } else {
1412 $this->db->rollback();
1413 $this->error = $this->db->lasterror();
1414 dol_syslog(get_class($this)."::refuse Error -1");
1415 $result = -1;
1416 }
1417 } else {
1418 dol_syslog(get_class($this)."::refuse Not Authorized");
1419 }
1420 return $result;
1421 }
1422
1423 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1432 public function Cancel($user, $idwarehouse = -1)
1433 {
1434 // phpcs:enable
1435 global $langs, $conf;
1436
1437 $error = 0;
1438
1439 //dol_syslog("CommandeFournisseur::Cancel");
1440 $result = 0;
1441 if ($user->hasRight("fournisseur", "commande", "commander")) {
1442 $statut = self::STATUS_CANCELED;
1443
1444 $this->db->begin();
1445
1446 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1447 $sql .= " WHERE rowid = ".((int) $this->id);
1448 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1449 if ($this->db->query($sql)) {
1450 $result = 0;
1451
1452 // Call trigger
1453 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1454 if ($result < 0) {
1455 $error++;
1456 }
1457 // End call triggers
1458
1459 if ($error == 0) {
1460 $this->db->commit();
1461 return 1;
1462 } else {
1463 $this->db->rollback();
1464 return -1;
1465 }
1466 } else {
1467 $this->db->rollback();
1468 $this->error = $this->db->lasterror();
1469 dol_syslog(get_class($this)."::cancel ".$this->error);
1470 return -1;
1471 }
1472 } else {
1473 dol_syslog(get_class($this)."::cancel Not Authorized");
1474 return -1;
1475 }
1476 }
1477
1487 public function commande($user, $date, $methode, $comment = '')
1488 {
1489 global $langs;
1490 dol_syslog(get_class($this)."::commande");
1491 $error = 0;
1492 if ($user->hasRight("fournisseur", "commande", "commander")) {
1493 $this->db->begin();
1494
1495 $newnoteprivate = $this->note_private;
1496 if ($comment) {
1497 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1498 }
1499
1500 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1501 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1502 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1503 $sql .= " WHERE rowid=".((int) $this->id);
1504
1505 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1506 if ($this->db->query($sql)) {
1507 $this->statut = self::STATUS_ORDERSENT;
1508 $this->methode_commande_id = $methode;
1509 $this->date_commande = $date;
1510 $this->context = array('comments' => $comment);
1511
1512 // Call trigger
1513 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1514 if ($result < 0) {
1515 $error++;
1516 }
1517 // End call triggers
1518 } else {
1519 $error++;
1520 $this->error = $this->db->lasterror();
1521 $this->errors[] = $this->db->lasterror();
1522 }
1523
1524 if (!$error) {
1525 $this->db->commit();
1526 } else {
1527 $this->db->rollback();
1528 }
1529 } else {
1530 $error++;
1531 $this->error = $langs->trans('NotAuthorized');
1532 $this->errors[] = $langs->trans('NotAuthorized');
1533 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1534 }
1535
1536 return ($error ? -1 : 1);
1537 }
1538
1546 public function create($user, $notrigger = 0)
1547 {
1548 global $langs, $conf, $hookmanager;
1549
1550 $this->db->begin();
1551
1552 $error = 0;
1553 $now = dol_now();
1554
1555 // set tmp vars
1556 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1557 if (empty($date)) {
1558 $date = $now;
1559 }
1560 $delivery_date = $this->delivery_date;
1561
1562 // Clean parameters
1563 if (empty($this->source)) {
1564 $this->source = 0;
1565 }
1566
1567 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1568 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1569 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1570 } else {
1571 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1572 }
1573 if (empty($this->fk_multicurrency)) {
1574 $this->multicurrency_code = $conf->currency;
1575 $this->fk_multicurrency = 0;
1576 $this->multicurrency_tx = 1;
1577 }
1578
1579 $this->statut = self::STATUS_DRAFT; // deprecated
1580 $this->status = self::STATUS_DRAFT;
1581
1582 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1583 $sql .= "ref";
1584 $sql .= ", ref_supplier";
1585 $sql .= ", note_private";
1586 $sql .= ", note_public";
1587 $sql .= ", entity";
1588 $sql .= ", fk_soc";
1589 $sql .= ", fk_projet";
1590 $sql .= ", date_creation";
1591 $sql .= ", date_livraison";
1592 $sql .= ", fk_user_author";
1593 $sql .= ", fk_statut";
1594 $sql .= ", source";
1595 $sql .= ", model_pdf";
1596 $sql .= ", fk_mode_reglement";
1597 $sql .= ", fk_cond_reglement";
1598 $sql .= ", fk_account";
1599 $sql .= ", fk_incoterms, location_incoterms";
1600 $sql .= ", fk_multicurrency";
1601 $sql .= ", multicurrency_code";
1602 $sql .= ", multicurrency_tx";
1603 $sql .= ") ";
1604 $sql .= " VALUES (";
1605 $sql .= "'(PROV)'";
1606 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1607 $sql .= ", '".$this->db->escape($this->note_private)."'";
1608 $sql .= ", '".$this->db->escape($this->note_public)."'";
1609 $sql .= ", ".setEntity($this);
1610 $sql .= ", ".((int) $this->socid);
1611 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1612 $sql .= ", '".$this->db->idate($date)."'";
1613 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1614 $sql .= ", ".((int) $user->id);
1615 $sql .= ", ".self::STATUS_DRAFT;
1616 $sql .= ", ".((int) $this->source);
1617 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1618 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1619 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1620 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1621 $sql .= ", ".(int) $this->fk_incoterms;
1622 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1623 $sql .= ", ".(int) $this->fk_multicurrency;
1624 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1625 $sql .= ", ".(float) $this->multicurrency_tx;
1626 $sql .= ")";
1627
1628 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1629 if ($this->db->query($sql)) {
1630 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1631
1632 if ($this->id) {
1633 $num = count($this->lines);
1634
1635 // insert products details into database
1636 for ($i = 0; $i < $num; $i++) {
1637 $line = $this->lines[$i];
1638 if (!is_object($line)) {
1639 $line = (object) $line;
1640 }
1641
1642 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1643
1644 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1645 $result = $this->addline(
1646 $line->desc,
1647 $line->subprice,
1648 $line->qty,
1649 $line->tva_tx,
1650 $line->localtax1_tx,
1651 $line->localtax2_tx,
1652 $line->fk_product,
1653 0,
1654 $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
1655 $line->remise_percent,
1656 'HT',
1657 0,
1658 $line->product_type,
1659 $line->info_bits,
1660 0,
1661 $line->date_start,
1662 $line->date_end,
1663 $line->array_options,
1664 $line->fk_unit,
1665 $line->multicurrency_subprice, // pu_ht_devise
1666 $line->origin, // origin
1667 $line->origin_id, // origin_id
1668 $line->rang, // rang
1669 $line->special_code
1670 );
1671 if ($result < 0) {
1672 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1673 $this->db->rollback();
1674 return -1;
1675 }
1676 }
1677
1678 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1679 $sql .= " SET ref='(PROV".$this->id.")'";
1680 $sql .= " WHERE rowid=".((int) $this->id);
1681
1682 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1683 if ($this->db->query($sql)) {
1684 // Add link with price request and supplier order
1685 if ($this->id) {
1686 $this->ref = "(PROV".$this->id.")";
1687
1688 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1689 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1690 }
1691
1692 // Add object linked
1693 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1694 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1695 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, ...))
1696 foreach ($tmp_origin_id as $origin_id) {
1697 $ret = $this->add_object_linked($origin, $origin_id);
1698 if (!$ret) {
1699 dol_print_error($this->db);
1700 $error++;
1701 }
1702 }
1703 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1704 $origin_id = $tmp_origin_id;
1705 $ret = $this->add_object_linked($origin, $origin_id);
1706 if (!$ret) {
1707 dol_print_error($this->db);
1708 $error++;
1709 }
1710 }
1711 }
1712 }
1713 }
1714
1715 if (!$error) {
1716 $result = $this->insertExtraFields();
1717 if ($result < 0) {
1718 $error++;
1719 }
1720 }
1721
1722 if (!$error && !$notrigger) {
1723 // Call trigger
1724 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1725 if ($result < 0) {
1726 $this->db->rollback();
1727
1728 return -1;
1729 }
1730 // End call triggers
1731 }
1732
1733 $this->db->commit();
1734 return $this->id;
1735 } else {
1736 $this->error = $this->db->lasterror();
1737 $this->db->rollback();
1738
1739 return -2;
1740 }
1741 } else {
1742 $this->error = 'Failed to get ID of inserted line';
1743
1744 return -1;
1745 }
1746 } else {
1747 $this->error = $this->db->lasterror();
1748 $this->db->rollback();
1749
1750 return -1;
1751 }
1752 }
1753
1761 public function update(User $user, $notrigger = 0)
1762 {
1763 global $conf;
1764
1765 $error = 0;
1766
1767 // Clean parameters
1768 if (isset($this->ref)) {
1769 $this->ref = trim($this->ref);
1770 }
1771 if (isset($this->ref_supplier)) {
1772 $this->ref_supplier = trim($this->ref_supplier);
1773 }
1774 if (isset($this->note_private)) {
1775 $this->note_private = trim($this->note_private);
1776 }
1777 if (isset($this->note_public)) {
1778 $this->note_public = trim($this->note_public);
1779 }
1780 if (isset($this->model_pdf)) {
1781 $this->model_pdf = trim($this->model_pdf);
1782 }
1783 if (isset($this->import_key)) {
1784 $this->import_key = trim($this->import_key);
1785 }
1786
1787 // Update request
1788 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1789
1790 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1791 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1792 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1793 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1794 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1795 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1796 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1797 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1798 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1799 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1800 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1801 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1802 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1803 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1804 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1805 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1806 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1807 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1808 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1809 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1810 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1811 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1812 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1813 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1814 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1815
1816 $sql .= " WHERE rowid=".((int) $this->id);
1817
1818 $this->db->begin();
1819
1820 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1821 $resql = $this->db->query($sql);
1822 if (!$resql) {
1823 $error++;
1824 $this->errors[] = "Error ".$this->db->lasterror();
1825 }
1826
1827 if (!$error) {
1828 $result = $this->insertExtraFields();
1829 if ($result < 0) {
1830 $error++;
1831 }
1832 }
1833
1834 if (!$error && !$notrigger) {
1835 // Call trigger
1836 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1837 if ($result < 0) {
1838 $error++;
1839 }
1840 // End call triggers
1841 }
1842
1843 // Commit or rollback
1844 if ($error) {
1845 foreach ($this->errors as $errmsg) {
1846 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1847 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1848 }
1849 $this->db->rollback();
1850 return -1 * $error;
1851 } else {
1852 $this->db->commit();
1853 return 1;
1854 }
1855 }
1856
1865 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1866 {
1867 global $conf, $user, $hookmanager;
1868
1869 $error = 0;
1870
1871 $this->db->begin();
1872
1873 // get extrafields so they will be clone
1874 foreach ($this->lines as $line) {
1875 $line->fetch_optionals();
1876 }
1877
1878 // Load source object
1879 $objFrom = clone $this;
1880
1881 // Change socid if needed
1882 if (!empty($socid) && $socid != $this->socid) {
1883 $objsoc = new Societe($this->db);
1884
1885 if ($objsoc->fetch($socid) > 0) {
1886 $this->socid = $objsoc->id;
1887 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1888 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1889 $this->fk_project = 0;
1890 $this->fk_delivery_address = 0;
1891 }
1892
1893 // TODO Change product price if multi-prices
1894 }
1895
1896 $this->id = 0;
1897 $this->statut = self::STATUS_DRAFT;
1898
1899 // Clear fields
1900 $this->user_author_id = $user->id;
1901 $this->user_validation_id = 0;
1902
1903 $this->date = dol_now();
1904 $this->date_creation = 0;
1905 $this->date_validation = 0;
1906 $this->date_commande = 0;
1907 $this->ref_supplier = '';
1908 $this->user_approve_id = 0;
1909 $this->user_approve_id2 = 0;
1910 $this->date_approve = 0;
1911 $this->date_approve2 = 0;
1912
1913 // Create clone
1914 $this->context['createfromclone'] = 'createfromclone';
1915 $result = $this->create($user, $notrigger);
1916 if ($result < 0) {
1917 $error++;
1918 }
1919
1920 if (!$error) {
1921 // Hook of thirdparty module
1922 if (is_object($hookmanager)) {
1923 $parameters = array('objFrom' => $objFrom);
1924 $action = '';
1925 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1926 if ($reshook < 0) {
1927 $this->setErrorsFromObject($hookmanager);
1928 $error++;
1929 }
1930 }
1931 }
1932
1933 unset($this->context['createfromclone']);
1934
1935 // End
1936 if (!$error) {
1937 $this->db->commit();
1938 return $this->id;
1939 } else {
1940 $this->db->rollback();
1941 return -1;
1942 }
1943 }
1944
1974 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)
1975 {
1976 global $langs, $mysoc, $conf;
1977
1978 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");
1979 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1980
1981 if ($this->statut == self::STATUS_DRAFT) {
1982 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1983
1984 // Clean parameters
1985 if (empty($qty)) {
1986 $qty = 0;
1987 }
1988 if (!$info_bits) {
1989 $info_bits = 0;
1990 }
1991 if (empty($txtva)) {
1992 $txtva = 0;
1993 }
1994 if (empty($rang)) {
1995 $rang = 0;
1996 }
1997 if (empty($txlocaltax1)) {
1998 $txlocaltax1 = 0;
1999 }
2000 if (empty($txlocaltax2)) {
2001 $txlocaltax2 = 0;
2002 }
2003 if (empty($remise_percent)) {
2004 $remise_percent = 0;
2005 }
2006
2007 $remise_percent = price2num($remise_percent);
2008 $qty = price2num($qty);
2009 $pu_ht = price2num($pu_ht);
2010 $pu_ht_devise = price2num($pu_ht_devise);
2011 $pu_ttc = price2num($pu_ttc);
2012 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2013 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2014 }
2015 $txlocaltax1 = price2num($txlocaltax1);
2016 $txlocaltax2 = price2num($txlocaltax2);
2017 if ($price_base_type == 'HT') {
2018 $pu = $pu_ht;
2019 } else {
2020 $pu = $pu_ttc;
2021 }
2022 $desc = trim($desc);
2023
2024 // Check parameters
2025 if ($qty < 0 && !$fk_product) {
2026 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
2027 return -1;
2028 }
2029 if ($type < 0) {
2030 return -1;
2031 }
2032 if ($date_start && $date_end && $date_start > $date_end) {
2033 $langs->load("errors");
2034 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2035 return -1;
2036 }
2037
2038
2039 $this->db->begin();
2040
2041 $product_type = $type;
2042 $label = ''; // deprecated
2043
2044 if ($fk_product > 0) {
2045 if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
2046 // Check quantity is enough
2047 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);
2048 $prod = new ProductFournisseur($this->db);
2049 if ($prod->fetch($fk_product) > 0) {
2050 $product_type = $prod->type;
2051 $label = $prod->label;
2052
2053 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2054 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2055 $result = $prod->get_buyprice($fk_prod_fourn_price, $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
2056
2057 // If supplier order created from sales order, we take best supplier price
2058 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2059 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2060 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2061 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2062 // is remise percent not keyed but present for the product we add it
2063 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2064 $remise_percent = $prod->remise_percent;
2065 }
2066 }
2067 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2068 $langs->load("errors");
2069 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2070 $this->db->rollback();
2071 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2072 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2073 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2074 return -1;
2075 }
2076 if ($result == -1) {
2077 $langs->load("errors");
2078 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2079 $this->db->rollback();
2080 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2081 return -1;
2082 }
2083 if ($result < -1) {
2084 $this->error = $prod->error;
2085 $this->db->rollback();
2086 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2087 return -1;
2088 }
2089 } else {
2090 $this->error = $prod->error;
2091 $this->db->rollback();
2092 return -1;
2093 }
2094 }
2095
2096 // Predefine quantity according to packaging
2097 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2098 $prod = new Product($this->db);
2099 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2100
2101 if ($qty < $prod->packaging) {
2102 $qty = $prod->packaging;
2103 } else {
2104 if (!empty($prod->packaging) && (fmod((float) $qty, $prod->packaging) > 0.000001)) {
2105 $coeff = intval((float) $qty / $prod->packaging) + 1;
2106 $qty = (float) $prod->packaging * $coeff;
2107 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2108 }
2109 }
2110 }
2111 }
2112
2113 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2114 $pu = 0;
2115 }
2116
2117 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2118
2119 // Clean vat code
2120 $reg = array();
2121 $vat_src_code = '';
2122 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2123 $vat_src_code = $reg[1];
2124 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2125 }
2126
2127 // Calcul du total TTC et de la TVA pour la ligne a partir de
2128 // qty, pu, remise_percent et txtva
2129 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2130 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2131
2132 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2133
2134 $total_ht = $tabprice[0];
2135 $total_tva = $tabprice[1];
2136 $total_ttc = $tabprice[2];
2137 $total_localtax1 = $tabprice[9];
2138 $total_localtax2 = $tabprice[10];
2139 $pu = $pu_ht = $tabprice[3];
2140
2141 // MultiCurrency
2142 $multicurrency_total_ht = $tabprice[16];
2143 $multicurrency_total_tva = $tabprice[17];
2144 $multicurrency_total_ttc = $tabprice[18];
2145 $pu_ht_devise = $tabprice[19];
2146
2147 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2148 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2149
2150 if ($rang < 0) {
2151 $rangmax = $this->line_max();
2152 $rang = $rangmax + 1;
2153 }
2154
2155 // Insert line
2156 $this->line = new CommandeFournisseurLigne($this->db);
2157
2158 $this->line->context = $this->context;
2159
2160 $this->line->fk_commande = $this->id;
2161 $this->line->label = $label;
2162 $this->line->ref_fourn = $ref_supplier;
2163 $this->line->ref_supplier = $ref_supplier;
2164 $this->line->desc = $desc;
2165 $this->line->qty = $qty;
2166 $this->line->tva_tx = $txtva;
2167 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2168 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2169 $this->line->localtax1_type = $localtax1_type;
2170 $this->line->localtax2_type = $localtax2_type;
2171 $this->line->fk_product = $fk_product;
2172 $this->line->product_type = $product_type;
2173 $this->line->remise_percent = $remise_percent;
2174 $this->line->subprice = $pu_ht;
2175 $this->line->rang = $rang;
2176 $this->line->info_bits = $info_bits;
2177
2178 $this->line->vat_src_code = $vat_src_code;
2179 $this->line->total_ht = $total_ht;
2180 $this->line->total_tva = $total_tva;
2181 $this->line->total_localtax1 = $total_localtax1;
2182 $this->line->total_localtax2 = $total_localtax2;
2183 $this->line->total_ttc = $total_ttc;
2184 $this->line->product_type = $type;
2185 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2186 $this->line->origin = $origin;
2187 $this->line->origin_id = $origin_id;
2188 $this->line->fk_unit = $fk_unit;
2189
2190 $this->line->date_start = $date_start;
2191 $this->line->date_end = $date_end;
2192
2193 // Multicurrency
2194 $this->line->fk_multicurrency = $this->fk_multicurrency;
2195 $this->line->multicurrency_code = $this->multicurrency_code;
2196 $this->line->multicurrency_subprice = $pu_ht_devise;
2197 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2198 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2199 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2200
2201 $this->line->subprice = $pu_ht;
2202 $this->line->price = $this->line->subprice;
2203
2204 $this->line->remise_percent = $remise_percent;
2205
2206 if (is_array($array_options) && count($array_options) > 0) {
2207 $this->line->array_options = $array_options;
2208 }
2209
2210 $result = $this->line->insert($notrigger);
2211 if ($result > 0) {
2212 // Reorder if child line
2213 if (!empty($this->line->fk_parent_line)) {
2214 $this->line_order(true, 'DESC');
2215 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2216 $linecount = count($this->lines);
2217 for ($ii = $rang; $ii <= $linecount; $ii++) {
2218 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2219 }
2220 }
2221
2222 // Mise a jour information denormalisees au niveau de la commande meme
2223 $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.
2224 if ($result > 0) {
2225 $this->db->commit();
2226 return $this->line->id;
2227 } else {
2228 $this->db->rollback();
2229 return -1;
2230 }
2231 } else {
2232 $this->error = $this->line->error;
2233 $this->errors = $this->line->errors;
2234 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2235 $this->db->rollback();
2236 return -1;
2237 }
2238 }
2239 return -1;
2240 }
2241
2242
2260 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2261 {
2262 global $conf, $langs;
2263
2264 $error = 0;
2265 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2266
2267 // Check parameters (if test are wrong here, there is bug into caller)
2268 if ($entrepot <= 0) {
2269 $this->error = 'ErrorBadValueForParameterWarehouse';
2270 return -1;
2271 }
2272 if ($qty == 0) {
2273 $this->error = 'ErrorBadValueForParameterQty';
2274 return -1;
2275 }
2276
2277 $dispatchstatus = 1;
2278 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2279 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2280 }
2281
2282 $now = dol_now();
2283
2284 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2285
2286 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2287 $this->db->begin();
2288
2289 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2290 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2291 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2292 $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($fk_reception)."'" : "null");
2293 $sql .= ")";
2294
2295 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2296 $resql = $this->db->query($sql);
2297 if ($resql) {
2298 if (!$notrigger) {
2299 global $conf, $langs, $user;
2300 // Call trigger
2301 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2302 if ($result < 0) {
2303 $error++;
2304 }
2305 // End call triggers
2306 }
2307 } else {
2308 $this->error = $this->db->lasterror();
2309 $error++;
2310 }
2311
2312 // If module stock is enabled and the stock increase is done on purchase order dispatching
2313 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2314 $mouv = new MouvementStock($this->db);
2315 if ($product > 0) {
2316 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2317 $mouv->origin = &$this;
2318 $mouv->setOrigin($this->element, $this->id);
2319
2320 // Method change if qty < 0
2321 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2322 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2323 } else {
2324 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2325 }
2326
2327 if ($result < 0) {
2328 $this->error = $mouv->error;
2329 $this->errors = $mouv->errors;
2330 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2331 $error++;
2332 }
2333 }
2334 }
2335
2336 if ($error == 0) {
2337 $this->db->commit();
2338 return 1;
2339 } else {
2340 $this->db->rollback();
2341 return -1;
2342 }
2343 } else {
2344 $this->error = 'BadStatusForObject';
2345 return -2;
2346 }
2347 }
2348
2356 public function deleteLine($idline, $notrigger = 0)
2357 {
2358 global $user;
2359
2360 if ($this->statut == 0) {
2361 $line = new CommandeFournisseurLigne($this->db);
2362
2363 if ($line->fetch($idline) <= 0) {
2364 return 0;
2365 }
2366
2367 // check if not yet received
2368 $dispatchedLines = $this->getDispachedLines();
2369 foreach ($dispatchedLines as $dispatchLine) {
2370 if ($dispatchLine['orderlineid'] == $idline) {
2371 $this->error = "LineAlreadyDispatched";
2372 $this->errors[] = $this->error;
2373 return -3;
2374 }
2375 }
2376
2377 if ($line->delete($user, $notrigger) > 0) {
2378 $this->update_price(1);
2379 return 1;
2380 } else {
2381 $this->setErrorsFromObject($line);
2382 return -1;
2383 }
2384 } else {
2385 return -2;
2386 }
2387 }
2388
2396 public function delete(User $user, $notrigger = 0)
2397 {
2398 global $langs, $conf;
2399 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2400
2401 $error = 0;
2402
2403 $this->db->begin();
2404
2405 if (empty($notrigger)) {
2406 // Call trigger
2407 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2408 if ($result < 0) {
2409 $this->errors[] = 'ErrorWhenRunningTrigger';
2410 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2411 $this->db->rollback();
2412 return -1;
2413 }
2414 // End call triggers
2415 }
2416
2417 // Test we can delete
2418 $this->fetchObjectLinked(null, 'order_supplier');
2419 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2420 foreach ($this->linkedObjects['reception'] as $element) {
2421 if ($element->statut >= 0) {
2422 $this->errors[] = $langs->trans('ReceptionExist');
2423 $error++;
2424 break;
2425 }
2426 }
2427 }
2428
2429 $main = $this->db->prefix().'commande_fournisseurdet';
2430 $ef = $main."_extrafields";
2431 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2432 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2433 if (!$this->db->query($sql)) {
2434 $this->error = $this->db->lasterror();
2435 $this->errors[] = $this->db->lasterror();
2436 $error++;
2437 }
2438
2439 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2440 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2441 if (!$this->db->query($sql)) {
2442 $this->error = $this->db->lasterror();
2443 $this->errors[] = $this->db->lasterror();
2444 $error++;
2445 }
2446
2447 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2448 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2449 if ($resql = $this->db->query($sql)) {
2450 if ($this->db->affected_rows($resql) < 1) {
2451 $this->error = $this->db->lasterror();
2452 $this->errors[] = $this->db->lasterror();
2453 $error++;
2454 }
2455 } else {
2456 $this->error = $this->db->lasterror();
2457 $this->errors[] = $this->db->lasterror();
2458 $error++;
2459 }
2460
2461 // Remove extrafields
2462 if (!$error) {
2463 $result = $this->deleteExtraFields();
2464 if ($result < 0) {
2465 $this->error = 'FailToDeleteExtraFields';
2466 $this->errors[] = 'FailToDeleteExtraFields';
2467 $error++;
2468 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2469 }
2470 }
2471
2472 // Delete linked object
2473 $res = $this->deleteObjectLinked();
2474 if ($res < 0) {
2475 $this->error = 'FailToDeleteObjectLinked';
2476 $this->errors[] = 'FailToDeleteObjectLinked';
2477 $error++;
2478 }
2479
2480 if (!$error) {
2481 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2482 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2483 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2484
2485 // We remove directory
2486 $ref = dol_sanitizeFileName($this->ref);
2487 if ($conf->fournisseur->commande->dir_output) {
2488 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2489 $file = $dir."/".$ref.".pdf";
2490 if (file_exists($file)) {
2491 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2492 $this->error = 'ErrorFailToDeleteFile';
2493 $this->errors[] = 'ErrorFailToDeleteFile';
2494 $error++;
2495 }
2496 }
2497 if (file_exists($dir)) {
2498 $res = @dol_delete_dir_recursive($dir);
2499 if (!$res) {
2500 $this->error = 'ErrorFailToDeleteDir';
2501 $this->errors[] = 'ErrorFailToDeleteDir';
2502 $error++;
2503 }
2504 }
2505 }
2506 }
2507
2508 if (!$error) {
2509 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2510 $this->db->commit();
2511 return 1;
2512 } else {
2513 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2514 $this->db->rollback();
2515 return -$error;
2516 }
2517 }
2518
2519
2528 public function getDispachedLines($status = -1)
2529 {
2530 $ret = array();
2531
2532 // List of already dispatched lines
2533 $sql = "SELECT p.ref, p.label,";
2534 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2535 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2536 $sql .= " FROM ".$this->db->prefix()."product as p,";
2537 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2538 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2539 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2540 $sql .= " AND cfd.fk_product = p.rowid";
2541 if ($status >= 0) {
2542 $sql .= " AND cfd.status = ".((int) $status);
2543 }
2544 $sql .= " ORDER BY cfd.rowid ASC";
2545
2546 $resql = $this->db->query($sql);
2547 if ($resql) {
2548 $num = $this->db->num_rows($resql);
2549 $i = 0;
2550
2551 while ($i < $num) {
2552 $objp = $this->db->fetch_object($resql);
2553 if ($objp) {
2554 $ret[] = array(
2555 'id' => $objp->dispatchedlineid,
2556 'productid' => $objp->fk_product,
2557 'warehouseid' => $objp->warehouse_id,
2558 'qty' => $objp->qty,
2559 'orderlineid' => $objp->fk_elementdet
2560 );
2561 }
2562
2563 $i++;
2564 }
2565 } else {
2566 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2567 }
2568
2569 return $ret;
2570 }
2571
2572 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2582 public function Livraison($user, $date, $type, $comment)
2583 {
2584 // phpcs:enable
2585 global $conf, $langs;
2586
2587 $result = 0;
2588 $error = 0;
2589
2590 dol_syslog(get_class($this)."::Livraison");
2591
2592 $usercanreceive = 0;
2593 if (!isModEnabled('reception')) {
2594 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2595 } else {
2596 $usercanreceive = $user->hasRight("reception", "creer");
2597 }
2598
2599 if ($usercanreceive) {
2600 // Define the new status
2601 if ($type == 'par') {
2603 } elseif ($type == 'tot') {
2605 } elseif ($type == 'nev') {
2607 } elseif ($type == 'can') {
2609 } else {
2610 $error++;
2611 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2612 return -2;
2613 }
2614
2615 // Some checks to accept the record
2616 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2617 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2618 if (!$error && ($type == 'tot')) {
2619 $dispatchedlinearray = $this->getDispachedLines(0);
2620 if (count($dispatchedlinearray) > 0) {
2621 $result = -1;
2622 $error++;
2623 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2624 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2625 }
2626 }
2627 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)
2628 $dispatcheddenied = $this->getDispachedLines(2);
2629 if (count($dispatchedlinearray) > 0) {
2630 $result = -1;
2631 $error++;
2632 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2633 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2634 }
2635 }
2636 }
2637
2638 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2639
2640 if (empty($error)) {
2641 $this->db->begin();
2642
2643 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2644 $sql .= " SET fk_statut = ".((int) $statut);
2645 $sql .= " WHERE rowid = ".((int) $this->id);
2646 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2647
2648 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2649 $resql = $this->db->query($sql);
2650 if ($resql) {
2651 $result = 1;
2652 $old_statut = $this->statut;
2653 $this->statut = $statut;
2654 $this->context['actionmsg2'] = $comment;
2655
2656 // Call trigger
2657 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2658 if ($result_trigger < 0) {
2659 $error++;
2660 }
2661 // End call triggers
2662
2663 if (empty($error)) {
2664 $this->db->commit();
2665 } else {
2666 $this->statut = $old_statut;
2667 $this->db->rollback();
2668 $this->error = $this->db->lasterror();
2669 $result = -1;
2670 }
2671 } else {
2672 $this->db->rollback();
2673 $this->error = $this->db->lasterror();
2674 $result = -1;
2675 }
2676 }
2677 } else {
2678 $this->error = $langs->trans('NotAuthorized');
2679 $this->errors[] = $langs->trans('NotAuthorized');
2680 dol_syslog(get_class($this)."::Livraison Not Authorized");
2681 $result = -3;
2682 }
2683 return $result;
2684 }
2685
2686 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2696 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2697 {
2698 // phpcs:enable
2699 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2700 }
2701
2710 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2711 {
2712 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2713 $error = 0;
2714
2715 $this->db->begin();
2716
2717 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2718 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2719 $sql .= " WHERE rowid = ".((int) $this->id);
2720
2721 dol_syslog(__METHOD__, LOG_DEBUG);
2722 $resql = $this->db->query($sql);
2723 if (!$resql) {
2724 $this->errors[] = $this->db->error();
2725 $error++;
2726 }
2727
2728 if (!$error) {
2729 $this->oldcopy = clone $this;
2730 $this->delivery_date = $delivery_date;
2731 }
2732
2733 if (!$notrigger && empty($error)) {
2734 // Call trigger
2735 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2736 if ($result < 0) {
2737 $error++;
2738 }
2739 // End call triggers
2740 }
2741
2742 if (!$error) {
2743 $this->db->commit();
2744 return 1;
2745 } else {
2746 foreach ($this->errors as $errmsg) {
2747 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2748 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2749 }
2750 $this->db->rollback();
2751 return -1 * $error;
2752 }
2753 } else {
2754 return -2;
2755 }
2756 }
2757
2758 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2767 public function set_id_projet($user, $id_projet, $notrigger = 0)
2768 {
2769 // phpcs:enable
2770 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2771 $error = 0;
2772
2773 $this->db->begin();
2774
2775 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2776 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2777 $sql .= " WHERE rowid = ".((int) $this->id);
2778
2779 dol_syslog(__METHOD__, LOG_DEBUG);
2780 $resql = $this->db->query($sql);
2781 if (!$resql) {
2782 $this->errors[] = $this->db->error();
2783 $error++;
2784 }
2785
2786 if (!$error) {
2787 $this->oldcopy = clone $this;
2788 $this->fk_projet = $id_projet;
2789 $this->fk_project = $id_projet;
2790 }
2791
2792 if (!$notrigger && empty($error)) {
2793 // Call trigger
2794 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2795 if ($result < 0) {
2796 $error++;
2797 }
2798 // End call triggers
2799 }
2800
2801 if (!$error) {
2802 $this->db->commit();
2803 return 1;
2804 } else {
2805 foreach ($this->errors as $errmsg) {
2806 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2807 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2808 }
2809 $this->db->rollback();
2810 return -1 * $error;
2811 }
2812 } else {
2813 return -2;
2814 }
2815 }
2816
2825 public function updateFromCommandeClient($user, $idc, $comclientid)
2826 {
2827 $comclient = new Commande($this->db);
2828 $comclient->fetch($comclientid);
2829
2830 $this->id = $idc;
2831
2832 $this->lines = array();
2833
2834 $num = count($comclient->lines);
2835 for ($i = 0; $i < $num; $i++) {
2836 $prod = new Product($this->db);
2837 $label = '';
2838 $ref = '';
2839 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2840 $label = $prod->label;
2841 $ref = $prod->ref;
2842 }
2843
2844 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2845 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2846 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2847 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2848 $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);
2849 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2850 if ($this->db->query($sql)) {
2851 $this->update_price(1);
2852 }
2853 }
2854
2855 return 1;
2856 }
2857
2865 public function setStatus($user, $status)
2866 {
2867 global $conf, $langs;
2868 $error = 0;
2869
2870 $this->db->begin();
2871
2872 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2873 $sql .= " SET fk_statut = ".$status;
2874 $sql .= " WHERE rowid = ".((int) $this->id);
2875
2876 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2877 $resql = $this->db->query($sql);
2878 if ($resql) {
2879 // Trigger names for each status
2880 $triggerName = array();
2881 $triggerName[0] = 'DRAFT';
2882 $triggerName[1] = 'VALIDATED';
2883 $triggerName[2] = 'APPROVED';
2884 $triggerName[3] = 'ORDERED'; // Ordered
2885 $triggerName[4] = 'RECEIVED_PARTIALLY';
2886 $triggerName[5] = 'RECEIVED_COMPLETELY';
2887 $triggerName[6] = 'CANCELED';
2888 $triggerName[7] = 'CANCELED';
2889 $triggerName[9] = 'REFUSED';
2890
2891 // Call trigger
2892 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2893 if ($result < 0) {
2894 $error++;
2895 }
2896 // End call triggers
2897 } else {
2898 $error++;
2899 $this->error = $this->db->lasterror();
2900 dol_syslog(get_class($this)."::setStatus ".$this->error);
2901 }
2902
2903 if (!$error) {
2904 $this->statut = $status;
2905 $this->db->commit();
2906 return 1;
2907 } else {
2908 $this->db->rollback();
2909 return -1;
2910 }
2911 }
2912
2936 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 = '')
2937 {
2938 global $mysoc, $conf, $langs;
2939 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2940 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2941
2942 $error = 0;
2943
2944 if ($this->statut == self::STATUS_DRAFT) {
2945 // Clean parameters
2946 if (empty($qty)) {
2947 $qty = 0;
2948 }
2949 if (empty($info_bits)) {
2950 $info_bits = 0;
2951 }
2952 if (empty($txtva)) {
2953 $txtva = 0;
2954 }
2955 if (empty($txlocaltax1)) {
2956 $txlocaltax1 = 0;
2957 }
2958 if (empty($txlocaltax2)) {
2959 $txlocaltax2 = 0;
2960 }
2961 if (empty($remise_percent)) {
2962 $remise_percent = 0;
2963 }
2964
2965 $remise_percent = (float) price2num($remise_percent);
2966 $qty = price2num($qty);
2967 if (!$qty) {
2968 $qty = 1;
2969 }
2970 $pu = price2num($pu);
2971 $pu_ht_devise = price2num($pu_ht_devise);
2972 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2973 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2974 }
2975 $txlocaltax1 = (float) price2num($txlocaltax1);
2976 $txlocaltax2 = (float) price2num($txlocaltax2);
2977
2978 // Check parameters
2979 if ($type < 0) {
2980 return -1;
2981 }
2982 if ($date_start && $date_end && $date_start > $date_end) {
2983 $langs->load("errors");
2984 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2985 return -1;
2986 }
2987
2988 $this->db->begin();
2989
2990 // Calcul du total TTC et de la TVA pour la ligne a partir de
2991 // qty, pu, remise_percent et txtva
2992 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2993 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2994
2995 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2996
2997 // Clean vat code
2998 $reg = array();
2999 $vat_src_code = '';
3000 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3001 $vat_src_code = $reg[1];
3002 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3003 }
3004
3005 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
3006 $total_ht = $tabprice[0];
3007 $total_tva = $tabprice[1];
3008 $total_ttc = $tabprice[2];
3009 $total_localtax1 = $tabprice[9];
3010 $total_localtax2 = $tabprice[10];
3011 $pu_ht = $tabprice[3];
3012 $pu_tva = $tabprice[4];
3013 $pu_ttc = $tabprice[5];
3014
3015 // MultiCurrency
3016 $multicurrency_total_ht = $tabprice[16];
3017 $multicurrency_total_tva = $tabprice[17];
3018 $multicurrency_total_ttc = $tabprice[18];
3019 $pu_ht_devise = $tabprice[19];
3020
3021 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3022 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3023
3024 // Fetch current line from the database and then clone the object and set it in $oldline property
3025 $this->line = new CommandeFournisseurLigne($this->db);
3026 $this->line->fetch($rowid);
3027
3028 $oldline = clone $this->line;
3029 $this->line->oldline = $oldline;
3030
3031 $this->line->context = $this->context;
3032
3033 $this->line->fk_commande = $this->id;
3034 //$this->line->label=$label;
3035 $this->line->desc = $desc;
3036
3037 // redefine quantity according to packaging
3038 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3039 if ($qty < $this->line->packaging) {
3040 $qty = $this->line->packaging;
3041 } else {
3042 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
3043 $coeff = intval($qty / $this->line->packaging) + 1;
3044 $qty = $this->line->packaging * $coeff;
3045 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3046 }
3047 }
3048 }
3049
3050 $this->line->qty = $qty;
3051 $this->line->ref_supplier = $ref_supplier;
3052
3053 $this->line->vat_src_code = $vat_src_code;
3054 $this->line->tva_tx = $txtva;
3055 $this->line->localtax1_tx = $txlocaltax1;
3056 $this->line->localtax2_tx = $txlocaltax2;
3057 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3058 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3059 $this->line->remise_percent = $remise_percent;
3060 $this->line->subprice = $pu_ht;
3061 $this->line->info_bits = $info_bits;
3062 $this->line->total_ht = $total_ht;
3063 $this->line->total_tva = $total_tva;
3064 $this->line->total_localtax1 = $total_localtax1;
3065 $this->line->total_localtax2 = $total_localtax2;
3066 $this->line->total_ttc = $total_ttc;
3067 $this->line->product_type = $type;
3068 $this->line->special_code = $oldline->special_code;
3069 $this->line->rang = $oldline->rang;
3070 $this->line->origin = $this->origin;
3071 $this->line->fk_unit = $fk_unit;
3072
3073 $this->line->date_start = $date_start;
3074 $this->line->date_end = $date_end;
3075
3076 // Multicurrency
3077 $this->line->fk_multicurrency = $this->fk_multicurrency;
3078 $this->line->multicurrency_code = $this->multicurrency_code;
3079 $this->line->multicurrency_subprice = $pu_ht_devise;
3080 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3081 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3082 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3083
3084 $this->line->subprice = $pu_ht;
3085 $this->line->price = $this->line->subprice;
3086
3087 $this->line->remise_percent = $remise_percent;
3088
3089 if (is_array($array_options) && count($array_options) > 0) {
3090 // We replace values in this->line->array_options only for entries defined into $array_options
3091 foreach ($array_options as $key => $value) {
3092 $this->line->array_options[$key] = $array_options[$key];
3093 }
3094 }
3095
3096 $result = $this->line->update($notrigger);
3097
3098
3099 // Mise a jour info denormalisees au niveau facture
3100 if ($result >= 0) {
3101 $this->update_price('1', 'auto');
3102 $this->db->commit();
3103 return $result;
3104 } else {
3105 $this->error = $this->db->lasterror();
3106 $this->db->rollback();
3107 return -1;
3108 }
3109 } else {
3110 $this->error = "Order status makes operation forbidden";
3111 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3112 return -2;
3113 }
3114 }
3115
3116
3124 public function initAsSpecimen()
3125 {
3126 global $user, $langs, $conf;
3127
3128 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3129
3130 dol_syslog(get_class($this)."::initAsSpecimen");
3131
3132 $now = dol_now();
3133
3134 // Find first product
3135 $prodid = 0;
3136 $product = new ProductFournisseur($this->db);
3137 $sql = "SELECT rowid";
3138 $sql .= " FROM ".$this->db->prefix()."product";
3139 $sql .= " WHERE entity IN (".getEntity('product').")";
3140 $sql .= $this->db->order("rowid", "ASC");
3141 $sql .= $this->db->plimit(1);
3142 $resql = $this->db->query($sql);
3143 if ($resql) {
3144 $obj = $this->db->fetch_object($resql);
3145 $prodid = $obj->rowid;
3146 }
3147
3148 // Initialise parameters
3149 $this->id = 0;
3150 $this->ref = 'SPECIMEN';
3151 $this->specimen = 1;
3152 $this->socid = 1;
3153 $this->date = $now;
3154 $this->date_commande = $now;
3155 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3156 $this->cond_reglement_code = 'RECEP';
3157 $this->mode_reglement_code = 'CHQ';
3158
3159 $this->note_public = 'This is a comment (public)';
3160 $this->note_private = 'This is a comment (private)';
3161
3162 $this->multicurrency_tx = 1;
3163 $this->multicurrency_code = $conf->currency;
3164
3165 $this->statut = 0;
3166
3167 // Lines
3168 $nbp = 5;
3169 $xnbp = 0;
3170 while ($xnbp < $nbp) {
3171 $line = new CommandeFournisseurLigne($this->db);
3172 $line->desc = $langs->trans("Description")." ".$xnbp;
3173 $line->qty = 1;
3174 $line->subprice = 100;
3175 $line->tva_tx = 19.6;
3176 $line->localtax1_tx = 0;
3177 $line->localtax2_tx = 0;
3178 if ($xnbp == 2) {
3179 $line->total_ht = 50;
3180 $line->total_ttc = 59.8;
3181 $line->total_tva = 9.8;
3182 $line->remise_percent = 50;
3183 } else {
3184 $line->total_ht = 100;
3185 $line->total_ttc = 119.6;
3186 $line->total_tva = 19.6;
3187 $line->remise_percent = 00;
3188 }
3189 $line->fk_product = $prodid;
3190
3191 $this->lines[$xnbp] = $line;
3192
3193 $this->total_ht += $line->total_ht;
3194 $this->total_tva += $line->total_tva;
3195 $this->total_ttc += $line->total_ttc;
3196
3197 $xnbp++;
3198 }
3199
3200 return 1;
3201 }
3202
3209 public function info($id)
3210 {
3211 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3212 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3213 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3214 $sql .= ' WHERE c.rowid = '.((int) $id);
3215
3216 $result = $this->db->query($sql);
3217 if ($result) {
3218 if ($this->db->num_rows($result)) {
3219 $obj = $this->db->fetch_object($result);
3220
3221 $this->id = $obj->rowid;
3222
3223 $this->user_creation_id = $obj->fk_user_author;
3224 $this->user_validation_id = $obj->fk_user_valid;
3225 $this->user_modification_id = $obj->fk_user_modif;
3226 $this->user_approve_id = $obj->fk_user_approve;
3227 $this->user_approve_id2 = $obj->fk_user_approve2;
3228
3229 $this->date_creation = $this->db->jdate($obj->datec);
3230 $this->date_modification = $this->db->jdate($obj->datem);
3231 $this->date_approve = $this->db->jdate($obj->datea);
3232 $this->date_approve2 = $this->db->jdate($obj->datea2);
3233 $this->date_validation = $this->db->jdate($obj->date_validation);
3234 }
3235 $this->db->free($result);
3236 } else {
3237 dol_print_error($this->db);
3238 }
3239 }
3240
3246 public function loadStateBoard()
3247 {
3248 global $conf, $user;
3249
3250 $this->nb = array();
3251 $clause = "WHERE";
3252
3253 $sql = "SELECT count(co.rowid) as nb";
3254 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3255 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3256 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3257 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3258 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3259 $clause = "AND";
3260 }
3261 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3262
3263 $resql = $this->db->query($sql);
3264 if ($resql) {
3265 while ($obj = $this->db->fetch_object($resql)) {
3266 $this->nb["supplier_orders"] = $obj->nb;
3267 }
3268 $this->db->free($resql);
3269 return 1;
3270 } else {
3271 dol_print_error($this->db);
3272 $this->error = $this->db->error();
3273 return -1;
3274 }
3275 }
3276
3277 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3285 public function load_board($user, $mode = 'opened')
3286 {
3287 // phpcs:enable
3288 global $conf, $langs;
3289
3290 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3291 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3292 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3293 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3294 }
3295 $sql .= " WHERE c.entity = ".$conf->entity;
3296 if ($mode === 'awaiting') {
3297 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3298 } else {
3299 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3300 }
3301 if ($user->socid) {
3302 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3303 }
3304
3305 $resql = $this->db->query($sql);
3306 if ($resql) {
3307 $commandestatic = new CommandeFournisseur($this->db);
3308
3309 $response = new WorkboardResponse();
3310 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3311 $response->label = $langs->trans("SuppliersOrdersToProcess");
3312 $response->labelShort = $langs->trans("Opened");
3313 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3314 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=late';
3315 $response->img = img_object('', "order");
3316
3317 if ($mode === 'awaiting') {
3318 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3319 $response->labelShort = $langs->trans("AwaitingReception");
3320 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3321 }
3322
3323 while ($obj = $this->db->fetch_object($resql)) {
3324 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3325 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3326 $commandestatic->statut = $obj->fk_statut;
3327
3328 $response->nbtodo++;
3329 $response->total += $obj->total_ht;
3330
3331 if ($commandestatic->hasDelay()) {
3332 $response->nbtodolate++;
3333 }
3334 }
3335
3336 return $response;
3337 } else {
3338 $this->error = $this->db->error();
3339 return -1;
3340 }
3341 }
3342
3349 public function getInputMethod()
3350 {
3351 global $langs;
3352
3353 if ($this->methode_commande_id > 0) {
3354 $sql = "SELECT rowid, code, libelle as label";
3355 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3356 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3357
3358 $resql = $this->db->query($sql);
3359 if ($resql) {
3360 if ($this->db->num_rows($resql)) {
3361 $obj = $this->db->fetch_object($resql);
3362
3363 $string = $langs->trans($obj->code);
3364 if ($string == $obj->code) {
3365 $string = $obj->label != '-' ? $obj->label : '';
3366 }
3367 return $string;
3368 }
3369 } else {
3370 dol_print_error($this->db);
3371 }
3372 }
3373
3374 return '';
3375 }
3376
3388 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3389 {
3390 global $conf, $langs;
3391
3392 if (!dol_strlen($modele)) {
3393 $modele = ''; // No doc template/generation by default
3394
3395 if (!empty($this->model_pdf)) {
3396 $modele = $this->model_pdf;
3397 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3398 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3399 }
3400 }
3401
3402 if (empty($modele)) {
3403 return 0;
3404 } else {
3405 $langs->load("suppliers");
3406 $outputlangs->load("products");
3407
3408 $modelpath = "core/modules/supplier_order/doc/";
3409 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3410 return $result;
3411 }
3412 }
3413
3420 public function getMaxDeliveryTimeDay($langs)
3421 {
3422 if (empty($this->lines)) {
3423 return '';
3424 }
3425
3426 $tmpproductfourn = new ProductFournisseur($this->db);
3427
3428 $nb = 0;
3429 foreach ($this->lines as $line) {
3430 if ($line->fk_product > 0) {
3431 // Load delivery_time_days, return id into product_fournisseur_price
3432 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3433 if ($idp > 0) {
3434 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3435 if ($tmpproductfourn->delivery_time_days > $nb) {
3436 $nb = $tmpproductfourn->delivery_time_days;
3437 }
3438 }
3439 }
3440 }
3441
3442 if ($nb === 0) {
3443 return '';
3444 } else {
3445 return $nb.' '.$langs->trans('days');
3446 }
3447 }
3448
3453 public function getRights()
3454 {
3455 global $user;
3456
3457 return $user->hasRight("fournisseur", "commande");
3458 }
3459
3460
3469 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3470 {
3471 $tables = array(
3472 'commande_fournisseur'
3473 );
3474
3475 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3476 }
3477
3486 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3487 {
3488 $tables = array(
3489 'commande_fournisseurdet'
3490 );
3491
3492 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3493 }
3494
3502 public function hasDelay()
3503 {
3504 global $conf;
3505
3506 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3507 $now = dol_now();
3508 if (!empty($this->delivery_date)) {
3509 $date_to_test = $this->delivery_date;
3510 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3511 } else {
3512 //$date_to_test = $this->date_commande;
3513 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3514 return false;
3515 }
3516 } else {
3517 $now = dol_now();
3518 $date_to_test = $this->date_commande;
3519
3520 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3521 }
3522 }
3523
3531 public function showDelay()
3532 {
3533 global $conf, $langs;
3534
3535 $langs->load('orders');
3536
3537 $text = '';
3538
3539 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3540 if (!empty($this->delivery_date)) {
3541 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3542 } else {
3543 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3544 }
3545 } else {
3546 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3547 }
3548 if ($text) {
3549 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3550 }
3551
3552 return $text;
3553 }
3554
3555
3564 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3565 {
3566 if (isModEnabled("supplier_order")) {
3567 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3568
3569 $qtydelivered = array();
3570 $qtywished = array();
3571
3572 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3573
3574 $filter = array('t.fk_element' => $this->id);
3575 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3576 $filter['t.status'] = 1; // Restrict to lines with status validated
3577 }
3578
3579 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3580 if ($ret < 0) {
3581 $this->error = $supplierorderdispatch->error;
3582 $this->errors = $supplierorderdispatch->errors;
3583 return $ret;
3584 } else {
3585 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3586 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3587 $date_liv = dol_now();
3588
3589 // Build array with quantity deliverd by product
3590 foreach ($supplierorderdispatch->lines as $line) {
3591 $qtydelivered[$line->fk_product] += $line->qty;
3592 }
3593 foreach ($this->lines as $line) {
3594 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3595 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3596 continue;
3597 }
3598 $qtywished[$line->fk_product] += $line->qty;
3599 }
3600
3601 //Compare array
3602 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3603 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3604 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3605 //var_dump(array_keys($qtydelivered));
3606 //var_dump(array_keys($qtywished));
3607 //var_dump($diff_array);
3608 //var_dump($keysinwishednotindelivered);
3609 //var_dump($keysindeliverednotinwished);
3610 //exit;
3611
3612 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3613 if ($closeopenorder) {
3614 //$ret=$this->setStatus($user,5);
3615 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3616 if ($ret < 0) {
3617 return -1;
3618 }
3619 return 5;
3620 } else {
3621 //Diff => received partially
3622 //$ret=$this->setStatus($user,4);
3623 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3624 if ($ret < 0) {
3625 return -1;
3626 }
3627 return 4;
3628 }
3629 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3630 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3631
3632 $close = 0;
3633
3634 if (count($diff_array) > 0) {
3635 //there are some difference between the two arrays
3636
3637 //scan the array of results
3638 foreach ($diff_array as $key => $value) {
3639 //if the quantity delivered is greater or equal to wish quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
3640 if ($qtydelivered[$key] >= $qtywished[$key]) {
3641 $close++;
3642 }
3643 }
3644 }
3645
3646
3647 if ($close == count($diff_array)) {
3648 //all the products are received equal or more than the wished quantity
3649 if ($closeopenorder) {
3650 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3651 if ($ret < 0) {
3652 return -1;
3653 }
3654 return 5;
3655 } else {
3656 //Diff => received partially
3657 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3658 if ($ret < 0) {
3659 return -1;
3660 }
3661 return 4;
3662 }
3663 } else {
3664 //all the products are not received
3665 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3666 if ($ret < 0) {
3667 return -1;
3668 }
3669 return 4;
3670 }
3671 } else {
3672 //Diff => received partially
3673 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3674 if ($ret < 0) {
3675 return -1;
3676 }
3677 return 4;
3678 }
3679 }
3680 return 1;
3681 }
3682 }
3683 return 0;
3684 }
3685
3693 public function loadReceptions($filtre_statut = -1)
3694 {
3695 $this->receptions = array();
3696
3697 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3698
3699 $sql = 'SELECT cd.rowid, cd.fk_product,';
3700 $sql .= ' sum(cfd.qty) as qty';
3701 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3702 if ($filtre_statut >= 0) {
3703 $sql .= ' '.$this->db->prefix().'reception as e,';
3704 }
3705 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3706 $sql .= ' WHERE';
3707 if ($filtre_statut >= 0) {
3708 $sql .= ' cfd.fk_reception = e.rowid AND';
3709 }
3710 $sql .= ' cfd.fk_elementdet = cd.rowid';
3711 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3712 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3713 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3714 }
3715 if ($filtre_statut >= 0) {
3716 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3717 }
3718 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3719
3720 $resql = $this->db->query($sql);
3721 if ($resql) {
3722 $num = $this->db->num_rows($resql);
3723 $i = 0;
3724 while ($i < $num) {
3725 $obj = $this->db->fetch_object($resql);
3726 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3727 $i++;
3728 }
3729 $this->db->free($resql);
3730
3731 return $num;
3732 } else {
3733 $this->error = $this->db->lasterror();
3734 return -1;
3735 }
3736 }
3737
3745 public function getKanbanView($option = '', $arraydata = null)
3746 {
3747 global $langs;
3748
3749 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3750
3751 $return = '<div class="box-flex-item box-flex-grow-zero">';
3752 $return .= '<div class="info-box info-box-sm">';
3753 $return .= '<span class="info-box-icon bg-infobox-action">';
3754 $return .= img_picto('', $this->picto);
3755 $return .= '</span>';
3756 $return .= '<div class="info-box-content">';
3757 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3758 if ($selected >= 0) {
3759 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3760 }
3761 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3762 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3763 }
3764 if (property_exists($this, 'billed')) {
3765 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3766 }
3767 if (method_exists($this, 'getLibStatut')) {
3768 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3769 }
3770 $return .= '</div>';
3771 $return .= '</div>';
3772 $return .= '</div>';
3773 return $return;
3774 }
3775}
$object ref
Definition info.php:79
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.
getNextNumRef($soc)
Returns the next order reference not used, based on the numbering model defined within COMMANDE_SUPPL...
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.
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.
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 getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage 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:162
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return 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...
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88