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