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