dolibarr 21.0.0-beta
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
122 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
123 // -> 7=Canceled/Never received -> (reopen) 3=Process running
124 // -> 6=Canceled -> (reopen) 2=Approved
125 // -> 9=Refused -> (reopen) 1=Validated
126 // Note: billed or not is on another field "billed"
127
131 public $billed;
132
136 public $socid;
137
141 public $fourn_id;
142
146 public $date;
147
151 public $date_valid;
152
156 public $date_approve;
157
162 public $date_approve2;
163
167 public $date_commande;
168
173 public $remise_percent;
177 public $methode_commande_id;
181 public $methode_commande;
182
186 public $delivery_date;
187
191 public $total_ht;
192
196 public $total_tva;
197
201 public $total_localtax1;
202
206 public $total_localtax2;
207
211 public $total_ttc;
212
216 public $source;
217
221 public $fk_project;
222
226 public $cond_reglement_id;
227
231 public $cond_reglement_code;
232
236 public $cond_reglement_label;
237
241 public $cond_reglement_doc;
242
246 public $fk_account;
247
251 public $mode_reglement_id;
252
256 public $mode_reglement_code;
257
261 public $mode_reglement;
262
266 public $user_author_id;
267
271 public $user_approve_id;
272
277 public $user_approve_id2;
278
282 public $refuse_note;
283
287 public $extraparams = array();
288
292 public $lines = array();
293
297 public $line;
298
302 public $origin;
306 public $origin_id;
307 public $linked_objects = array();
308
312 public $date_lim_reglement;
316 public $receptions = array();
317
318 // Multicurrency
322 public $fk_multicurrency;
323
327 public $multicurrency_code;
328
332 public $multicurrency_tx;
333
337 public $multicurrency_total_ht;
338
342 public $multicurrency_total_tva;
343
347 public $multicurrency_total_ttc;
348
376 public $fields = array(
377 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
378 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
379 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => 0, 'position' => 35),
380 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
381 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
382 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
383 'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
384 'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
385 'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
386 'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
387 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
388 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
389 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
390 'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
391 'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
392 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
393 'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
394 'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
395 'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
396 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
397 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
398 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
399 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
400 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
401 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
402 'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
403 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
404 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
405 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
406 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
407 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
408 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
409 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
410 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
411 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
412 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
413 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
414 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
415 'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
416 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
417 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
418 'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
419 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
420 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
421 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
422 );
423
424
428 const STATUS_DRAFT = 0;
429
434
439
444
449
454
459
464
468 const STATUS_REFUSED = 9;
469
470
475
481 public function __construct($db)
482 {
483 $this->db = $db;
484
485 $this->ismultientitymanaged = 1;
486 }
487
488
496 public function fetch($id, $ref = '')
497 {
498 // Check parameters
499 if (empty($id) && empty($ref)) {
500 return -1;
501 }
502
503 $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,";
504 $sql .= " c.localtax1, c.localtax2, ";
505 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
506 $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,";
507 $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,";
508 $sql .= " c.fk_account,";
509 $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
510 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
511 $sql .= " cm.libelle as methode_commande,";
512 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
513 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
514 $sql .= ', c.fk_incoterms, c.location_incoterms';
515 $sql .= ', i.libelle as label_incoterms';
516 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
517 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
518 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
519 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
520 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
521
522 if (empty($id)) {
523 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
524 } else {
525 $sql .= " WHERE c.rowid=".((int) $id);
526 }
527
528 if ($ref) {
529 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
530 }
531
532 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
533 $resql = $this->db->query($sql);
534 if ($resql) {
535 $obj = $this->db->fetch_object($resql);
536 if (!$obj) {
537 $this->error = 'Bill with id '.$id.' not found';
538 dol_syslog(get_class($this).'::fetch '.$this->error);
539 return 0;
540 }
541
542 $this->id = $obj->rowid;
543 $this->entity = $obj->entity;
544
545 $this->ref = $obj->ref;
546 $this->ref_supplier = $obj->ref_supplier;
547 $this->socid = $obj->fk_soc;
548 $this->fourn_id = $obj->fk_soc;
549 $this->statut = $obj->status; // deprecated
550 $this->status = $obj->status;
551 $this->billed = $obj->billed;
552 $this->user_author_id = $obj->user_author_id;
553 $this->user_validation_id = $obj->user_validation_id;
554 $this->user_approve_id = $obj->user_approve_id;
555 $this->user_approve_id2 = $obj->user_approve_id2;
556 $this->total_ht = $obj->total_ht;
557 $this->total_tva = $obj->total_tva;
558 $this->total_localtax1 = $obj->localtax1;
559 $this->total_localtax2 = $obj->localtax2;
560 $this->total_ttc = $obj->total_ttc;
561 $this->date_creation = $this->db->jdate($obj->date_creation);
562 $this->date_valid = $this->db->jdate($obj->date_valid);
563 $this->date_approve = $this->db->jdate($obj->date_approve);
564 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
565 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
566 if (isset($obj->date_commande)) {
567 $this->date = $this->date_commande;
568 } else {
569 $this->date = $this->date_creation;
570 }
571 $this->delivery_date = $this->db->jdate($obj->delivery_date);
572 $this->remise_percent = $obj->remise_percent;
573 $this->methode_commande_id = $obj->fk_input_method;
574 $this->methode_commande = $obj->methode_commande;
575
576 $this->source = $obj->source;
577 $this->fk_project = $obj->fk_project;
578 $this->cond_reglement_id = $obj->fk_cond_reglement;
579 $this->cond_reglement_code = $obj->cond_reglement_code;
580 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
581 $this->cond_reglement_label = $obj->cond_reglement_label;
582 $this->cond_reglement_doc = $obj->cond_reglement_doc;
583 $this->fk_account = $obj->fk_account;
584 $this->mode_reglement_id = $obj->fk_mode_reglement;
585 $this->mode_reglement_code = $obj->mode_reglement_code;
586 $this->mode_reglement = $obj->mode_reglement_libelle;
587 $this->note = $obj->note_private; // deprecated
588 $this->note_private = $obj->note_private;
589 $this->note_public = $obj->note_public;
590 $this->model_pdf = $obj->model_pdf;
591
592 //Incoterms
593 $this->fk_incoterms = $obj->fk_incoterms;
594 $this->location_incoterms = $obj->location_incoterms;
595 $this->label_incoterms = $obj->label_incoterms;
596
597 // Multicurrency
598 $this->fk_multicurrency = $obj->fk_multicurrency;
599 $this->multicurrency_code = $obj->multicurrency_code;
600 $this->multicurrency_tx = $obj->multicurrency_tx;
601 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
602 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
603 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
604
605 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
606
607 $this->db->free($resql);
608
609 // Retrieve all extrafield
610 // fetch optionals attributes and labels
611 $this->fetch_optionals();
612
613 // Lines
614 $result = $this->fetch_lines();
615
616 if ($result < 0) {
617 return -1;
618 } else {
619 return 1;
620 }
621 } else {
622 $this->error = $this->db->error()." sql=".$sql;
623 return -1;
624 }
625 }
626
627 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
634 public function fetch_lines($only_product = 0)
635 {
636 // phpcs:enable
637
638 $this->lines = array();
639
640 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
641 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
642 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
643 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
644 $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,";
645 $sql .= " l.fk_unit,";
646 $sql .= " l.date_start, l.date_end,";
647 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
648 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
649 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
650 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
651 if ($only_product) {
652 $sql .= ' AND p.fk_product_type = 0';
653 }
654 $sql .= " ORDER BY l.rang, l.rowid";
655 //print $sql;
656
657 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
658
659 $result = $this->db->query($sql);
660 if ($result) {
661 $num = $this->db->num_rows($result);
662 $i = 0;
663
664 while ($i < $num) {
665 $objp = $this->db->fetch_object($result);
666
667 $line = new CommandeFournisseurLigne($this->db);
668
669 $line->id = $objp->rowid;
670 $line->fk_commande = $objp->fk_commande;
671 $line->desc = $objp->description;
672 $line->description = $objp->description;
673 $line->qty = $objp->qty;
674 $line->tva_tx = $objp->tva_tx;
675 $line->localtax1_tx = $objp->localtax1_tx;
676 $line->localtax2_tx = $objp->localtax2_tx;
677 $line->localtax1_type = $objp->localtax1_type;
678 $line->localtax2_type = $objp->localtax2_type;
679 $line->subprice = $objp->subprice;
680 $line->pu_ht = $objp->subprice;
681 $line->remise_percent = $objp->remise_percent;
682
683 $line->vat_src_code = $objp->vat_src_code;
684 $line->total_ht = $objp->total_ht;
685 $line->total_tva = $objp->total_tva;
686 $line->total_localtax1 = $objp->total_localtax1;
687 $line->total_localtax2 = $objp->total_localtax2;
688 $line->total_ttc = $objp->total_ttc;
689 $line->product_type = $objp->product_type;
690
691 $line->fk_product = $objp->fk_product;
692
693 $line->libelle = $objp->product_label; // deprecated
694 $line->product_label = $objp->product_label;
695 $line->product_desc = $objp->product_desc;
696 $line->product_tobatch = $objp->product_tobatch;
697 $line->product_barcode = $objp->product_barcode;
698
699 $line->ref = $objp->product_ref; // Ref of product
700 $line->product_ref = $objp->product_ref; // Ref of product
701 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
702 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
703
704 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
705 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
706 // Move this into another method and call it when required.
707
708 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
709 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
710 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
711 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
712 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
713 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
714 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
715 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
716 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
717 $sqlsearchpackage .= " LIMIT 1";
718
719 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
720 if ($resqlsearchpackage) {
721 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
722 if ($objsearchpackage) {
723 $line->fk_fournprice = $objsearchpackage->rowid;
724 $line->packaging = $objsearchpackage->packaging;
725 }
726 } else {
727 $this->error = $this->db->lasterror();
728 return -1;
729 }
730 }
731
732 $line->date_start = $this->db->jdate($objp->date_start);
733 $line->date_end = $this->db->jdate($objp->date_end);
734 $line->fk_unit = $objp->fk_unit;
735
736 // Multicurrency
737 $line->fk_multicurrency = $objp->fk_multicurrency;
738 $line->multicurrency_code = $objp->multicurrency_code;
739 $line->multicurrency_subprice = $objp->multicurrency_subprice;
740 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
741 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
742 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
743
744 $line->info_bits = $objp->info_bits;
745 $line->special_code = $objp->special_code;
746 $line->fk_parent_line = $objp->fk_parent_line;
747
748 $line->rang = $objp->rang;
749
750 // Retrieve all extrafield
751 // fetch optionals attributes and labels
752 $line->fetch_optionals();
753
754 $this->lines[$i] = $line;
755
756 $i++;
757 }
758 $this->db->free($result);
759
760 return $num;
761 } else {
762 $this->error = $this->db->error()." sql=".$sql;
763 return -1;
764 }
765 }
766
775 public function valid($user, $idwarehouse = 0, $notrigger = 0)
776 {
777 global $conf;
778 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
779
780 $error = 0;
781
782 dol_syslog(get_class($this)."::valid");
783 $result = 0;
784 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
785 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
786 $this->db->begin();
787
788 // Definition of supplier order numbering model name
789 $soc = new Societe($this->db);
790 $soc->fetch($this->fourn_id);
791
792 // Check if object has a temporary ref
793 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
794 $num = $this->getNextNumRef($soc);
795 } else {
796 $num = $this->ref;
797 }
798 $this->newref = dol_sanitizeFileName($num);
799
800 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
801 $sql .= " SET ref='".$this->db->escape($num)."',";
802 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
803 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
804 $sql .= " fk_user_valid = ".((int) $user->id);
805 $sql .= " WHERE rowid = ".((int) $this->id);
806 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
807
808 $resql = $this->db->query($sql);
809 if (!$resql) {
810 dol_print_error($this->db);
811 $error++;
812 }
813
814 if (!$error && !$notrigger) {
815 // Call trigger
816 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
817 if ($result < 0) {
818 $error++;
819 }
820 // End call triggers
821 }
822
823 if (!$error) {
824 $this->oldref = $this->ref;
825
826 // Rename directory if dir was a temporary ref
827 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
828 // Now we rename also files into index
829 $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)."'";
830 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
831 $resql = $this->db->query($sql);
832 if (!$resql) {
833 $error++;
834 $this->error = $this->db->lasterror();
835 }
836 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
837 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
838 $resql = $this->db->query($sql);
839 if (!$resql) {
840 $error++;
841 $this->error = $this->db->lasterror();
842 }
843
844 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
845 $oldref = dol_sanitizeFileName($this->ref);
846 $newref = dol_sanitizeFileName($num);
847 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
848 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
849 if (!$error && file_exists($dirsource)) {
850 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
851
852 if (@rename($dirsource, $dirdest)) {
853 dol_syslog("Rename ok");
854 // Rename docs starting with $oldref with $newref
855 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
856 foreach ($listoffiles as $fileentry) {
857 $dirsource = $fileentry['name'];
858 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
859 $dirsource = $fileentry['path'].'/'.$dirsource;
860 $dirdest = $fileentry['path'].'/'.$dirdest;
861 @rename($dirsource, $dirdest);
862 }
863 }
864 }
865 }
866 }
867
868 if (!$error) {
869 $result = 1;
871 $this->statut = self::STATUS_VALIDATED; // deprecated
872 $this->ref = $num;
873 }
874
875 if (!$error) {
876 $this->db->commit();
877 return 1;
878 } else {
879 $this->db->rollback();
880 return -1;
881 }
882 } else {
883 $this->error = 'NotAuthorized';
884 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
885 return -1;
886 }
887 }
888
895 public function getLibStatut($mode = 0)
896 {
897 return $this->LibStatut($this->status, $mode, $this->billed);
898 }
899
900 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
909 public function LibStatut($status, $mode = 0, $billed = 0)
910 {
911 // phpcs:enable
912 global $langs, $hookmanager;
913
914 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
915 $langs->load('orders');
916
917 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
918 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
919 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
920 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
921 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
922 } else {
923 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
924 }
925 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
926 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
927 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
928 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
929 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
930
931 // List of language codes for status
932 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
933 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
934 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
935 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
936 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
937 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
938 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
939 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
940 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
941 }
942
943 $statustrans = array(
944 0 => 'status0',
945 1 => 'status1b',
946 2 => 'status1',
947 3 => 'status4',
948 4 => 'status4b',
949 5 => 'status6',
950 6 => 'status9',
951 7 => 'status9',
952 9 => 'status9',
953 );
954
955 $statusClass = 'status0';
956 if (!empty($statustrans[$status])) {
957 $statusClass = $statustrans[$status];
958 }
959
960 $billedtext = '';
961 if ($billed) {
962 $billedtext = ' - '.$langs->trans("Billed");
963 }
964 if ($status == 5 && $billed) {
965 $statusClass = 'status6';
966 }
967
968 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
969 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
970
971 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
972 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
973 if ($reshook > 0) {
974 return $hookmanager->resPrint;
975 }
976
977 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
978 }
979
986 public function getTooltipContentArray($params)
987 {
988 global $conf, $langs, $user;
989
990 $langs->loadLangs(['bills', 'orders']);
991
992 $datas = [];
993 $nofetch = !empty($params['nofetch']);
994
995 if ($user->hasRight("fournisseur", "commande", "read")) {
996 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
997 if ($this->status) {
998 $datas['picto'] .= ' '.$this->getLibStatut(5);
999 }
1000 if (!empty($this->ref)) {
1001 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1002 }
1003 if (!empty($this->ref_supplier)) {
1004 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1005 }
1006 if (!$nofetch) {
1007 $langs->load('companies');
1008 if (empty($this->thirdparty)) {
1009 $this->fetch_thirdparty();
1010 }
1011 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
1012 }
1013 if (!empty($this->total_ht)) {
1014 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1015 }
1016 if (!empty($this->total_tva)) {
1017 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1018 }
1019 if (!empty($this->total_ttc)) {
1020 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1021 }
1022 if (!empty($this->date)) {
1023 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
1024 }
1025 if (!empty($this->delivery_date)) {
1026 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
1027 }
1028 }
1029 return $datas;
1030 }
1031
1042 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1043 {
1044 global $langs, $user, $hookmanager;
1045
1046 $result = '';
1047 $params = [
1048 'id' => $this->id,
1049 'objecttype' => $this->element,
1050 'option' => $option,
1051 'nofetch' => 1
1052 ];
1053 $classfortooltip = 'classfortooltip';
1054 $dataparams = '';
1055 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1056 $classfortooltip = 'classforajaxtooltip';
1057 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1058 $label = '';
1059 } else {
1060 $label = implode($this->getTooltipContentArray($params));
1061 }
1062
1063 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1064
1065 if ($option !== 'nolink') {
1066 // Add param to save lastsearch_values or not
1067 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1068 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1069 $add_save_lastsearch_values = 1;
1070 }
1071 if ($add_save_lastsearch_values) {
1072 $url .= '&save_lastsearch_values=1';
1073 }
1074 }
1075
1076 $linkclose = '';
1077 if (empty($notooltip)) {
1078 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1079 $label = $langs->trans("ShowOrder");
1080 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1081 }
1082 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1083 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1084 }
1085
1086 $linkstart = '<a href="'.$url.'"';
1087 $linkstart .= $linkclose.'>';
1088 $linkend = '</a>';
1089
1090 $result .= $linkstart;
1091 if ($withpicto) {
1092 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1093 }
1094 if ($withpicto != 2) {
1095 $result .= $this->ref;
1096 }
1097 $result .= $linkend;
1098
1099 if ($addlinktonotes) {
1100 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1101 if ($txttoshow) {
1102 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1103 $result .= ' <span class="note inline-block">';
1104 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1105 $result .= img_picto('', 'note');
1106 $result .= '</a>';
1107 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1108 //$result.='</a>';
1109 $result .= '</span>';
1110 }
1111 }
1112
1113 global $action;
1114 $hookmanager->initHooks(array($this->element . 'dao'));
1115 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1116 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1117 if ($reshook > 0) {
1118 $result = $hookmanager->resPrint;
1119 } else {
1120 $result .= $hookmanager->resPrint;
1121 }
1122 return $result;
1123 }
1124
1125
1133 public function getNextNumRef($soc)
1134 {
1135 global $langs, $conf;
1136 $langs->load("orders");
1137
1138 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1139 $mybool = false;
1140
1141 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1142 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1143
1144 // Include file with class
1145 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1146
1147 foreach ($dirmodels as $reldir) {
1148 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1149
1150 // Load file with numbering class (if found)
1151 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1152 }
1153
1154 if (!$mybool) {
1155 dol_print_error(null, "Failed to include file ".$file);
1156 return '';
1157 }
1158
1159 $obj = new $classname();
1160 '@phan-var-force ModeleNumRefSuppliersOrders $obj';
1162 $numref = $obj->getNextValue($soc, $this);
1163
1164 if ($numref != "") {
1165 return $numref;
1166 } else {
1167 $this->error = $obj->error;
1168 return -1;
1169 }
1170 } else {
1171 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1172 return -2;
1173 }
1174 }
1175
1182 public function classifyBilled(User $user)
1183 {
1184 $error = 0;
1185
1186 if ($this->billed) {
1187 return 0;
1188 }
1189
1190 $this->db->begin();
1191
1192 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1193 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1194
1195 if ($this->db->query($sql)) {
1196 if (!$error) {
1197 // Call trigger
1198 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1199 if ($result < 0) {
1200 $error++;
1201 }
1202 // End call triggers
1203 }
1204
1205 if (!$error) {
1206 $this->billed = 1;
1207
1208 $this->db->commit();
1209 return 1;
1210 } else {
1211 $this->db->rollback();
1212 return -1;
1213 }
1214 } else {
1215 dol_print_error($this->db);
1216
1217 $this->db->rollback();
1218 return -1;
1219 }
1220 }
1221
1222
1229 public function classifyUnBilled(User $user)
1230 {
1231 if (empty($this->billed)) {
1232 return 0;
1233 }
1234
1235 $this->db->begin();
1236
1237 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 0';
1238 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1239 ;
1240
1241 if (!$this->db->query($sql)) {
1242 dol_print_error($this->db);
1243 $this->db->rollback();
1244 return -1;
1245 }
1246
1247 // Call trigger
1248 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_UNBILLED', $user);
1249 if ($result < 0) {
1250 $this->db->rollback();
1251 return -1;
1252 }
1253 // End call triggers
1254
1255 $this->billed = 1;
1256 $this->db->commit();
1257 return 1;
1258 }
1259
1260
1269 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1270 {
1271 global $langs, $conf;
1272 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1273
1274 $error = 0;
1275
1276 dol_syslog(get_class($this)."::approve");
1277
1278 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1279 $now = dol_now();
1280
1281 $this->db->begin();
1282
1283 // Definition of order numbering model name
1284 $soc = new Societe($this->db);
1285 $soc->fetch($this->fourn_id);
1286
1287 // Check if object has a temporary ref
1288 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1289 $num = $this->getNextNumRef($soc);
1290 } else {
1291 $num = $this->ref;
1292 }
1293 $this->newref = dol_sanitizeFileName($num);
1294
1295 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1296 $movetoapprovestatus = true;
1297 $comment = '';
1298
1299 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1300 $sql .= " SET ref='".$this->db->escape($num)."',";
1301 if (empty($secondlevel)) { // standard or first level approval
1302 $sql .= " date_approve='".$this->db->idate($now)."',";
1303 $sql .= " fk_user_approve = ".((int) $user->id);
1304 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= getDolGlobalFloat('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED')) {
1305 if (empty($this->user_approve_id2)) {
1306 $movetoapprovestatus = false; // second level approval not done
1307 $comment = ' (first level)';
1308 }
1309 }
1310 } else { // request a second level approval
1311 $sql .= " date_approve2='".$this->db->idate($now)."',";
1312 $sql .= " fk_user_approve2 = ".((int) $user->id);
1313 if (empty($this->user_approve_id)) {
1314 $movetoapprovestatus = false; // first level approval not done
1315 }
1316 $comment = ' (second level)';
1317 }
1318 // If double approval is required and first approval, we keep status to 1 = validated
1319 if ($movetoapprovestatus) {
1320 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1321 } else {
1322 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1323 }
1324 $sql .= " WHERE rowid = ".((int) $this->id);
1325 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1326
1327 if ($this->db->query($sql)) {
1328 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1329 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1330 if ($result < 0 && $result != -2) { // -2 means already exists
1331 $error++;
1332 }
1333 }
1334
1335 // If stock is incremented on validate order, we must increment it
1336 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1337 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1338 $langs->load("agenda");
1339
1340 $cpt = count($this->lines);
1341 for ($i = 0; $i < $cpt; $i++) {
1342 // Product with reference
1343 if ($this->lines[$i]->fk_product > 0) {
1344 $this->line = $this->lines[$i];
1345 $mouvP = new MouvementStock($this->db);
1346 $mouvP->origin = &$this;
1347 $mouvP->setOrigin($this->element, $this->id);
1348 // We decrement stock of product (and sub-products)
1349 $up_ht_disc = $this->lines[$i]->subprice;
1350 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1351 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1352 }
1353 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1354 if ($result < 0) {
1355 $error++;
1356 }
1357 unset($this->line);
1358 }
1359 }
1360 }
1361
1362 if (!$error) {
1363 // Call trigger
1364 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1365 if ($result < 0) {
1366 $error++;
1367 }
1368 // End call triggers
1369 }
1370
1371 if (!$error) {
1372 $this->ref = $this->newref;
1373
1374 if ($movetoapprovestatus) {
1375 $this->statut = self::STATUS_ACCEPTED;
1376 } else {
1377 $this->statut = self::STATUS_VALIDATED;
1378 }
1379 if (empty($secondlevel)) { // standard or first level approval
1380 $this->date_approve = $now;
1381 $this->user_approve_id = $user->id;
1382 } else { // request a second level approval
1383 $this->date_approve2 = $now;
1384 $this->user_approve_id2 = $user->id;
1385 }
1386
1387 $this->db->commit();
1388 return 1;
1389 } else {
1390 $this->db->rollback();
1391 return -1;
1392 }
1393 } else {
1394 $this->db->rollback();
1395 $this->error = $this->db->lasterror();
1396 return -1;
1397 }
1398 } else {
1399 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1400 }
1401 return -1;
1402 }
1403
1410 public function refuse($user)
1411 {
1412 global $conf, $langs;
1413
1414 $error = 0;
1415
1416 dol_syslog(get_class($this)."::refuse");
1417 $result = 0;
1418 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1419 $this->db->begin();
1420
1421 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1422 $sql .= " WHERE rowid = ".((int) $this->id);
1423
1424 if ($this->db->query($sql)) {
1425 $result = 0;
1426
1427 if ($error == 0) {
1428 // Call trigger
1429 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1430 if ($result < 0) {
1431 $error++;
1432 $this->db->rollback();
1433 } else {
1434 $this->db->commit();
1435 }
1436 // End call triggers
1437 }
1438 } else {
1439 $this->db->rollback();
1440 $this->error = $this->db->lasterror();
1441 dol_syslog(get_class($this)."::refuse Error -1");
1442 $result = -1;
1443 }
1444 } else {
1445 dol_syslog(get_class($this)."::refuse Not Authorized");
1446 }
1447 return $result;
1448 }
1449
1450 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1459 public function Cancel($user, $idwarehouse = -1)
1460 {
1461 // phpcs:enable
1462 global $langs, $conf;
1463
1464 $error = 0;
1465
1466 //dol_syslog("CommandeFournisseur::Cancel");
1467 $result = 0;
1468 if ($user->hasRight("fournisseur", "commande", "commander")) {
1469 $statut = self::STATUS_CANCELED;
1470
1471 $this->db->begin();
1472
1473 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1474 $sql .= " WHERE rowid = ".((int) $this->id);
1475 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1476 if ($this->db->query($sql)) {
1477 $result = 0;
1478
1479 // Call trigger
1480 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1481 if ($result < 0) {
1482 $error++;
1483 }
1484 // End call triggers
1485
1486 if ($error == 0) {
1487 $this->db->commit();
1488 return 1;
1489 } else {
1490 $this->db->rollback();
1491 return -1;
1492 }
1493 } else {
1494 $this->db->rollback();
1495 $this->error = $this->db->lasterror();
1496 dol_syslog(get_class($this)."::cancel ".$this->error);
1497 return -1;
1498 }
1499 } else {
1500 dol_syslog(get_class($this)."::cancel Not Authorized");
1501 return -1;
1502 }
1503 }
1504
1514 public function commande($user, $date, $methode, $comment = '')
1515 {
1516 global $langs;
1517 dol_syslog(get_class($this)."::commande");
1518 $error = 0;
1519 if ($user->hasRight("fournisseur", "commande", "commander")) {
1520 $this->db->begin();
1521
1522 $newnoteprivate = $this->note_private;
1523 if ($comment) {
1524 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1525 }
1526
1527 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1528 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1529 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1530 $sql .= " WHERE rowid=".((int) $this->id);
1531
1532 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1533 if ($this->db->query($sql)) {
1534 $this->statut = self::STATUS_ORDERSENT;
1535 $this->methode_commande_id = $methode;
1536 $this->date_commande = $date;
1537 $this->context = array('comments' => $comment);
1538
1539 // Call trigger
1540 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1541 if ($result < 0) {
1542 $error++;
1543 }
1544 // End call triggers
1545 } else {
1546 $error++;
1547 $this->error = $this->db->lasterror();
1548 $this->errors[] = $this->db->lasterror();
1549 }
1550
1551 if (!$error) {
1552 $this->db->commit();
1553 } else {
1554 $this->db->rollback();
1555 }
1556 } else {
1557 $error++;
1558 $this->error = $langs->trans('NotAuthorized');
1559 $this->errors[] = $langs->trans('NotAuthorized');
1560 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1561 }
1562
1563 return ($error ? -1 : 1);
1564 }
1565
1573 public function create($user, $notrigger = 0)
1574 {
1575 global $langs, $conf, $hookmanager;
1576
1577 $this->db->begin();
1578
1579 $error = 0;
1580 $now = dol_now();
1581
1582 // set tmp vars
1583 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1584 if (empty($date)) {
1585 $date = $now;
1586 }
1587 $delivery_date = $this->delivery_date;
1588
1589 // Clean parameters
1590 if (empty($this->source)) {
1591 $this->source = 0;
1592 }
1593
1594 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1595 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1596 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1597 } else {
1598 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1599 }
1600 if (empty($this->fk_multicurrency)) {
1601 $this->multicurrency_code = $conf->currency;
1602 $this->fk_multicurrency = 0;
1603 $this->multicurrency_tx = 1;
1604 }
1605 $this->entity = setEntity($this);
1606
1607 // We set order into draft status
1608 $this->statut = self::STATUS_DRAFT; // deprecated
1609 $this->status = self::STATUS_DRAFT;
1610
1611 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1612 $sql .= "ref";
1613 $sql .= ", ref_supplier";
1614 $sql .= ", note_private";
1615 $sql .= ", note_public";
1616 $sql .= ", entity";
1617 $sql .= ", fk_soc";
1618 $sql .= ", fk_projet";
1619 $sql .= ", date_creation";
1620 $sql .= ", date_livraison";
1621 $sql .= ", fk_user_author";
1622 $sql .= ", fk_statut";
1623 $sql .= ", source";
1624 $sql .= ", model_pdf";
1625 $sql .= ", fk_mode_reglement";
1626 $sql .= ", fk_cond_reglement";
1627 $sql .= ", fk_account";
1628 $sql .= ", fk_incoterms, location_incoterms";
1629 $sql .= ", fk_multicurrency";
1630 $sql .= ", multicurrency_code";
1631 $sql .= ", multicurrency_tx";
1632 $sql .= ") ";
1633 $sql .= " VALUES (";
1634 $sql .= "'(PROV)'";
1635 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1636 $sql .= ", '".$this->db->escape($this->note_private)."'";
1637 $sql .= ", '".$this->db->escape($this->note_public)."'";
1638 $sql .= ", ".((int) $this->entity);
1639 $sql .= ", ".((int) $this->socid);
1640 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1641 $sql .= ", '".$this->db->idate($date)."'";
1642 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1643 $sql .= ", ".((int) $user->id);
1644 $sql .= ", ".self::STATUS_DRAFT;
1645 $sql .= ", ".((int) $this->source);
1646 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1647 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1648 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1649 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1650 $sql .= ", ".(int) $this->fk_incoterms;
1651 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1652 $sql .= ", ".(int) $this->fk_multicurrency;
1653 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1654 $sql .= ", ".(float) $this->multicurrency_tx;
1655 $sql .= ")";
1656
1657 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1658 if ($this->db->query($sql)) {
1659 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1660
1661 if ($this->id) {
1662 $num = count($this->lines);
1663
1664 // insert products details into database
1665 for ($i = 0; $i < $num; $i++) {
1666 $line = $this->lines[$i];
1667 if (!is_object($line)) {
1668 $line = (object) $line;
1669 }
1670
1671 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1672
1673 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1674 $result = $this->addline(
1675 $line->desc,
1676 $line->subprice,
1677 $line->qty,
1678 $line->tva_tx,
1679 $line->localtax1_tx,
1680 $line->localtax2_tx,
1681 $line->fk_product,
1682 0,
1683 $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
1684 $line->remise_percent,
1685 'HT',
1686 0,
1687 $line->product_type,
1688 $line->info_bits,
1689 0,
1690 $line->date_start,
1691 $line->date_end,
1692 $line->array_options,
1693 $line->fk_unit,
1694 $line->multicurrency_subprice, // pu_ht_devise
1695 $line->origin, // origin
1696 $line->origin_id, // origin_id
1697 $line->rang, // rang
1698 $line->special_code
1699 );
1700 if ($result < 0) {
1701 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1702 $this->db->rollback();
1703 return -1;
1704 }
1705 }
1706
1707 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1708 $sql .= " SET ref='(PROV".$this->id.")'";
1709 $sql .= " WHERE rowid=".((int) $this->id);
1710
1711 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1712 if ($this->db->query($sql)) {
1713 // Add link with price request and supplier order
1714 if ($this->id) {
1715 $this->ref = "(PROV".$this->id.")";
1716
1717 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1718 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1719 }
1720
1721 // Add object linked
1722 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1723 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1724 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, ...))
1725 foreach ($tmp_origin_id as $origin_id) {
1726 $ret = $this->add_object_linked($origin, $origin_id);
1727 if (!$ret) {
1728 dol_print_error($this->db);
1729 $error++;
1730 }
1731 }
1732 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1733 $origin_id = $tmp_origin_id;
1734 $ret = $this->add_object_linked($origin, $origin_id);
1735 if (!$ret) {
1736 dol_print_error($this->db);
1737 $error++;
1738 }
1739 }
1740 }
1741 }
1742 }
1743
1744 if (!$error) {
1745 $result = $this->insertExtraFields();
1746 if ($result < 0) {
1747 $error++;
1748 }
1749 }
1750
1751 if (!$error && !$notrigger) {
1752 // Call trigger
1753 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1754 if ($result < 0) {
1755 $this->db->rollback();
1756
1757 return -1;
1758 }
1759 // End call triggers
1760 }
1761
1762 $this->db->commit();
1763 return $this->id;
1764 } else {
1765 $this->error = $this->db->lasterror();
1766 $this->db->rollback();
1767
1768 return -2;
1769 }
1770 } else {
1771 $this->error = 'Failed to get ID of inserted line';
1772
1773 return -1;
1774 }
1775 } else {
1776 $this->error = $this->db->lasterror();
1777 $this->db->rollback();
1778
1779 return -1;
1780 }
1781 }
1782
1790 public function update(User $user, $notrigger = 0)
1791 {
1792 global $conf;
1793
1794 $error = 0;
1795
1796 // Clean parameters
1797 if (isset($this->ref)) {
1798 $this->ref = trim($this->ref);
1799 }
1800 if (isset($this->ref_supplier)) {
1801 $this->ref_supplier = trim($this->ref_supplier);
1802 }
1803 if (isset($this->note_private)) {
1804 $this->note_private = trim($this->note_private);
1805 }
1806 if (isset($this->note_public)) {
1807 $this->note_public = trim($this->note_public);
1808 }
1809 if (isset($this->model_pdf)) {
1810 $this->model_pdf = trim($this->model_pdf);
1811 }
1812 if (isset($this->import_key)) {
1813 $this->import_key = trim($this->import_key);
1814 }
1815
1816 // Update request
1817 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1818
1819 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1820 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1821 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1822 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1823 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1824 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1825 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1826 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1827 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1828 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1829 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1830 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1831 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1832 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1833 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1834 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1835 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1836 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1837 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1838 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1839 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1840 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1841 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1842 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1843 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1844
1845 $sql .= " WHERE rowid=".((int) $this->id);
1846
1847 $this->db->begin();
1848
1849 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1850 $resql = $this->db->query($sql);
1851 if (!$resql) {
1852 $error++;
1853 $this->errors[] = "Error ".$this->db->lasterror();
1854 }
1855
1856 if (!$error) {
1857 $result = $this->insertExtraFields();
1858 if ($result < 0) {
1859 $error++;
1860 }
1861 }
1862
1863 if (!$error && !$notrigger) {
1864 // Call trigger
1865 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1866 if ($result < 0) {
1867 $error++;
1868 }
1869 // End call triggers
1870 }
1871
1872 // Commit or rollback
1873 if ($error) {
1874 foreach ($this->errors as $errmsg) {
1875 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1876 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1877 }
1878 $this->db->rollback();
1879 return -1 * $error;
1880 } else {
1881 $this->db->commit();
1882 return 1;
1883 }
1884 }
1885
1894 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1895 {
1896 global $conf, $user, $hookmanager;
1897
1898 $error = 0;
1899
1900 $this->db->begin();
1901
1902 // get extrafields so they will be clone
1903 foreach ($this->lines as $line) {
1904 $line->fetch_optionals();
1905 }
1906
1907 // Load source object
1908 $objFrom = clone $this;
1909
1910 // Change socid if needed
1911 if (!empty($socid) && $socid != $this->socid) {
1912 $objsoc = new Societe($this->db);
1913
1914 if ($objsoc->fetch($socid) > 0) {
1915 $this->socid = $objsoc->id;
1916 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1917 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1918 $this->fk_project = 0;
1919 $this->fk_delivery_address = 0;
1920 }
1921
1922 // TODO Change product price if multi-prices
1923 }
1924
1925 $this->id = 0;
1926 $this->statut = self::STATUS_DRAFT;
1927
1928 // Clear fields
1929 $this->user_author_id = $user->id;
1930 $this->user_validation_id = 0;
1931
1932 $this->date = dol_now();
1933 $this->date_creation = 0;
1934 $this->date_validation = 0;
1935 $this->date_commande = 0;
1936 $this->ref_supplier = '';
1937 $this->user_approve_id = 0;
1938 $this->user_approve_id2 = 0;
1939 $this->date_approve = 0;
1940 $this->date_approve2 = 0;
1941
1942 // Create clone
1943 $this->context['createfromclone'] = 'createfromclone';
1944 $result = $this->create($user, $notrigger);
1945 if ($result < 0) {
1946 $error++;
1947 }
1948
1949 if (!$error) {
1950 // Hook of thirdparty module
1951 if (is_object($hookmanager)) {
1952 $parameters = array('objFrom' => $objFrom);
1953 $action = '';
1954 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1955 if ($reshook < 0) {
1956 $this->setErrorsFromObject($hookmanager);
1957 $error++;
1958 }
1959 }
1960 }
1961
1962 unset($this->context['createfromclone']);
1963
1964 // End
1965 if (!$error) {
1966 $this->db->commit();
1967 return $this->id;
1968 } else {
1969 $this->db->rollback();
1970 return -1;
1971 }
1972 }
1973
2003 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)
2004 {
2005 global $langs, $mysoc, $conf;
2006
2007 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");
2008 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2009
2010 if ($this->statut == self::STATUS_DRAFT) {
2011 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2012
2013 // Clean parameters
2014 if (empty($qty)) {
2015 $qty = 0;
2016 }
2017 if (!$info_bits) {
2018 $info_bits = 0;
2019 }
2020 if (empty($txtva)) {
2021 $txtva = 0;
2022 }
2023 if (empty($rang)) {
2024 $rang = 0;
2025 }
2026 if (empty($txlocaltax1)) {
2027 $txlocaltax1 = 0;
2028 }
2029 if (empty($txlocaltax2)) {
2030 $txlocaltax2 = 0;
2031 }
2032 if (empty($remise_percent)) {
2033 $remise_percent = 0;
2034 }
2035
2036 $remise_percent = price2num($remise_percent);
2037 $qty = price2num($qty);
2038 $pu_ht = price2num($pu_ht);
2039 $pu_ht_devise = price2num($pu_ht_devise);
2040 $pu_ttc = price2num($pu_ttc);
2041 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2042 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2043 }
2044 $txlocaltax1 = price2num($txlocaltax1);
2045 $txlocaltax2 = price2num($txlocaltax2);
2046 if ($price_base_type == 'HT') {
2047 $pu = $pu_ht;
2048 } else {
2049 $pu = $pu_ttc;
2050 }
2051 $desc = trim($desc);
2052
2053 // Check parameters
2054 if ($qty < 0 && !$fk_product) {
2055 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
2056 return -1;
2057 }
2058 if ($type < 0) {
2059 return -1;
2060 }
2061 if ($date_start && $date_end && $date_start > $date_end) {
2062 $langs->load("errors");
2063 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2064 return -1;
2065 }
2066
2067
2068 $this->db->begin();
2069
2070 $product_type = $type;
2071 $label = ''; // deprecated
2072
2073 if ($fk_product > 0) {
2074 if (getDolGlobalInt('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2075 // Check quantity is enough
2076 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);
2077 $prod = new ProductFournisseur($this->db);
2078 if ($prod->fetch($fk_product) > 0) {
2079 $product_type = $prod->type;
2080 $label = $prod->label;
2081
2082 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2083 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2084 $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
2085
2086 // If supplier order created from sales order, we take best supplier price
2087 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2088 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2089 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2090 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2091 // is remise percent not keyed but present for the product we add it
2092 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2093 $remise_percent = $prod->remise_percent;
2094 }
2095 }
2096 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2097 $langs->load("errors");
2098 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2099 $this->db->rollback();
2100 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2101 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2102 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2103 return -1;
2104 }
2105 if ($result == -1) {
2106 $langs->load("errors");
2107 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2108 $this->db->rollback();
2109 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2110 return -1;
2111 }
2112 if ($result < -1) {
2113 $this->error = $prod->error;
2114 $this->db->rollback();
2115 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2116 return -1;
2117 }
2118 } else {
2119 $this->error = $prod->error;
2120 $this->db->rollback();
2121 return -1;
2122 }
2123 }
2124
2125 // Predefine quantity according to packaging
2126 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2127 $prod = new Product($this->db);
2128 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2129
2130 if ($qty < $prod->packaging) {
2131 $qty = $prod->packaging;
2132 } else {
2133 if (!empty($prod->packaging) && (fmod((float) $qty, $prod->packaging) > 0.000001)) {
2134 $coeff = intval((float) $qty / $prod->packaging) + 1;
2135 $qty = (float) $prod->packaging * $coeff;
2136 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2137 }
2138 }
2139 }
2140 }
2141
2142 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2143 $pu = 0;
2144 }
2145
2146 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2147
2148 // Clean vat code
2149 $reg = array();
2150 $vat_src_code = '';
2151 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2152 $vat_src_code = $reg[1];
2153 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2154 }
2155
2156 // Calcul du total TTC et de la TVA pour la ligne a partir de
2157 // qty, pu, remise_percent et txtva
2158 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2159 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2160
2161 $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);
2162
2163 $total_ht = $tabprice[0];
2164 $total_tva = $tabprice[1];
2165 $total_ttc = $tabprice[2];
2166 $total_localtax1 = $tabprice[9];
2167 $total_localtax2 = $tabprice[10];
2168 $pu = $pu_ht = $tabprice[3];
2169
2170 // MultiCurrency
2171 $multicurrency_total_ht = $tabprice[16];
2172 $multicurrency_total_tva = $tabprice[17];
2173 $multicurrency_total_ttc = $tabprice[18];
2174 $pu_ht_devise = $tabprice[19];
2175
2176 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2177 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2178
2179 if ($rang < 0) {
2180 $rangmax = $this->line_max();
2181 $rang = $rangmax + 1;
2182 }
2183
2184 // Insert line
2185 $this->line = new CommandeFournisseurLigne($this->db);
2186
2187 $this->line->context = $this->context;
2188
2189 $this->line->fk_commande = $this->id;
2190 $this->line->label = $label;
2191 $this->line->ref_fourn = $ref_supplier;
2192 $this->line->ref_supplier = $ref_supplier;
2193 $this->line->desc = $desc;
2194 $this->line->qty = $qty;
2195 $this->line->tva_tx = $txtva;
2196 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2197 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2198 $this->line->localtax1_type = $localtax1_type;
2199 $this->line->localtax2_type = $localtax2_type;
2200 $this->line->fk_product = $fk_product;
2201 $this->line->product_type = $product_type;
2202 $this->line->remise_percent = $remise_percent;
2203 $this->line->subprice = (float) $pu_ht;
2204 $this->line->rang = $rang;
2205 $this->line->info_bits = $info_bits;
2206
2207 $this->line->vat_src_code = $vat_src_code;
2208 $this->line->total_ht = (float) $total_ht;
2209 $this->line->total_tva = (float) $total_tva;
2210 $this->line->total_localtax1 = (float) $total_localtax1;
2211 $this->line->total_localtax2 = (float) $total_localtax2;
2212 $this->line->total_ttc = (float) $total_ttc;
2213 $this->line->product_type = $type;
2214 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2215 $this->line->origin = $origin;
2216 $this->line->origin_id = $origin_id;
2217 $this->line->fk_unit = $fk_unit;
2218
2219 $this->line->date_start = $date_start;
2220 $this->line->date_end = $date_end;
2221
2222 // Multicurrency
2223 $this->line->fk_multicurrency = $this->fk_multicurrency;
2224 $this->line->multicurrency_code = $this->multicurrency_code;
2225 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
2226 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2227 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2228 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2229
2230 $this->line->subprice = (float) $pu_ht;
2231 $this->line->price = $this->line->subprice;
2232
2233 $this->line->remise_percent = $remise_percent;
2234
2235 if (is_array($array_options) && count($array_options) > 0) {
2236 $this->line->array_options = $array_options;
2237 }
2238
2239 $result = $this->line->insert($notrigger);
2240 if ($result > 0) {
2241 // Reorder if child line
2242 if (!empty($this->line->fk_parent_line)) {
2243 $this->line_order(true, 'DESC');
2244 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2245 $linecount = count($this->lines);
2246 for ($ii = $rang; $ii <= $linecount; $ii++) {
2247 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2248 }
2249 }
2250
2251 // Mise a jour information denormalisees au niveau de la commande meme
2252 $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.
2253 if ($result > 0) {
2254 $this->db->commit();
2255 return $this->line->id;
2256 } else {
2257 $this->db->rollback();
2258 return -1;
2259 }
2260 } else {
2261 $this->error = $this->line->error;
2262 $this->errors = $this->line->errors;
2263 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2264 $this->db->rollback();
2265 return -1;
2266 }
2267 }
2268 return -1;
2269 }
2270
2271
2289 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2290 {
2291 global $conf, $langs;
2292
2293 $error = 0;
2294 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2295
2296 // Check parameters (if test are wrong here, there is bug into caller)
2297 if ($entrepot <= 0) {
2298 $this->error = 'ErrorBadValueForParameterWarehouse';
2299 return -1;
2300 }
2301 if ($qty == 0) {
2302 $this->error = 'ErrorBadValueForParameterQty';
2303 return -1;
2304 }
2305
2306 $dispatchstatus = 1;
2307 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2308 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2309 }
2310
2311 $now = dol_now();
2312
2313 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2314
2315 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2316 $this->db->begin();
2317
2318 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2319 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2320 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2321 $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");
2322 $sql .= ")";
2323
2324 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2325 $resql = $this->db->query($sql);
2326 if ($resql) {
2327 if (!$notrigger) {
2328 global $conf, $langs, $user;
2329 // Call trigger
2330 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2331 if ($result < 0) {
2332 $error++;
2333 }
2334 // End call triggers
2335 }
2336 } else {
2337 $this->error = $this->db->lasterror();
2338 $error++;
2339 }
2340
2341 // If module stock is enabled and the stock increase is done on purchase order dispatching
2342 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2343 $mouv = new MouvementStock($this->db);
2344 if ($product > 0) {
2345 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2346 $mouv->origin = &$this;
2347 $mouv->setOrigin($this->element, $this->id);
2348
2349 // Method change if qty < 0
2350 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2351 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2352 } else {
2353 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2354 }
2355
2356 if ($result < 0) {
2357 $this->error = $mouv->error;
2358 $this->errors = $mouv->errors;
2359 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2360 $error++;
2361 }
2362 }
2363 }
2364
2365 if ($error == 0) {
2366 $this->db->commit();
2367 return 1;
2368 } else {
2369 $this->db->rollback();
2370 return -1;
2371 }
2372 } else {
2373 $this->error = 'BadStatusForObject';
2374 return -2;
2375 }
2376 }
2377
2385 public function deleteLine($idline, $notrigger = 0)
2386 {
2387 global $user;
2388
2389 if ($this->statut == 0) {
2390 $line = new CommandeFournisseurLigne($this->db);
2391
2392 if ($line->fetch($idline) <= 0) {
2393 return 0;
2394 }
2395
2396 // check if not yet received
2397 $dispatchedLines = $this->getDispachedLines();
2398 foreach ($dispatchedLines as $dispatchLine) {
2399 if ($dispatchLine['orderlineid'] == $idline) {
2400 $this->error = "LineAlreadyDispatched";
2401 $this->errors[] = $this->error;
2402 return -3;
2403 }
2404 }
2405
2406 if ($line->delete($user, $notrigger) > 0) {
2407 $this->update_price(1);
2408 return 1;
2409 } else {
2410 $this->setErrorsFromObject($line);
2411 return -1;
2412 }
2413 } else {
2414 return -2;
2415 }
2416 }
2417
2425 public function delete(User $user, $notrigger = 0)
2426 {
2427 global $langs, $conf;
2428 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2429
2430 $error = 0;
2431
2432 $this->db->begin();
2433
2434 if (empty($notrigger)) {
2435 // Call trigger
2436 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2437 if ($result < 0) {
2438 $this->errors[] = 'ErrorWhenRunningTrigger';
2439 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2440 $this->db->rollback();
2441 return -1;
2442 }
2443 // End call triggers
2444 }
2445
2446 // Test we can delete
2447 $this->fetchObjectLinked(null, 'order_supplier');
2448 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2449 foreach ($this->linkedObjects['reception'] as $element) {
2450 if ($element->statut >= 0) {
2451 $this->errors[] = $langs->trans('ReceptionExist');
2452 $error++;
2453 break;
2454 }
2455 }
2456 }
2457
2458 $main = $this->db->prefix().'commande_fournisseurdet';
2459 $ef = $main."_extrafields";
2460 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2461 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2462 if (!$this->db->query($sql)) {
2463 $this->error = $this->db->lasterror();
2464 $this->errors[] = $this->db->lasterror();
2465 $error++;
2466 }
2467
2468 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2469 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2470 if (!$this->db->query($sql)) {
2471 $this->error = $this->db->lasterror();
2472 $this->errors[] = $this->db->lasterror();
2473 $error++;
2474 }
2475
2476 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2477 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2478 if ($resql = $this->db->query($sql)) {
2479 if ($this->db->affected_rows($resql) < 1) {
2480 $this->error = $this->db->lasterror();
2481 $this->errors[] = $this->db->lasterror();
2482 $error++;
2483 }
2484 } else {
2485 $this->error = $this->db->lasterror();
2486 $this->errors[] = $this->db->lasterror();
2487 $error++;
2488 }
2489
2490 // Remove extrafields
2491 if (!$error) {
2492 $result = $this->deleteExtraFields();
2493 if ($result < 0) {
2494 $this->error = 'FailToDeleteExtraFields';
2495 $this->errors[] = 'FailToDeleteExtraFields';
2496 $error++;
2497 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2498 }
2499 }
2500
2501 // Delete linked object
2502 $res = $this->deleteObjectLinked();
2503 if ($res < 0) {
2504 $this->error = 'FailToDeleteObjectLinked';
2505 $this->errors[] = 'FailToDeleteObjectLinked';
2506 $error++;
2507 }
2508
2509 if (!$error) {
2510 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2511 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2512 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2513
2514 // We remove directory
2515 $ref = dol_sanitizeFileName($this->ref);
2516 if ($conf->fournisseur->commande->dir_output) {
2517 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2518 $file = $dir."/".$ref.".pdf";
2519 if (file_exists($file)) {
2520 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2521 $this->error = 'ErrorFailToDeleteFile';
2522 $this->errors[] = 'ErrorFailToDeleteFile';
2523 $error++;
2524 }
2525 }
2526 if (file_exists($dir)) {
2527 $res = @dol_delete_dir_recursive($dir);
2528 if (!$res) {
2529 $this->error = 'ErrorFailToDeleteDir';
2530 $this->errors[] = 'ErrorFailToDeleteDir';
2531 $error++;
2532 }
2533 }
2534 }
2535 }
2536
2537 if (!$error) {
2538 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2539 $this->db->commit();
2540 return 1;
2541 } else {
2542 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2543 $this->db->rollback();
2544 return -$error;
2545 }
2546 }
2547
2548
2557 public function getDispachedLines($status = -1)
2558 {
2559 $ret = array();
2560
2561 // List of already dispatched lines
2562 $sql = "SELECT p.ref, p.label,";
2563 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2564 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2565 $sql .= " FROM ".$this->db->prefix()."product as p,";
2566 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2567 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2568 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2569 $sql .= " AND cfd.fk_product = p.rowid";
2570 if ($status >= 0) {
2571 $sql .= " AND cfd.status = ".((int) $status);
2572 }
2573 $sql .= " ORDER BY cfd.rowid ASC";
2574
2575 $resql = $this->db->query($sql);
2576 if ($resql) {
2577 $num = $this->db->num_rows($resql);
2578 $i = 0;
2579
2580 while ($i < $num) {
2581 $objp = $this->db->fetch_object($resql);
2582 if ($objp) {
2583 $ret[] = array(
2584 'id' => $objp->dispatchedlineid,
2585 'productid' => $objp->fk_product,
2586 'warehouseid' => $objp->warehouse_id,
2587 'qty' => $objp->qty,
2588 'orderlineid' => $objp->fk_elementdet
2589 );
2590 }
2591
2592 $i++;
2593 }
2594 } else {
2595 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2596 }
2597
2598 return $ret;
2599 }
2600
2601 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2611 public function Livraison($user, $date, $type, $comment)
2612 {
2613 // phpcs:enable
2614 global $conf, $langs;
2615
2616 $result = 0;
2617 $error = 0;
2618 $dispatchedlinearray = array();
2619
2620 dol_syslog(get_class($this)."::Livraison");
2621
2622 $usercanreceive = 0;
2623 if (!isModEnabled('reception')) {
2624 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2625 } else {
2626 $usercanreceive = $user->hasRight("reception", "creer");
2627 }
2628
2629 if ($usercanreceive) {
2630 // Define the new status
2631 if ($type == 'par') {
2633 } elseif ($type == 'tot') {
2635 } elseif ($type == 'nev') {
2637 } elseif ($type == 'can') {
2639 } else {
2640 $error++;
2641 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2642 return -2;
2643 }
2644
2645 // Some checks to accept the record
2646 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2647 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2648 if (!$error && ($type == 'tot')) {
2649 $dispatchedlinearray = $this->getDispachedLines(0);
2650 if (count($dispatchedlinearray) > 0) {
2651 $result = -1;
2652 $error++;
2653 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2654 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2655 }
2656 }
2657 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)
2658 $dispatcheddenied = $this->getDispachedLines(2);
2659 if (count($dispatchedlinearray) > 0) {
2660 $result = -1;
2661 $error++;
2662 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2663 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2664 }
2665 }
2666 }
2667
2668 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2669
2670 if (empty($error)) {
2671 $this->db->begin();
2672
2673 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2674 $sql .= " SET fk_statut = ".((int) $statut);
2675 $sql .= " WHERE rowid = ".((int) $this->id);
2676 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2677
2678 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2679 $resql = $this->db->query($sql);
2680 if ($resql) {
2681 $result = 1;
2682 $old_statut = $this->statut;
2683 $this->statut = $statut;
2684 $this->context['actionmsg2'] = $comment;
2685
2686 // Call trigger
2687 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2688 if ($result_trigger < 0) {
2689 $error++;
2690 }
2691 // End call triggers
2692
2693 if (empty($error)) {
2694 $this->db->commit();
2695 } else {
2696 $this->statut = $old_statut;
2697 $this->db->rollback();
2698 $this->error = $this->db->lasterror();
2699 $result = -1;
2700 }
2701 } else {
2702 $this->db->rollback();
2703 $this->error = $this->db->lasterror();
2704 $result = -1;
2705 }
2706 }
2707 } else {
2708 $this->error = $langs->trans('NotAuthorized');
2709 $this->errors[] = $langs->trans('NotAuthorized');
2710 dol_syslog(get_class($this)."::Livraison Not Authorized");
2711 $result = -3;
2712 }
2713 return $result;
2714 }
2715
2716 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2726 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2727 {
2728 // phpcs:enable
2729 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2730 }
2731
2740 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2741 {
2742 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2743 $error = 0;
2744
2745 $this->db->begin();
2746
2747 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2748 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2749 $sql .= " WHERE rowid = ".((int) $this->id);
2750
2751 dol_syslog(__METHOD__, LOG_DEBUG);
2752 $resql = $this->db->query($sql);
2753 if (!$resql) {
2754 $this->errors[] = $this->db->error();
2755 $error++;
2756 }
2757
2758 if (!$error) {
2759 $this->oldcopy = clone $this;
2760 $this->delivery_date = $delivery_date;
2761 }
2762
2763 if (!$notrigger && empty($error)) {
2764 // Call trigger
2765 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2766 if ($result < 0) {
2767 $error++;
2768 }
2769 // End call triggers
2770 }
2771
2772 if (!$error) {
2773 $this->db->commit();
2774 return 1;
2775 } else {
2776 foreach ($this->errors as $errmsg) {
2777 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2778 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2779 }
2780 $this->db->rollback();
2781 return -1 * $error;
2782 }
2783 } else {
2784 return -2;
2785 }
2786 }
2787
2788 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2797 public function set_id_projet($user, $id_projet, $notrigger = 0)
2798 {
2799 // phpcs:enable
2800 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2801 $error = 0;
2802
2803 $this->db->begin();
2804
2805 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2806 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2807 $sql .= " WHERE rowid = ".((int) $this->id);
2808
2809 dol_syslog(__METHOD__, LOG_DEBUG);
2810 $resql = $this->db->query($sql);
2811 if (!$resql) {
2812 $this->errors[] = $this->db->error();
2813 $error++;
2814 }
2815
2816 if (!$error) {
2817 $this->oldcopy = clone $this;
2818 $this->fk_projet = $id_projet;
2819 $this->fk_project = $id_projet;
2820 }
2821
2822 if (!$notrigger && empty($error)) {
2823 // Call trigger
2824 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2825 if ($result < 0) {
2826 $error++;
2827 }
2828 // End call triggers
2829 }
2830
2831 if (!$error) {
2832 $this->db->commit();
2833 return 1;
2834 } else {
2835 foreach ($this->errors as $errmsg) {
2836 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2837 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2838 }
2839 $this->db->rollback();
2840 return -1 * $error;
2841 }
2842 } else {
2843 return -2;
2844 }
2845 }
2846
2855 public function updateFromCommandeClient($user, $idc, $comclientid)
2856 {
2857 $comclient = new Commande($this->db);
2858 $comclient->fetch($comclientid);
2859
2860 $this->id = $idc;
2861
2862 $this->lines = array();
2863
2864 $num = count($comclient->lines);
2865 for ($i = 0; $i < $num; $i++) {
2866 $prod = new Product($this->db);
2867 $label = '';
2868 $ref = '';
2869 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2870 $label = $prod->label;
2871 $ref = $prod->ref;
2872 }
2873
2874 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2875 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2876 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2877 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2878 $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);
2879 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2880 if ($this->db->query($sql)) {
2881 $this->update_price(1);
2882 }
2883 }
2884
2885 return 1;
2886 }
2887
2895 public function setStatus($user, $status)
2896 {
2897 global $conf, $langs;
2898 $error = 0;
2899
2900 $this->db->begin();
2901
2902 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2903 $sql .= " SET fk_statut = ".$status;
2904 $sql .= " WHERE rowid = ".((int) $this->id);
2905
2906 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2907 $resql = $this->db->query($sql);
2908 if ($resql) {
2909 // Trigger names for each status
2910 $triggerName = array();
2911 $triggerName[0] = 'DRAFT';
2912 $triggerName[1] = 'VALIDATED';
2913 $triggerName[2] = 'APPROVED';
2914 $triggerName[3] = 'ORDERED'; // Ordered
2915 $triggerName[4] = 'RECEIVED_PARTIALLY';
2916 $triggerName[5] = 'RECEIVED_COMPLETELY';
2917 $triggerName[6] = 'CANCELED';
2918 $triggerName[7] = 'CANCELED';
2919 $triggerName[9] = 'REFUSED';
2920
2921 // Call trigger
2922 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2923 if ($result < 0) {
2924 $error++;
2925 }
2926 // End call triggers
2927 } else {
2928 $error++;
2929 $this->error = $this->db->lasterror();
2930 dol_syslog(get_class($this)."::setStatus ".$this->error);
2931 }
2932
2933 if (!$error) {
2934 $this->statut = $status;
2935 $this->db->commit();
2936 return 1;
2937 } else {
2938 $this->db->rollback();
2939 return -1;
2940 }
2941 }
2942
2966 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 = '')
2967 {
2968 global $mysoc, $conf, $langs;
2969 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2970 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2971
2972 $error = 0;
2973
2974 if ($this->statut == self::STATUS_DRAFT) {
2975 // Clean parameters
2976 if (empty($qty)) {
2977 $qty = 0;
2978 }
2979 if (empty($info_bits)) {
2980 $info_bits = 0;
2981 }
2982 if (empty($txtva)) {
2983 $txtva = 0;
2984 }
2985 if (empty($txlocaltax1)) {
2986 $txlocaltax1 = 0;
2987 }
2988 if (empty($txlocaltax2)) {
2989 $txlocaltax2 = 0;
2990 }
2991 if (empty($remise_percent)) {
2992 $remise_percent = 0;
2993 }
2994
2995 $remise_percent = (float) price2num($remise_percent);
2996 $qty = price2num($qty);
2997 if (!$qty) {
2998 $qty = 1;
2999 }
3000 $pu = price2num($pu);
3001 $pu_ht_devise = price2num($pu_ht_devise);
3002 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
3003 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
3004 }
3005 $txlocaltax1 = (float) price2num($txlocaltax1);
3006 $txlocaltax2 = (float) price2num($txlocaltax2);
3007
3008 // Check parameters
3009 if ($type < 0) {
3010 return -1;
3011 }
3012 if ($date_start && $date_end && $date_start > $date_end) {
3013 $langs->load("errors");
3014 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
3015 return -1;
3016 }
3017
3018 $this->db->begin();
3019
3020 // Calcul du total TTC et de la TVA pour la ligne a partir de
3021 // qty, pu, remise_percent et txtva
3022 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3023 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3024
3025 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
3026
3027 // Clean vat code
3028 $reg = array();
3029 $vat_src_code = '';
3030 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
3031 $vat_src_code = $reg[1];
3032 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
3033 }
3034
3035 $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);
3036 $total_ht = $tabprice[0];
3037 $total_tva = $tabprice[1];
3038 $total_ttc = $tabprice[2];
3039 $total_localtax1 = $tabprice[9];
3040 $total_localtax2 = $tabprice[10];
3041 $pu_ht = $tabprice[3];
3042 $pu_tva = $tabprice[4];
3043 $pu_ttc = $tabprice[5];
3044
3045 // MultiCurrency
3046 $multicurrency_total_ht = $tabprice[16];
3047 $multicurrency_total_tva = $tabprice[17];
3048 $multicurrency_total_ttc = $tabprice[18];
3049 $pu_ht_devise = $tabprice[19];
3050
3051 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3052 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3053
3054 // Fetch current line from the database and then clone the object and set it in $oldline property
3055 $this->line = new CommandeFournisseurLigne($this->db);
3056 $this->line->fetch($rowid);
3057
3058 $oldline = clone $this->line;
3059 $this->line->oldline = $oldline;
3060
3061 $this->line->context = $this->context;
3062
3063 $this->line->fk_commande = $this->id;
3064 //$this->line->label=$label;
3065 $this->line->desc = $desc;
3066
3067 // redefine quantity according to packaging
3068 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3069 if ($qty < $this->line->packaging) {
3070 $qty = $this->line->packaging;
3071 } else {
3072 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
3073 $coeff = intval($qty / $this->line->packaging) + 1;
3074 $qty = $this->line->packaging * $coeff;
3075 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3076 }
3077 }
3078 }
3079
3080 $this->line->qty = $qty;
3081 $this->line->ref_supplier = $ref_supplier;
3082
3083 $this->line->vat_src_code = $vat_src_code;
3084 $this->line->tva_tx = $txtva;
3085 $this->line->localtax1_tx = $txlocaltax1;
3086 $this->line->localtax2_tx = $txlocaltax2;
3087 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3088 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3089 $this->line->remise_percent = $remise_percent;
3090 $this->line->subprice = (float) $pu_ht;
3091 $this->line->info_bits = $info_bits;
3092 $this->line->total_ht = (float) $total_ht;
3093 $this->line->total_tva = (float) $total_tva;
3094 $this->line->total_localtax1 = (float) $total_localtax1;
3095 $this->line->total_localtax2 = (float) $total_localtax2;
3096 $this->line->total_ttc = (float) $total_ttc;
3097 $this->line->product_type = $type;
3098 $this->line->special_code = $oldline->special_code;
3099 $this->line->rang = $oldline->rang;
3100 $this->line->origin = $this->origin;
3101 $this->line->fk_unit = $fk_unit;
3102
3103 $this->line->date_start = $date_start;
3104 $this->line->date_end = $date_end;
3105
3106 // Multicurrency
3107 $this->line->fk_multicurrency = $this->fk_multicurrency;
3108 $this->line->multicurrency_code = $this->multicurrency_code;
3109 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
3110 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
3111 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
3112 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
3113
3114 $this->line->subprice = (float) $pu_ht;
3115 $this->line->price = $this->line->subprice;
3116
3117 $this->line->remise_percent = $remise_percent;
3118
3119 if (is_array($array_options) && count($array_options) > 0) {
3120 // We replace values in this->line->array_options only for entries defined into $array_options
3121 foreach ($array_options as $key => $value) {
3122 $this->line->array_options[$key] = $array_options[$key];
3123 }
3124 }
3125
3126 $result = $this->line->update($notrigger);
3127
3128
3129 // Mise a jour info denormalisees au niveau facture
3130 if ($result >= 0) {
3131 $this->update_price('1', 'auto');
3132 $this->db->commit();
3133 return $result;
3134 } else {
3135 $this->error = $this->db->lasterror();
3136 $this->db->rollback();
3137 return -1;
3138 }
3139 } else {
3140 $this->error = "Order status makes operation forbidden";
3141 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3142 return -2;
3143 }
3144 }
3145
3146
3154 public function initAsSpecimen()
3155 {
3156 global $user, $langs, $conf;
3157
3158 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3159
3160 dol_syslog(get_class($this)."::initAsSpecimen");
3161
3162 $now = dol_now();
3163
3164 // Find first product
3165 $prodid = 0;
3166 $product = new ProductFournisseur($this->db);
3167 $sql = "SELECT rowid";
3168 $sql .= " FROM ".$this->db->prefix()."product";
3169 $sql .= " WHERE entity IN (".getEntity('product').")";
3170 $sql .= $this->db->order("rowid", "ASC");
3171 $sql .= $this->db->plimit(1);
3172 $resql = $this->db->query($sql);
3173 if ($resql && $this->db->num_rows($resql)) {
3174 $obj = $this->db->fetch_object($resql);
3175 $prodid = $obj->rowid;
3176 }
3177
3178 // Initialise parameters
3179 $this->id = 0;
3180 $this->ref = 'SPECIMEN';
3181 $this->specimen = 1;
3182 $this->socid = 1;
3183 $this->date = $now;
3184 $this->date_commande = $now;
3185 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3186 $this->cond_reglement_code = 'RECEP';
3187 $this->mode_reglement_code = 'CHQ';
3188
3189 $this->note_public = 'This is a comment (public)';
3190 $this->note_private = 'This is a comment (private)';
3191
3192 $this->multicurrency_tx = 1;
3193 $this->multicurrency_code = $conf->currency;
3194
3195 $this->statut = 0;
3196 $this->status = 0;
3197
3198 // Lines
3199 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
3200 $xnbp = 0;
3201 while ($xnbp < $nbp) {
3202 $line = new CommandeFournisseurLigne($this->db);
3203 $line->desc = $langs->trans("Description")." ".$xnbp;
3204 $line->qty = 1;
3205 $line->subprice = 100;
3206 $line->tva_tx = 19.6;
3207 $line->localtax1_tx = 0;
3208 $line->localtax2_tx = 0;
3209 if ($xnbp == 2) {
3210 $line->total_ht = 50;
3211 $line->total_ttc = 59.8;
3212 $line->total_tva = 9.8;
3213 $line->remise_percent = 50;
3214 } else {
3215 $line->total_ht = 100;
3216 $line->total_ttc = 119.6;
3217 $line->total_tva = 19.6;
3218 $line->remise_percent = 00;
3219 }
3220 $line->fk_product = $prodid;
3221
3222 $this->lines[$xnbp] = $line;
3223
3224 $this->total_ht += $line->total_ht;
3225 $this->total_tva += $line->total_tva;
3226 $this->total_ttc += $line->total_ttc;
3227
3228 $xnbp++;
3229 }
3230
3231 return 1;
3232 }
3233
3240 public function info($id)
3241 {
3242 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3243 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3244 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3245 $sql .= ' WHERE c.rowid = '.((int) $id);
3246
3247 $result = $this->db->query($sql);
3248 if ($result) {
3249 if ($this->db->num_rows($result)) {
3250 $obj = $this->db->fetch_object($result);
3251
3252 $this->id = $obj->rowid;
3253
3254 $this->user_creation_id = $obj->fk_user_author;
3255 $this->user_validation_id = $obj->fk_user_valid;
3256 $this->user_modification_id = $obj->fk_user_modif;
3257 $this->user_approve_id = $obj->fk_user_approve;
3258 $this->user_approve_id2 = $obj->fk_user_approve2;
3259
3260 $this->date_creation = $this->db->jdate($obj->datec);
3261 $this->date_modification = $this->db->jdate($obj->datem);
3262 $this->date_approve = $this->db->jdate($obj->datea);
3263 $this->date_approve2 = $this->db->jdate($obj->datea2);
3264 $this->date_validation = $this->db->jdate($obj->date_validation);
3265 }
3266 $this->db->free($result);
3267 } else {
3268 dol_print_error($this->db);
3269 }
3270 }
3271
3277 public function loadStateBoard()
3278 {
3279 global $conf, $user;
3280
3281 $this->nb = array();
3282 $clause = "WHERE";
3283
3284 $sql = "SELECT count(co.rowid) as nb";
3285 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3286 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3287 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3288 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3289 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3290 $clause = "AND";
3291 }
3292 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3293
3294 $resql = $this->db->query($sql);
3295 if ($resql) {
3296 while ($obj = $this->db->fetch_object($resql)) {
3297 $this->nb["supplier_orders"] = $obj->nb;
3298 }
3299 $this->db->free($resql);
3300 return 1;
3301 } else {
3302 dol_print_error($this->db);
3303 $this->error = $this->db->error();
3304 return -1;
3305 }
3306 }
3307
3308 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3316 public function load_board($user, $mode = 'opened')
3317 {
3318 // phpcs:enable
3319 global $conf, $langs;
3320
3321 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3322 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3323 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3324 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3325 }
3326 $sql .= " WHERE c.entity = ".$conf->entity;
3327 if ($mode === 'awaiting') {
3328 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3329 } else {
3330 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3331 }
3332 if ($user->socid) {
3333 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3334 }
3335
3336 $resql = $this->db->query($sql);
3337 if ($resql) {
3338 $commandestatic = new CommandeFournisseur($this->db);
3339
3340 $response = new WorkboardResponse();
3341 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3342 $response->label = $langs->trans("SuppliersOrdersToProcess");
3343 $response->labelShort = $langs->trans("Opened");
3344 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3345 $response->url_late = DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers&search_option=late';
3346 $response->img = img_object('', "order");
3347
3348 if ($mode === 'awaiting') {
3349 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3350 $response->labelShort = $langs->trans("AwaitingReception");
3351 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3352 }
3353
3354 while ($obj = $this->db->fetch_object($resql)) {
3355 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3356 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3357 $commandestatic->statut = $obj->fk_statut;
3358
3359 $response->nbtodo++;
3360 $response->total += $obj->total_ht;
3361
3362 if ($commandestatic->hasDelay()) {
3363 $response->nbtodolate++;
3364 }
3365 }
3366
3367 return $response;
3368 } else {
3369 $this->error = $this->db->error();
3370 return -1;
3371 }
3372 }
3373
3380 public function getInputMethod()
3381 {
3382 global $langs;
3383
3384 if ($this->methode_commande_id > 0) {
3385 $sql = "SELECT rowid, code, libelle as label";
3386 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3387 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3388
3389 $resql = $this->db->query($sql);
3390 if ($resql) {
3391 if ($this->db->num_rows($resql)) {
3392 $obj = $this->db->fetch_object($resql);
3393
3394 $string = $langs->trans($obj->code);
3395 if ($string == $obj->code) {
3396 $string = $obj->label != '-' ? $obj->label : '';
3397 }
3398 return $string;
3399 }
3400 } else {
3401 dol_print_error($this->db);
3402 }
3403 }
3404
3405 return '';
3406 }
3407
3419 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3420 {
3421 global $conf, $langs;
3422
3423 if (!dol_strlen($modele)) {
3424 $modele = ''; // No doc template/generation by default
3425
3426 if (!empty($this->model_pdf)) {
3427 $modele = $this->model_pdf;
3428 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3429 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3430 }
3431 }
3432
3433 if (empty($modele)) {
3434 return 0;
3435 } else {
3436 $langs->load("suppliers");
3437 $outputlangs->load("products");
3438
3439 $modelpath = "core/modules/supplier_order/doc/";
3440 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3441 return $result;
3442 }
3443 }
3444
3451 public function getMaxDeliveryTimeDay($langs)
3452 {
3453 if (empty($this->lines)) {
3454 return '';
3455 }
3456
3457 $tmpproductfourn = new ProductFournisseur($this->db);
3458
3459 $nb = 0;
3460 foreach ($this->lines as $line) {
3461 if ($line->fk_product > 0) {
3462 // Load delivery_time_days, return id into product_fournisseur_price
3463 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3464 if ($idp > 0) {
3465 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3466 if ($tmpproductfourn->delivery_time_days > $nb) {
3467 $nb = $tmpproductfourn->delivery_time_days;
3468 }
3469 }
3470 }
3471 }
3472
3473 if ($nb === 0) {
3474 return '';
3475 } else {
3476 return $nb.' '.$langs->trans('days');
3477 }
3478 }
3479
3485 public function getRights()
3486 {
3487 global $user;
3488
3489 return $user->hasRight("fournisseur", "commande");
3490 }
3491
3492
3501 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3502 {
3503 $tables = array(
3504 'commande_fournisseur'
3505 );
3506
3507 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3508 }
3509
3518 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3519 {
3520 $tables = array(
3521 'commande_fournisseurdet'
3522 );
3523
3524 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3525 }
3526
3534 public function hasDelay()
3535 {
3536 global $conf;
3537
3538 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3539 $now = dol_now();
3540 if (!empty($this->delivery_date)) {
3541 $date_to_test = $this->delivery_date;
3542 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3543 } else {
3544 //$date_to_test = $this->date_commande;
3545 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3546 return false;
3547 }
3548 } else {
3549 $now = dol_now();
3550 $date_to_test = $this->date_commande;
3551
3552 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3553 }
3554 }
3555
3563 public function showDelay()
3564 {
3565 global $conf, $langs;
3566
3567 $langs->load('orders');
3568
3569 $text = '';
3570
3571 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3572 if (!empty($this->delivery_date)) {
3573 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3574 } else {
3575 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3576 }
3577 } else {
3578 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3579 }
3580 if ($text) {
3581 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3582 }
3583
3584 return $text;
3585 }
3586
3587
3596 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3597 {
3598 if (isModEnabled("supplier_order")) {
3599 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3600
3601 $qtydelivered = array();
3602 $qtywished = array();
3603
3604 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3605
3606 $filter = array('t.fk_element' => $this->id);
3607 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3608 $filter['t.status'] = 1; // Restrict to lines with status validated
3609 }
3610
3611 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3612 if ($ret < 0) {
3613 $this->error = $supplierorderdispatch->error;
3614 $this->errors = $supplierorderdispatch->errors;
3615 return $ret;
3616 } else {
3617 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3618 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3619 $date_liv = dol_now();
3620
3621 // Build array with quantity deliverd by product
3622 foreach ($supplierorderdispatch->lines as $line) {
3623 if (array_key_exists($line->fk_product, $qtydelivered)) {
3624 $qtydelivered[$line->fk_product] += $line->qty;
3625 } else {
3626 $qtydelivered[$line->fk_product] = $line->qty;
3627 }
3628 }
3629 foreach ($this->lines as $line) {
3630 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3631 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3632 continue;
3633 }
3634 if (array_key_exists($line->fk_product, $qtywished)) {
3635 $qtywished[$line->fk_product] += $line->qty;
3636 } else {
3637 $qtywished[$line->fk_product] = $line->qty;
3638 }
3639 }
3640
3641 //Compare array
3642 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3643 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3644 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3645 //var_dump(array_keys($qtydelivered));
3646 //var_dump(array_keys($qtywished));
3647 //var_dump($diff_array);
3648 //var_dump($keysinwishednotindelivered);
3649 //var_dump($keysindeliverednotinwished);
3650 //exit;
3651
3652 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3653 if ($closeopenorder) {
3654 //$ret=$this->setStatus($user,5);
3655 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3656 if ($ret < 0) {
3657 return -1;
3658 }
3659 return 5;
3660 } else {
3661 //Diff => received partially
3662 //$ret=$this->setStatus($user,4);
3663 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3664 if ($ret < 0) {
3665 return -1;
3666 }
3667 return 4;
3668 }
3669 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3670 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3671
3672 $close = 0;
3673
3674 if (count($diff_array) > 0) {
3675 //there are some difference between the two arrays
3676
3677 //scan the array of results
3678 foreach ($diff_array as $key => $value) {
3679 //if the quantity delivered is greater or equal to wish quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
3680 if ($qtydelivered[$key] >= $qtywished[$key]) {
3681 $close++;
3682 }
3683 }
3684 }
3685
3686
3687 if ($close == count($diff_array)) {
3688 //all the products are received equal or more than the wished quantity
3689 if ($closeopenorder) {
3690 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3691 if ($ret < 0) {
3692 return -1;
3693 }
3694 return 5;
3695 } else {
3696 //Diff => received partially
3697 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3698 if ($ret < 0) {
3699 return -1;
3700 }
3701 return 4;
3702 }
3703 } else {
3704 //all the products are not received
3705 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3706 if ($ret < 0) {
3707 return -1;
3708 }
3709 return 4;
3710 }
3711 } else {
3712 //Diff => received partially
3713 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3714 if ($ret < 0) {
3715 return -1;
3716 }
3717 return 4;
3718 }
3719 }
3720 return 1;
3721 }
3722 }
3723 return 0;
3724 }
3725
3733 public function loadReceptions($filtre_statut = -1)
3734 {
3735 $this->receptions = array();
3736
3737 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3738
3739 $sql = 'SELECT cd.rowid, cd.fk_product,';
3740 $sql .= ' sum(cfd.qty) as qty';
3741 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3742 if ($filtre_statut >= 0) {
3743 $sql .= ' '.$this->db->prefix().'reception as e,';
3744 }
3745 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3746 $sql .= ' WHERE';
3747 if ($filtre_statut >= 0) {
3748 $sql .= ' cfd.fk_reception = e.rowid AND';
3749 }
3750 $sql .= ' cfd.fk_elementdet = cd.rowid';
3751 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3752 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3753 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3754 }
3755 if ($filtre_statut >= 0) {
3756 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3757 }
3758 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3759
3760 $resql = $this->db->query($sql);
3761 if ($resql) {
3762 $num = $this->db->num_rows($resql);
3763 $i = 0;
3764 while ($i < $num) {
3765 $obj = $this->db->fetch_object($resql);
3766 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3767 $i++;
3768 }
3769 $this->db->free($resql);
3770
3771 return $num;
3772 } else {
3773 $this->error = $this->db->lasterror();
3774 return -1;
3775 }
3776 }
3777
3785 public function getKanbanView($option = '', $arraydata = null)
3786 {
3787 global $langs;
3788
3789 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3790
3791 $return = '<div class="box-flex-item box-flex-grow-zero">';
3792 $return .= '<div class="info-box info-box-sm">';
3793 $return .= '<span class="info-box-icon bg-infobox-action">';
3794 $return .= img_picto('', $this->picto);
3795 $return .= '</span>';
3796 $return .= '<div class="info-box-content">';
3797 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3798 if ($selected >= 0) {
3799 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3800 }
3801 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3802 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3803 }
3804 if (property_exists($this, 'billed')) {
3805 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3806 }
3807 if (method_exists($this, 'getLibStatut')) {
3808 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3809 }
3810 $return .= '</div>';
3811 $return .= '</div>';
3812 $return .= '</div>';
3813 return $return;
3814 }
3815}
$object ref
Definition info.php:89
Class to manage table ReceptionLineBatch.
Class to manage predefined suppliers products.
const STATUS_CANCELED_AFTER_ORDER
Order canceled/never received.
const STATUS_RECEIVED_PARTIALLY
Received partially.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
updateFromCommandeClient($user, $idc, $comclientid)
Update a supplier order from a sales order.
getNomUrl($withpicto=0, $option='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable name (with picto eventually)
loadReceptions($filtre_statut=-1)
Load array this->receptions of lines of shipments with nb of products sent for each order line Note: ...
static replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a product id with another one.
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
info($id)
Charge les information d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
fetch_lines($only_product=0)
Load array lines.
getInputMethod()
Returns the translated input method of object (defined if $this->methode_commande_id > 0).
const STATUS_VALIDATED
Validated status.
const STATUS_RECEIVED_COMPLETELY
Received completely.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $fk_prod_fourn_price=0, $ref_supplier='', $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $type=0, $info_bits=0, $notrigger=0, $date_start=null, $date_end=null, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
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:171
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.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_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...
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90