dolibarr 20.0.5
fournisseur.commande.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
8 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12 * Copyright (C) 2018-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 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
42if (isModEnabled('productbatch')) {
43 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
44}
45
46
51{
55 public $element = 'order_supplier';
56
60 public $table_element = 'commande_fournisseur';
61
65 public $table_element_line = 'commande_fournisseurdet';
66
70 public $fk_element = 'fk_commande';
71
75 public $picto = 'supplier_order';
76
81 public $restrictiononfksoc = 1;
82
86 protected $table_ref_field = 'ref';
87
91 public $id;
92
96 public $ref;
97
101 public $ref_supplier;
102
108 public $ref_fourn;
109
113 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
114 // -> 7=Canceled/Never received -> (reopen) 3=Process running
115 // -> 6=Canceled -> (reopen) 2=Approved
116 // -> 9=Refused -> (reopen) 1=Validated
117 // Note: billed or not is on another field "billed"
118
119 public $billed;
120
124 public $socid;
125
129 public $fourn_id;
130
134 public $date;
135
139 public $date_creation;
140
144 public $date_valid;
145
149 public $date_approve;
150
155 public $date_approve2;
156
160 public $date_commande;
161
162 //For backward compatibility
163 public $remise_percent;
164 public $methode_commande_id;
165 public $methode_commande;
166
170 public $delivery_date;
171
175 public $total_ht;
176
180 public $total_tva;
181
185 public $total_localtax1;
186
190 public $total_localtax2;
191
195 public $total_ttc;
196
197 public $source;
198
202 public $fk_project;
203
207 public $cond_reglement_id;
208
212 public $cond_reglement_code;
213
217 public $cond_reglement_label;
218
222 public $cond_reglement_doc;
223
227 public $fk_account;
228
232 public $mode_reglement_id;
233
237 public $mode_reglement_code;
238
242 public $mode_reglement;
243
247 public $user_author_id;
248
252 public $user_approve_id;
253
258 public $user_approve_id2;
259
260 public $refuse_note;
261
262 public $extraparams = array();
263
267 public $lines = array();
268
272 public $line;
273
274 // Add for supplier_proposal
275 public $origin;
276 public $origin_id;
277 public $linked_objects = array();
278
282 public $date_lim_reglement;
283 public $receptions = array();
284
285 // Multicurrency
289 public $fk_multicurrency;
290
294 public $multicurrency_code;
295
299 public $multicurrency_tx;
300
304 public $multicurrency_total_ht;
305
309 public $multicurrency_total_tva;
310
314 public $multicurrency_total_ttc;
315
343 public $fields = array(
344 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
345 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
346 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => 0, 'position' => 35),
347 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
348 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
349 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
350 'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
351 'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
352 'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
353 'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
354 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
355 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
356 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
357 'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
358 'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
359 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
360 'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
361 'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
362 'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
363 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
364 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
365 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
366 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
367 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
368 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
369 'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
370 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
371 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
372 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
373 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
374 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
375 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
376 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
377 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
378 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
379 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
380 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
381 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
382 'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
383 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
384 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
385 'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
386 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
387 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
388 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
389 );
390
391
395 const STATUS_DRAFT = 0;
396
401
406
411
416
421
426
431
435 const STATUS_REFUSED = 9;
436
437
442
448 public function __construct($db)
449 {
450 $this->db = $db;
451
452 $this->ismultientitymanaged = 1;
453 }
454
455
463 public function fetch($id, $ref = '')
464 {
465 // Check parameters
466 if (empty($id) && empty($ref)) {
467 return -1;
468 }
469
470 $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,";
471 $sql .= " c.localtax1, c.localtax2, ";
472 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
473 $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,";
474 $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,";
475 $sql .= " c.fk_account,";
476 $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
477 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
478 $sql .= " cm.libelle as methode_commande,";
479 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
480 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
481 $sql .= ', c.fk_incoterms, c.location_incoterms';
482 $sql .= ', c.last_main_doc';
483 $sql .= ', i.libelle as label_incoterms';
484 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
485 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
486 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
487 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
488 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
489
490 if (empty($id)) {
491 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
492 } else {
493 $sql .= " WHERE c.rowid=".((int) $id);
494 }
495
496 if ($ref) {
497 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
498 }
499
500 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
501 $resql = $this->db->query($sql);
502 if ($resql) {
503 $obj = $this->db->fetch_object($resql);
504 if (!$obj) {
505 $this->error = 'Bill with id '.$id.' not found';
506 dol_syslog(get_class($this).'::fetch '.$this->error);
507 return 0;
508 }
509
510 $this->id = $obj->rowid;
511 $this->entity = $obj->entity;
512
513 $this->ref = $obj->ref;
514 $this->ref_supplier = $obj->ref_supplier;
515
516 $this->socid = $obj->fk_soc;
517 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
518
519 $this->fourn_id = $obj->fk_soc;
520 $this->statut = $obj->status; // deprecated
521 $this->status = $obj->status;
522 $this->billed = $obj->billed;
523 $this->last_main_doc = $obj->last_main_doc;
524 $this->user_author_id = $obj->user_author_id;
525 $this->user_validation_id = $obj->user_validation_id;
526 $this->user_approve_id = $obj->user_approve_id;
527 $this->user_approve_id2 = $obj->user_approve_id2;
528 $this->total_ht = $obj->total_ht;
529 $this->total_tva = $obj->total_tva;
530 $this->total_localtax1 = $obj->localtax1;
531 $this->total_localtax2 = $obj->localtax2;
532 $this->total_ttc = $obj->total_ttc;
533 $this->date_creation = $this->db->jdate($obj->date_creation);
534 $this->date_valid = $this->db->jdate($obj->date_valid);
535 $this->date_approve = $this->db->jdate($obj->date_approve);
536 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
537 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
538 if (isset($obj->date_commande)) {
539 $this->date = $this->date_commande;
540 } else {
541 $this->date = $this->date_creation;
542 }
543 $this->delivery_date = $this->db->jdate($obj->delivery_date);
544 $this->remise_percent = $obj->remise_percent;
545 $this->methode_commande_id = $obj->fk_input_method;
546 $this->methode_commande = $obj->methode_commande;
547
548 $this->source = $obj->source;
549 $this->fk_project = $obj->fk_project;
550 $this->cond_reglement_id = $obj->fk_cond_reglement;
551 $this->cond_reglement_code = $obj->cond_reglement_code;
552 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
553 $this->cond_reglement_label = $obj->cond_reglement_label;
554 $this->cond_reglement_doc = $obj->cond_reglement_doc;
555 $this->fk_account = $obj->fk_account;
556 $this->mode_reglement_id = $obj->fk_mode_reglement;
557 $this->mode_reglement_code = $obj->mode_reglement_code;
558 $this->mode_reglement = $obj->mode_reglement_libelle;
559 $this->note = $obj->note_private; // deprecated
560 $this->note_private = $obj->note_private;
561 $this->note_public = $obj->note_public;
562 $this->model_pdf = $obj->model_pdf;
563
564 //Incoterms
565 $this->fk_incoterms = $obj->fk_incoterms;
566 $this->location_incoterms = $obj->location_incoterms;
567 $this->label_incoterms = $obj->label_incoterms;
568
569 // Multicurrency
570 $this->fk_multicurrency = $obj->fk_multicurrency;
571 $this->multicurrency_code = $obj->multicurrency_code;
572 $this->multicurrency_tx = $obj->multicurrency_tx;
573 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
574 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
575 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
576
577 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
578
579 $this->db->free($resql);
580
581 // Retrieve all extrafield
582 // fetch optionals attributes and labels
583 $this->fetch_optionals();
584
585 // Lines
586 $result = $this->fetch_lines();
587
588 if ($result < 0) {
589 return -1;
590 } else {
591 return 1;
592 }
593 } else {
594 $this->error = $this->db->error()." sql=".$sql;
595 return -1;
596 }
597 }
598
599 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
606 public function fetch_lines($only_product = 0)
607 {
608 // phpcs:enable
609
610 $this->lines = array();
611
612 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
613 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
614 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
615 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
616 $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,";
617 $sql .= " l.fk_unit,";
618 $sql .= " l.date_start, l.date_end,";
619 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
620 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
621 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
622 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
623 if ($only_product) {
624 $sql .= ' AND p.fk_product_type = 0';
625 }
626 $sql .= " ORDER BY l.rang, l.rowid";
627 //print $sql;
628
629 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
630
631 $result = $this->db->query($sql);
632 if ($result) {
633 $num = $this->db->num_rows($result);
634 $i = 0;
635
636 while ($i < $num) {
637 $objp = $this->db->fetch_object($result);
638
639 $line = new CommandeFournisseurLigne($this->db);
640
641 $line->id = $objp->rowid;
642 $line->fk_commande = $objp->fk_commande;
643 $line->desc = $objp->description;
644 $line->description = $objp->description;
645 $line->qty = $objp->qty;
646 $line->tva_tx = $objp->tva_tx;
647 $line->localtax1_tx = $objp->localtax1_tx;
648 $line->localtax2_tx = $objp->localtax2_tx;
649 $line->localtax1_type = $objp->localtax1_type;
650 $line->localtax2_type = $objp->localtax2_type;
651 $line->subprice = $objp->subprice;
652 $line->pu_ht = $objp->subprice;
653 $line->remise_percent = $objp->remise_percent;
654
655 $line->vat_src_code = $objp->vat_src_code;
656 $line->total_ht = $objp->total_ht;
657 $line->total_tva = $objp->total_tva;
658 $line->total_localtax1 = $objp->total_localtax1;
659 $line->total_localtax2 = $objp->total_localtax2;
660 $line->total_ttc = $objp->total_ttc;
661 $line->product_type = $objp->product_type;
662
663 $line->fk_product = $objp->fk_product;
664
665 $line->libelle = $objp->product_label; // deprecated
666 $line->product_label = $objp->product_label;
667 $line->product_desc = $objp->product_desc;
668 $line->product_tobatch = $objp->product_tobatch;
669 $line->product_barcode = $objp->product_barcode;
670
671 $line->ref = $objp->product_ref; // Ref of product
672 $line->product_ref = $objp->product_ref; // Ref of product
673 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
674 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
675
676 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
677 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
678 // Move this into another method and call it when required.
679
680 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
681 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
682 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
683 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
684 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
685 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
686 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
687 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
688 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
689 $sqlsearchpackage .= " LIMIT 1";
690
691 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
692 if ($resqlsearchpackage) {
693 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
694 if ($objsearchpackage) {
695 $line->fk_fournprice = $objsearchpackage->rowid;
696 $line->packaging = $objsearchpackage->packaging;
697 }
698 } else {
699 $this->error = $this->db->lasterror();
700 return -1;
701 }
702 }
703
704 $line->date_start = $this->db->jdate($objp->date_start);
705 $line->date_end = $this->db->jdate($objp->date_end);
706 $line->fk_unit = $objp->fk_unit;
707
708 // Multicurrency
709 $line->fk_multicurrency = $objp->fk_multicurrency;
710 $line->multicurrency_code = $objp->multicurrency_code;
711 $line->multicurrency_subprice = $objp->multicurrency_subprice;
712 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
713 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
714 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
715
716 $line->info_bits = $objp->info_bits;
717 $line->special_code = $objp->special_code;
718 $line->fk_parent_line = $objp->fk_parent_line;
719
720 $line->rang = $objp->rang;
721
722 // Retrieve all extrafield
723 // fetch optionals attributes and labels
724 $line->fetch_optionals();
725
726 $this->lines[$i] = $line;
727
728 $i++;
729 }
730 $this->db->free($result);
731
732 return $num;
733 } else {
734 $this->error = $this->db->error()." sql=".$sql;
735 return -1;
736 }
737 }
738
747 public function valid($user, $idwarehouse = 0, $notrigger = 0)
748 {
749 global $conf;
750 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
751
752 $error = 0;
753
754 dol_syslog(get_class($this)."::valid");
755 $result = 0;
756 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
757 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
758 $this->db->begin();
759
760 // Definition of supplier order numbering model name
761 $soc = new Societe($this->db);
762 $soc->fetch($this->fourn_id);
763
764 // Check if object has a temporary ref
765 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
766 $num = $this->getNextNumRef($soc);
767 } else {
768 $num = $this->ref;
769 }
770 $this->newref = dol_sanitizeFileName($num);
771
772 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
773 $sql .= " SET ref='".$this->db->escape($num)."',";
774 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
775 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
776 $sql .= " fk_user_valid = ".((int) $user->id);
777 $sql .= " WHERE rowid = ".((int) $this->id);
778 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
779
780 $resql = $this->db->query($sql);
781 if (!$resql) {
782 dol_print_error($this->db);
783 $error++;
784 }
785
786 if (!$error && !$notrigger) {
787 // Call trigger
788 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
789 if ($result < 0) {
790 $error++;
791 }
792 // End call triggers
793 }
794
795 if (!$error) {
796 $this->oldref = $this->ref;
797
798 // Rename directory if dir was a temporary ref
799 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
800 // Now we rename also files into index
801 $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)."'";
802 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
803 $resql = $this->db->query($sql);
804 if (!$resql) {
805 $error++;
806 $this->error = $this->db->lasterror();
807 }
808 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
809 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
810 $resql = $this->db->query($sql);
811 if (!$resql) {
812 $error++;
813 $this->error = $this->db->lasterror();
814 }
815
816 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
817 $oldref = dol_sanitizeFileName($this->ref);
818 $newref = dol_sanitizeFileName($num);
819 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
820 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
821 if (!$error && file_exists($dirsource)) {
822 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
823
824 if (@rename($dirsource, $dirdest)) {
825 dol_syslog("Rename ok");
826 // Rename docs starting with $oldref with $newref
827 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
828 foreach ($listoffiles as $fileentry) {
829 $dirsource = $fileentry['name'];
830 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
831 $dirsource = $fileentry['path'].'/'.$dirsource;
832 $dirdest = $fileentry['path'].'/'.$dirdest;
833 @rename($dirsource, $dirdest);
834 }
835 }
836 }
837 }
838 }
839
840 if (!$error) {
841 $result = 1;
843 $this->statut = self::STATUS_VALIDATED; // deprecated
844 $this->ref = $num;
845 }
846
847 if (!$error) {
848 $this->db->commit();
849 return 1;
850 } else {
851 $this->db->rollback();
852 return -1;
853 }
854 } else {
855 $this->error = 'NotAuthorized';
856 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
857 return -1;
858 }
859 }
860
867 public function getLibStatut($mode = 0)
868 {
869 return $this->LibStatut($this->statut, $mode, $this->billed);
870 }
871
872 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
881 public function LibStatut($status, $mode = 0, $billed = 0)
882 {
883 // phpcs:enable
884 global $langs, $hookmanager;
885
886 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
887 $langs->load('orders');
888
889 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
890 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
891 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
892 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
893 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
894 } else {
895 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
896 }
897 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
898 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
899 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
900 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
901 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
902
903 // List of language codes for status
904 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
905 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
906 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
907 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
908 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
909 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
910 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
911 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
912 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
913 }
914
915 $statustrans = array(
916 0 => 'status0',
917 1 => 'status1b',
918 2 => 'status1',
919 3 => 'status4',
920 4 => 'status4b',
921 5 => 'status6',
922 6 => 'status9',
923 7 => 'status9',
924 9 => 'status9',
925 );
926
927 $statusClass = 'status0';
928 if (!empty($statustrans[$status])) {
929 $statusClass = $statustrans[$status];
930 }
931
932 $billedtext = '';
933 if ($billed) {
934 $billedtext = ' - '.$langs->trans("Billed");
935 }
936 if ($status == 5 && $billed) {
937 $statusClass = 'status6';
938 }
939
940 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
941 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
942
943 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
944 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
945 if ($reshook > 0) {
946 return $hookmanager->resPrint;
947 }
948
949 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
950 }
951
959 public function getTooltipContentArray($params)
960 {
961 global $conf, $langs, $user;
962
963 $langs->loadLangs(['bills', 'orders']);
964
965 $datas = [];
966 $nofetch = !empty($params['nofetch']);
967
968 if ($user->hasRight("fournisseur", "commande", "read")) {
969 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
970 if (isset($this->statut)) {
971 $datas['picto'] .= ' '.$this->getLibStatut(5);
972 }
973 if (!empty($this->ref)) {
974 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
975 }
976 if (!empty($this->ref_supplier)) {
977 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
978 }
979 if (!$nofetch) {
980 $langs->load('companies');
981 if (empty($this->thirdparty)) {
982 $this->fetch_thirdparty();
983 }
984 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
985 }
986 if (!empty($this->total_ht)) {
987 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
988 }
989 if (!empty($this->total_tva)) {
990 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
991 }
992 if (!empty($this->total_ttc)) {
993 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
994 }
995 if (!empty($this->date)) {
996 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
997 }
998 if (!empty($this->delivery_date)) {
999 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
1000 }
1001 }
1002 return $datas;
1003 }
1004
1015 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1016 {
1017 global $langs, $user, $hookmanager;
1018
1019 $result = '';
1020 $params = [
1021 'id' => $this->id,
1022 'objecttype' => $this->element,
1023 'option' => $option,
1024 'nofetch' => 1
1025 ];
1026 $classfortooltip = 'classfortooltip';
1027 $dataparams = '';
1028 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1029 $classfortooltip = 'classforajaxtooltip';
1030 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1031 $label = '';
1032 } else {
1033 $label = implode($this->getTooltipContentArray($params));
1034 }
1035
1036 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1037
1038 if ($option !== 'nolink') {
1039 // Add param to save lastsearch_values or not
1040 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1041 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1042 $add_save_lastsearch_values = 1;
1043 }
1044 if ($add_save_lastsearch_values) {
1045 $url .= '&save_lastsearch_values=1';
1046 }
1047 }
1048
1049 $linkclose = '';
1050 if (empty($notooltip)) {
1051 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1052 $label = $langs->trans("ShowOrder");
1053 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1054 }
1055 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1056 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1057 }
1058
1059 $linkstart = '<a href="'.$url.'"';
1060 $linkstart .= $linkclose.'>';
1061 $linkend = '</a>';
1062
1063 $result .= $linkstart;
1064 if ($withpicto) {
1065 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1066 }
1067 if ($withpicto != 2) {
1068 $result .= $this->ref;
1069 }
1070 $result .= $linkend;
1071
1072 if ($addlinktonotes) {
1073 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1074 if ($txttoshow) {
1075 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1076 $result .= ' <span class="note inline-block">';
1077 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1078 $result .= img_picto('', 'note');
1079 $result .= '</a>';
1080 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1081 //$result.='</a>';
1082 $result .= '</span>';
1083 }
1084 }
1085
1086 global $action;
1087 $hookmanager->initHooks(array($this->element . 'dao'));
1088 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1089 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1090 if ($reshook > 0) {
1091 $result = $hookmanager->resPrint;
1092 } else {
1093 $result .= $hookmanager->resPrint;
1094 }
1095 return $result;
1096 }
1097
1098
1106 public function getNextNumRef($soc)
1107 {
1108 global $langs, $conf;
1109 $langs->load("orders");
1110
1111 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1112 $mybool = false;
1113
1114 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1115 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1116
1117 // Include file with class
1118 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1119
1120 foreach ($dirmodels as $reldir) {
1121 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1122
1123 // Load file with numbering class (if found)
1124 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1125 }
1126
1127 if ($mybool === false) {
1128 dol_print_error(null, "Failed to include file ".$file);
1129 return '';
1130 }
1131
1132 $obj = new $classname();
1133 $numref = $obj->getNextValue($soc, $this);
1134
1135 if ($numref != "") {
1136 return $numref;
1137 } else {
1138 $this->error = $obj->error;
1139 return -1;
1140 }
1141 } else {
1142 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1143 return -2;
1144 }
1145 }
1152 public function classifyBilled(User $user)
1153 {
1154 $error = 0;
1155
1156 if ($this->billed) {
1157 return 0;
1158 }
1159
1160 $this->db->begin();
1161
1162 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1163 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1164
1165 if ($this->db->query($sql)) {
1166 if (!$error) {
1167 // Call trigger
1168 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1169 if ($result < 0) {
1170 $error++;
1171 }
1172 // End call triggers
1173 }
1174
1175 if (!$error) {
1176 $this->billed = 1;
1177
1178 $this->db->commit();
1179 return 1;
1180 } else {
1181 $this->db->rollback();
1182 return -1;
1183 }
1184 } else {
1185 dol_print_error($this->db);
1186
1187 $this->db->rollback();
1188 return -1;
1189 }
1190 }
1191
1200 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1201 {
1202 global $langs, $conf;
1203 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1204
1205 $error = 0;
1206
1207 dol_syslog(get_class($this)."::approve");
1208
1209 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1210 $now = dol_now();
1211
1212 $this->db->begin();
1213
1214 // Definition of order numbering model name
1215 $soc = new Societe($this->db);
1216 $soc->fetch($this->fourn_id);
1217
1218 // Check if object has a temporary ref
1219 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1220 $num = $this->getNextNumRef($soc);
1221 } else {
1222 $num = $this->ref;
1223 }
1224 $this->newref = dol_sanitizeFileName($num);
1225
1226 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1227 $movetoapprovestatus = true;
1228 $comment = '';
1229
1230 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1231 $sql .= " SET ref='".$this->db->escape($num)."',";
1232 if (empty($secondlevel)) { // standard or first level approval
1233 $sql .= " date_approve='".$this->db->idate($now)."',";
1234 $sql .= " fk_user_approve = ".$user->id;
1235 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1236 if (empty($this->user_approve_id2)) {
1237 $movetoapprovestatus = false; // second level approval not done
1238 $comment = ' (first level)';
1239 }
1240 }
1241 } else { // request a second level approval
1242 $sql .= " date_approve2='".$this->db->idate($now)."',";
1243 $sql .= " fk_user_approve2 = ".((int) $user->id);
1244 if (empty($this->user_approve_id)) {
1245 $movetoapprovestatus = false; // first level approval not done
1246 }
1247 $comment = ' (second level)';
1248 }
1249 // If double approval is required and first approval, we keep status to 1 = validated
1250 if ($movetoapprovestatus) {
1251 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1252 } else {
1253 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1254 }
1255 $sql .= " WHERE rowid = ".((int) $this->id);
1256 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1257
1258 if ($this->db->query($sql)) {
1259 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1260 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1261 if ($result < 0 && $result != -2) { // -2 means already exists
1262 $error++;
1263 }
1264 }
1265
1266 // If stock is incremented on validate order, we must increment it
1267 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1268 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1269 $langs->load("agenda");
1270
1271 $cpt = count($this->lines);
1272 for ($i = 0; $i < $cpt; $i++) {
1273 // Product with reference
1274 if ($this->lines[$i]->fk_product > 0) {
1275 $this->line = $this->lines[$i];
1276 $mouvP = new MouvementStock($this->db);
1277 $mouvP->origin = &$this;
1278 $mouvP->setOrigin($this->element, $this->id);
1279 // We decrement stock of product (and sub-products)
1280 $up_ht_disc = $this->lines[$i]->subprice;
1281 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1282 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1283 }
1284 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1285 if ($result < 0) {
1286 $error++;
1287 }
1288 unset($this->line);
1289 }
1290 }
1291 }
1292
1293 if (!$error) {
1294 // Call trigger
1295 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1296 if ($result < 0) {
1297 $error++;
1298 }
1299 // End call triggers
1300 }
1301
1302 if (!$error) {
1303 $this->ref = $this->newref;
1304
1305 if ($movetoapprovestatus) {
1307 } else {
1309 }
1310 if (empty($secondlevel)) { // standard or first level approval
1311 $this->date_approve = $now;
1312 $this->user_approve_id = $user->id;
1313 } else { // request a second level approval
1314 $this->date_approve2 = $now;
1315 $this->user_approve_id2 = $user->id;
1316 }
1317
1318 $this->db->commit();
1319 return 1;
1320 } else {
1321 $this->db->rollback();
1322 return -1;
1323 }
1324 } else {
1325 $this->db->rollback();
1326 $this->error = $this->db->lasterror();
1327 return -1;
1328 }
1329 } else {
1330 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1331 }
1332 return -1;
1333 }
1334
1341 public function refuse($user)
1342 {
1343 global $conf, $langs;
1344
1345 $error = 0;
1346
1347 dol_syslog(get_class($this)."::refuse");
1348 $result = 0;
1349 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1350 $this->db->begin();
1351
1352 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1353 $sql .= " WHERE rowid = ".((int) $this->id);
1354
1355 if ($this->db->query($sql)) {
1356 $result = 0;
1357
1358 if ($error == 0) {
1359 // Call trigger
1360 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1361 if ($result < 0) {
1362 $error++;
1363 $this->db->rollback();
1364 } else {
1365 $this->db->commit();
1366 }
1367 // End call triggers
1368 }
1369 } else {
1370 $this->db->rollback();
1371 $this->error = $this->db->lasterror();
1372 dol_syslog(get_class($this)."::refuse Error -1");
1373 $result = -1;
1374 }
1375 } else {
1376 dol_syslog(get_class($this)."::refuse Not Authorized");
1377 }
1378 return $result;
1379 }
1380
1381 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1390 public function Cancel($user, $idwarehouse = -1)
1391 {
1392 // phpcs:enable
1393 global $langs, $conf;
1394
1395 $error = 0;
1396
1397 //dol_syslog("CommandeFournisseur::Cancel");
1398 $result = 0;
1399 if ($user->hasRight("fournisseur", "commande", "commander")) {
1400 $statut = self::STATUS_CANCELED;
1401
1402 $this->db->begin();
1403
1404 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1405 $sql .= " WHERE rowid = ".((int) $this->id);
1406 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1407 if ($this->db->query($sql)) {
1408 $result = 0;
1409
1410 // Call trigger
1411 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1412 if ($result < 0) {
1413 $error++;
1414 }
1415 // End call triggers
1416
1417 if ($error == 0) {
1418 $this->db->commit();
1419 return 1;
1420 } else {
1421 $this->db->rollback();
1422 return -1;
1423 }
1424 } else {
1425 $this->db->rollback();
1426 $this->error = $this->db->lasterror();
1427 dol_syslog(get_class($this)."::cancel ".$this->error);
1428 return -1;
1429 }
1430 } else {
1431 dol_syslog(get_class($this)."::cancel Not Authorized");
1432 return -1;
1433 }
1434 }
1435
1445 public function commande($user, $date, $methode, $comment = '')
1446 {
1447 global $langs;
1448 dol_syslog(get_class($this)."::commande");
1449 $error = 0;
1450 if ($user->hasRight("fournisseur", "commande", "commander")) {
1451 $this->db->begin();
1452
1453 $newnoteprivate = $this->note_private;
1454 if ($comment) {
1455 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1456 }
1457
1458 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1459 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1460 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1461 $sql .= " WHERE rowid=".((int) $this->id);
1462
1463 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1464 if ($this->db->query($sql)) {
1466 $this->methode_commande_id = $methode;
1467 $this->date_commande = $date;
1468 $this->context = array('comments' => $comment);
1469
1470 // Call trigger
1471 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1472 if ($result < 0) {
1473 $error++;
1474 }
1475 // End call triggers
1476 } else {
1477 $error++;
1478 $this->error = $this->db->lasterror();
1479 $this->errors[] = $this->db->lasterror();
1480 }
1481
1482 if (!$error) {
1483 $this->db->commit();
1484 } else {
1485 $this->db->rollback();
1486 }
1487 } else {
1488 $error++;
1489 $this->error = $langs->trans('NotAuthorized');
1490 $this->errors[] = $langs->trans('NotAuthorized');
1491 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1492 }
1493
1494 return ($error ? -1 : 1);
1495 }
1496
1504 public function create($user, $notrigger = 0)
1505 {
1506 global $langs, $conf, $hookmanager;
1507
1508 $this->db->begin();
1509
1510 $error = 0;
1511 $now = dol_now();
1512
1513 // set tmp vars
1514 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1515 if (empty($date)) {
1516 $date = $now;
1517 }
1518 $delivery_date = $this->delivery_date;
1519
1520 // Clean parameters
1521 if (empty($this->source)) {
1522 $this->source = 0;
1523 }
1524
1525 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1526 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1527 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1528 } else {
1529 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1530 }
1531 if (empty($this->fk_multicurrency)) {
1532 $this->multicurrency_code = $conf->currency;
1533 $this->fk_multicurrency = 0;
1534 $this->multicurrency_tx = 1;
1535 }
1536
1537 // We set order into draft status
1538 $this->statut = self::STATUS_DRAFT; // deprecated
1539 $this->status = self::STATUS_DRAFT;
1540
1541 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1542 $sql .= "ref";
1543 $sql .= ", ref_supplier";
1544 $sql .= ", note_private";
1545 $sql .= ", note_public";
1546 $sql .= ", entity";
1547 $sql .= ", fk_soc";
1548 $sql .= ", fk_projet";
1549 $sql .= ", date_creation";
1550 $sql .= ", date_livraison";
1551 $sql .= ", fk_user_author";
1552 $sql .= ", fk_statut";
1553 $sql .= ", source";
1554 $sql .= ", model_pdf";
1555 $sql .= ", fk_mode_reglement";
1556 $sql .= ", fk_cond_reglement";
1557 $sql .= ", fk_account";
1558 $sql .= ", fk_incoterms, location_incoterms";
1559 $sql .= ", fk_multicurrency";
1560 $sql .= ", multicurrency_code";
1561 $sql .= ", multicurrency_tx";
1562 $sql .= ") ";
1563 $sql .= " VALUES (";
1564 $sql .= "'(PROV)'";
1565 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1566 $sql .= ", '".$this->db->escape($this->note_private)."'";
1567 $sql .= ", '".$this->db->escape($this->note_public)."'";
1568 $sql .= ", ".setEntity($this);
1569 $sql .= ", ".((int) $this->socid);
1570 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1571 $sql .= ", '".$this->db->idate($date)."'";
1572 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1573 $sql .= ", ".((int) $user->id);
1574 $sql .= ", ".self::STATUS_DRAFT;
1575 $sql .= ", ".((int) $this->source);
1576 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1577 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1578 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1579 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1580 $sql .= ", ".(int) $this->fk_incoterms;
1581 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1582 $sql .= ", ".(int) $this->fk_multicurrency;
1583 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1584 $sql .= ", ".(float) $this->multicurrency_tx;
1585 $sql .= ")";
1586
1587 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1588 if ($this->db->query($sql)) {
1589 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1590
1591 if ($this->id) {
1592 $num = count($this->lines);
1593
1594 // insert products details into database
1595 for ($i = 0; $i < $num; $i++) {
1596 $line = $this->lines[$i];
1597 if (!is_object($line)) {
1598 $line = (object) $line;
1599 }
1600
1601 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1602
1603 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1604 $result = $this->addline(
1605 $line->desc,
1606 $line->subprice,
1607 $line->qty,
1608 $line->tva_tx,
1609 $line->localtax1_tx,
1610 $line->localtax2_tx,
1611 $line->fk_product,
1612 0,
1613 $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
1614 $line->remise_percent,
1615 'HT',
1616 0,
1617 $line->product_type,
1618 $line->info_bits,
1619 false,
1620 $line->date_start,
1621 $line->date_end,
1622 $line->array_options,
1623 $line->fk_unit,
1624 $line->multicurrency_subprice, // pu_ht_devise
1625 $line->origin, // origin
1626 $line->origin_id, // origin_id
1627 $line->rang, // rang
1628 $line->special_code
1629 );
1630 if ($result < 0) {
1631 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1632 $this->db->rollback();
1633 return -1;
1634 }
1635 }
1636
1637 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1638 $sql .= " SET ref='(PROV".$this->id.")'";
1639 $sql .= " WHERE rowid=".((int) $this->id);
1640
1641 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1642 if ($this->db->query($sql)) {
1643 // Add link with price request and supplier order
1644 if ($this->id) {
1645 $this->ref = "(PROV".$this->id.")";
1646
1647 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1648 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1649 }
1650
1651 // Add object linked
1652 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1653 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1654 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, ...))
1655 foreach ($tmp_origin_id as $origin_id) {
1656 $ret = $this->add_object_linked($origin, $origin_id);
1657 if (!$ret) {
1658 dol_print_error($this->db);
1659 $error++;
1660 }
1661 }
1662 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1663 $origin_id = $tmp_origin_id;
1664 $ret = $this->add_object_linked($origin, $origin_id);
1665 if (!$ret) {
1666 dol_print_error($this->db);
1667 $error++;
1668 }
1669 }
1670 }
1671 }
1672 }
1673
1674 if (!$error) {
1675 $result = $this->insertExtraFields();
1676 if ($result < 0) {
1677 $error++;
1678 }
1679 }
1680
1681 if (!$error && !$notrigger) {
1682 // Call trigger
1683 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1684 if ($result < 0) {
1685 $this->db->rollback();
1686
1687 return -1;
1688 }
1689 // End call triggers
1690 }
1691
1692 if (!$error) {
1693 $this->db->commit();
1694 return $this->id;
1695 } else {
1696 $this->db->rollback();
1697 return -4;
1698 }
1699 } else {
1700 $this->error = $this->db->lasterror();
1701 $this->db->rollback();
1702
1703 return -2;
1704 }
1705 } else {
1706 $this->error = 'Failed to get ID of inserted line';
1707
1708 return -1;
1709 }
1710 } else {
1711 $this->error = $this->db->lasterror();
1712 $this->db->rollback();
1713
1714 return -1;
1715 }
1716 }
1717
1725 public function update(User $user, $notrigger = 0)
1726 {
1727 global $conf;
1728
1729 $error = 0;
1730
1731 // Clean parameters
1732 if (isset($this->ref)) {
1733 $this->ref = trim($this->ref);
1734 }
1735 if (isset($this->ref_supplier)) {
1736 $this->ref_supplier = trim($this->ref_supplier);
1737 }
1738 if (isset($this->note_private)) {
1739 $this->note_private = trim($this->note_private);
1740 }
1741 if (isset($this->note_public)) {
1742 $this->note_public = trim($this->note_public);
1743 }
1744 if (isset($this->model_pdf)) {
1745 $this->model_pdf = trim($this->model_pdf);
1746 }
1747 if (isset($this->import_key)) {
1748 $this->import_key = trim($this->import_key);
1749 }
1750
1751 // Update request
1752 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1753
1754 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1755 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1756 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1757 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1758 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1759 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1760 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1761 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1762 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1763 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1764 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1765 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1766 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1767 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1768 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1769 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1770 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1771 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1772 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1773 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1774 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1775 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1776 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1777 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1778 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1779
1780 $sql .= " WHERE rowid=".((int) $this->id);
1781
1782 $this->db->begin();
1783
1784 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1785 $resql = $this->db->query($sql);
1786 if (!$resql) {
1787 $error++;
1788 $this->errors[] = "Error ".$this->db->lasterror();
1789 }
1790
1791 if (!$error) {
1792 $result = $this->insertExtraFields();
1793 if ($result < 0) {
1794 $error++;
1795 }
1796 }
1797
1798 if (!$error && !$notrigger) {
1799 // Call trigger
1800 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1801 if ($result < 0) {
1802 $error++;
1803 }
1804 // End call triggers
1805 }
1806
1807 // Commit or rollback
1808 if ($error) {
1809 foreach ($this->errors as $errmsg) {
1810 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1811 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1812 }
1813 $this->db->rollback();
1814 return -1 * $error;
1815 } else {
1816 $this->db->commit();
1817 return 1;
1818 }
1819 }
1820
1829 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1830 {
1831 global $conf, $user, $hookmanager;
1832
1833 $error = 0;
1834
1835 $this->db->begin();
1836
1837 // get extrafields so they will be clone
1838 foreach ($this->lines as $line) {
1839 $line->fetch_optionals();
1840 }
1841
1842 // Load source object
1843 $objFrom = clone $this;
1844
1845 // Change socid if needed
1846 if (!empty($socid) && $socid != $this->socid) {
1847 $objsoc = new Societe($this->db);
1848
1849 if ($objsoc->fetch($socid) > 0) {
1850 $this->socid = $objsoc->id;
1851 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1852 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1853 $this->fk_project = 0;
1854 $this->fk_delivery_address = 0;
1855 }
1856
1857 // TODO Change product price if multi-prices
1858 }
1859
1860 $this->id = 0;
1861 $this->statut = self::STATUS_DRAFT;
1862
1863 // Clear fields
1864 $this->user_author_id = $user->id;
1865 $this->user_validation_id = 0;
1866
1867 $this->date = dol_now();
1868 $this->date_creation = 0;
1869 $this->date_validation = 0;
1870 $this->date_commande = 0;
1871 $this->ref_supplier = '';
1872 $this->user_approve_id = 0;
1873 $this->user_approve_id2 = 0;
1874 $this->date_approve = 0;
1875 $this->date_approve2 = 0;
1876
1877 // Create clone
1878 $this->context['createfromclone'] = 'createfromclone';
1879 $result = $this->create($user, $notrigger);
1880 if ($result < 0) {
1881 $error++;
1882 }
1883
1884 if (!$error) {
1885 // Hook of thirdparty module
1886 if (is_object($hookmanager)) {
1887 $parameters = array('objFrom' => $objFrom);
1888 $action = '';
1889 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1890 if ($reshook < 0) {
1891 $this->setErrorsFromObject($hookmanager);
1892 $error++;
1893 }
1894 }
1895 }
1896
1897 unset($this->context['createfromclone']);
1898
1899 // End
1900 if (!$error) {
1901 $this->db->commit();
1902 return $this->id;
1903 } else {
1904 $this->db->rollback();
1905 return -1;
1906 }
1907 }
1908
1938 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)
1939 {
1940 global $langs, $mysoc, $conf;
1941
1942 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");
1943 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1944
1945 if ($this->statut == self::STATUS_DRAFT) {
1946 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1947
1948 // Clean parameters
1949 if (empty($qty)) {
1950 $qty = 0;
1951 }
1952 if (!$info_bits) {
1953 $info_bits = 0;
1954 }
1955 if (empty($txtva)) {
1956 $txtva = 0;
1957 }
1958 if (empty($rang)) {
1959 $rang = 0;
1960 }
1961 if (empty($txlocaltax1)) {
1962 $txlocaltax1 = 0;
1963 }
1964 if (empty($txlocaltax2)) {
1965 $txlocaltax2 = 0;
1966 }
1967 if (empty($remise_percent)) {
1968 $remise_percent = 0;
1969 }
1970
1971 $remise_percent = price2num($remise_percent);
1972 $qty = price2num($qty);
1973 $pu_ht = price2num($pu_ht);
1974 $pu_ht_devise = price2num($pu_ht_devise);
1975 $pu_ttc = price2num($pu_ttc);
1976 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1977 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1978 }
1979 $txlocaltax1 = price2num($txlocaltax1);
1980 $txlocaltax2 = price2num($txlocaltax2);
1981 if ($price_base_type == 'HT') {
1982 $pu = $pu_ht;
1983 } else {
1984 $pu = $pu_ttc;
1985 }
1986 $desc = trim($desc);
1987
1988 // Check parameters
1989 if ($qty < 0 && !$fk_product) {
1990 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1991 return -1;
1992 }
1993 if ($type < 0) {
1994 return -1;
1995 }
1996 if ($date_start && $date_end && $date_start > $date_end) {
1997 $langs->load("errors");
1998 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1999 return -1;
2000 }
2001
2002
2003 $this->db->begin();
2004
2005 $product_type = $type;
2006 $label = ''; // deprecated
2007
2008 if ($fk_product > 0) {
2009 if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
2010 // Check quantity is enough
2011 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);
2012 $prod = new ProductFournisseur($this->db);
2013 if ($prod->fetch($fk_product) > 0) {
2014 $product_type = $prod->type;
2015 $label = $prod->label;
2016
2017 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2018 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2019 $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
2020
2021 // If supplier order created from sales order, we take best supplier price
2022 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2023 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2024 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2025 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2026 // is remise percent not keyed but present for the product we add it
2027 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2028 $remise_percent = $prod->remise_percent;
2029 }
2030 }
2031 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2032 $langs->load("errors");
2033 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2034 $this->db->rollback();
2035 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2036 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2037 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2038 return -1;
2039 }
2040 if ($result == -1) {
2041 $langs->load("errors");
2042 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2043 $this->db->rollback();
2044 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2045 return -1;
2046 }
2047 if ($result < -1) {
2048 $this->error = $prod->error;
2049 $this->db->rollback();
2050 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2051 return -1;
2052 }
2053 } else {
2054 $this->error = $prod->error;
2055 $this->db->rollback();
2056 return -1;
2057 }
2058 }
2059
2060 // Predefine quantity according to packaging
2061 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2062 $prod = new Product($this->db);
2063 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2064
2065 if ($qty < $prod->packaging) {
2066 $qty = $prod->packaging;
2067 } else {
2068 if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
2069 $coeff = intval($qty / $prod->packaging) + 1;
2070 $qty = $prod->packaging * $coeff;
2071 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2072 }
2073 }
2074 }
2075 }
2076
2077 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2078 $pu = 0;
2079 }
2080
2081 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2082
2083 // Clean vat code
2084 $reg = array();
2085 $vat_src_code = '';
2086 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2087 $vat_src_code = $reg[1];
2088 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2089 }
2090
2091 // Calcul du total TTC et de la TVA pour la ligne a partir de
2092 // qty, pu, remise_percent et txtva
2093 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2094 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2095
2096 $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);
2097
2098 $total_ht = $tabprice[0];
2099 $total_tva = $tabprice[1];
2100 $total_ttc = $tabprice[2];
2101 $total_localtax1 = $tabprice[9];
2102 $total_localtax2 = $tabprice[10];
2103 $pu = $pu_ht = $tabprice[3];
2104
2105 // MultiCurrency
2106 $multicurrency_total_ht = $tabprice[16];
2107 $multicurrency_total_tva = $tabprice[17];
2108 $multicurrency_total_ttc = $tabprice[18];
2109 $pu_ht_devise = $tabprice[19];
2110
2111 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2112 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2113
2114 if ($rang < 0) {
2115 $rangmax = $this->line_max();
2116 $rang = $rangmax + 1;
2117 }
2118
2119 // Insert line
2120 $this->line = new CommandeFournisseurLigne($this->db);
2121
2122 $this->line->context = $this->context;
2123
2124 $this->line->fk_commande = $this->id;
2125 $this->line->label = $label;
2126 $this->line->ref_fourn = $ref_supplier;
2127 $this->line->ref_supplier = $ref_supplier;
2128 $this->line->desc = $desc;
2129 $this->line->qty = $qty;
2130 $this->line->tva_tx = $txtva;
2131 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2132 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2133 $this->line->localtax1_type = $localtax1_type;
2134 $this->line->localtax2_type = $localtax2_type;
2135 $this->line->fk_product = $fk_product;
2136 $this->line->product_type = $product_type;
2137 $this->line->remise_percent = $remise_percent;
2138 $this->line->subprice = $pu_ht;
2139 $this->line->rang = $rang;
2140 $this->line->info_bits = $info_bits;
2141
2142 $this->line->vat_src_code = $vat_src_code;
2143 $this->line->total_ht = $total_ht;
2144 $this->line->total_tva = $total_tva;
2145 $this->line->total_localtax1 = $total_localtax1;
2146 $this->line->total_localtax2 = $total_localtax2;
2147 $this->line->total_ttc = $total_ttc;
2148 $this->line->product_type = $type;
2149 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2150 $this->line->origin = $origin;
2151 $this->line->origin_id = $origin_id;
2152 $this->line->fk_unit = $fk_unit;
2153
2154 $this->line->date_start = $date_start;
2155 $this->line->date_end = $date_end;
2156
2157 // Multicurrency
2158 $this->line->fk_multicurrency = $this->fk_multicurrency;
2159 $this->line->multicurrency_code = $this->multicurrency_code;
2160 $this->line->multicurrency_subprice = $pu_ht_devise;
2161 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2162 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2163 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2164
2165 $this->line->subprice = $pu_ht;
2166 $this->line->price = $this->line->subprice;
2167
2168 $this->line->remise_percent = $remise_percent;
2169
2170 if (is_array($array_options) && count($array_options) > 0) {
2171 $this->line->array_options = $array_options;
2172 }
2173
2174 $result = $this->line->insert($notrigger);
2175 if ($result > 0) {
2176 // Reorder if child line
2177 if (!empty($this->line->fk_parent_line)) {
2178 $this->line_order(true, 'DESC');
2179 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2180 $linecount = count($this->lines);
2181 for ($ii = $rang; $ii <= $linecount; $ii++) {
2182 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2183 }
2184 }
2185
2186 // Mise a jour information denormalisees au niveau de la commande meme
2187 $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.
2188 if ($result > 0) {
2189 $this->db->commit();
2190 return $this->line->id;
2191 } else {
2192 $this->db->rollback();
2193 return -1;
2194 }
2195 } else {
2196 $this->error = $this->line->error;
2197 $this->errors = $this->line->errors;
2198 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2199 $this->db->rollback();
2200 return -1;
2201 }
2202 }
2203 return -1;
2204 }
2205
2206
2224 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2225 {
2226 global $conf, $langs;
2227
2228 $error = 0;
2229 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2230
2231 // Check parameters (if test are wrong here, there is bug into caller)
2232 if ($entrepot <= 0) {
2233 $this->error = 'ErrorBadValueForParameterWarehouse';
2234 return -1;
2235 }
2236 if ($qty == 0) {
2237 $this->error = 'ErrorBadValueForParameterQty';
2238 return -1;
2239 }
2240
2241 $dispatchstatus = 1;
2242 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2243 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2244 }
2245
2246 $now = dol_now();
2247
2248 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2249
2250 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2251 $this->db->begin();
2252
2253 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2254 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2255 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2256 $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");
2257 $sql .= ")";
2258
2259 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2260 $resql = $this->db->query($sql);
2261 if ($resql) {
2262 if (!$notrigger) {
2263 global $conf, $langs, $user;
2264 // Call trigger
2265 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2266 if ($result < 0) {
2267 $error++;
2268 }
2269 // End call triggers
2270 }
2271 } else {
2272 $this->error = $this->db->lasterror();
2273 $error++;
2274 }
2275
2276 // If module stock is enabled and the stock increase is done on purchase order dispatching
2277 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2278 $mouv = new MouvementStock($this->db);
2279 if ($product > 0) {
2280 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2281 $mouv->origin = &$this;
2282 $mouv->setOrigin($this->element, $this->id);
2283
2284 // Method change if qty < 0
2285 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2286 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2287 } else {
2288 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2289 }
2290
2291 if ($result < 0) {
2292 $this->error = $mouv->error;
2293 $this->errors = $mouv->errors;
2294 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2295 $error++;
2296 }
2297 }
2298 }
2299
2300 if ($error == 0) {
2301 $this->db->commit();
2302 return 1;
2303 } else {
2304 $this->db->rollback();
2305 return -1;
2306 }
2307 } else {
2308 $this->error = 'BadStatusForObject';
2309 return -2;
2310 }
2311 }
2312
2320 public function deleteLine($idline, $notrigger = 0)
2321 {
2322 global $user;
2323
2324 if ($this->statut == 0) {
2325 $line = new CommandeFournisseurLigne($this->db);
2326
2327 if ($line->fetch($idline) <= 0) {
2328 return 0;
2329 }
2330
2331 // check if not yet received
2332 $dispatchedLines = $this->getDispachedLines();
2333 foreach ($dispatchedLines as $dispatchLine) {
2334 if ($dispatchLine['orderlineid'] == $idline) {
2335 $this->error = "LineAlreadyDispatched";
2336 $this->errors[] = $this->error;
2337 return -3;
2338 }
2339 }
2340
2341 if ($line->delete($user, $notrigger) > 0) {
2342 $this->update_price(1);
2343 return 1;
2344 } else {
2345 $this->setErrorsFromObject($line);
2346 return -1;
2347 }
2348 } else {
2349 return -2;
2350 }
2351 }
2352
2360 public function delete(User $user, $notrigger = 0)
2361 {
2362 global $langs, $conf;
2363 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2364
2365 $error = 0;
2366
2367 $this->db->begin();
2368
2369 if (empty($notrigger)) {
2370 // Call trigger
2371 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2372 if ($result < 0) {
2373 $this->errors[] = 'ErrorWhenRunningTrigger';
2374 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2375 $this->db->rollback();
2376 return -1;
2377 }
2378 // End call triggers
2379 }
2380
2381 // Test we can delete
2382 $this->fetchObjectLinked(null, 'order_supplier');
2383 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2384 foreach ($this->linkedObjects['reception'] as $element) {
2385 if ($element->statut >= 0) {
2386 $this->errors[] = $langs->trans('ReceptionExist');
2387 $error++;
2388 break;
2389 }
2390 }
2391 }
2392
2393 $main = $this->db->prefix().'commande_fournisseurdet';
2394
2395 if (!$error) {
2396 $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).")";
2397 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2398 if (!$this->db->query($sql1)) {
2399 $error++;
2400 $this->error = $this->db->lasterror();
2401 $this->errors[] = $this->db->lasterror();
2402 }
2403 }
2404
2405 if (!$error) {
2406 $ef = $main."_extrafields";
2407 $sql = "DELETE FROM ".$this->db->sanitize($ef)." WHERE fk_object IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2408 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2409 if (!$this->db->query($sql)) {
2410 $this->error = $this->db->lasterror();
2411 $this->errors[] = $this->db->lasterror();
2412 $error++;
2413 }
2414 }
2415
2416 if (!$error) {
2417 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande = ".((int) $this->id);
2418 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2419 if (!$this->db->query($sql)) {
2420 $this->error = $this->db->lasterror();
2421 $this->errors[] = $this->db->lasterror();
2422 $error++;
2423 }
2424 }
2425
2426 if (!$error) {
2427 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid = ".((int) $this->id);
2428 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2429 if ($resql = $this->db->query($sql)) {
2430 if ($this->db->affected_rows($resql) < 1) {
2431 $this->error = $this->db->lasterror();
2432 $this->errors[] = $this->db->lasterror();
2433 $error++;
2434 }
2435 } else {
2436 $this->error = $this->db->lasterror();
2437 $this->errors[] = $this->db->lasterror();
2438 $error++;
2439 }
2440 }
2441
2442 // Remove extrafields
2443 if (!$error) {
2444 $result = $this->deleteExtraFields();
2445 if ($result < 0) {
2446 $this->error = 'FailToDeleteExtraFields';
2447 $this->errors[] = 'FailToDeleteExtraFields';
2448 $error++;
2449 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2450 }
2451 }
2452
2453 // Delete linked object
2454 $res = $this->deleteObjectLinked();
2455 if ($res < 0) {
2456 $this->error = 'FailToDeleteObjectLinked';
2457 $this->errors[] = 'FailToDeleteObjectLinked';
2458 $error++;
2459 }
2460
2461 if (!$error) {
2462 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2463 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2464 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2465
2466 // We remove directory
2467 $ref = dol_sanitizeFileName($this->ref);
2468 if ($conf->fournisseur->commande->dir_output) {
2469 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2470 $file = $dir."/".$ref.".pdf";
2471 if (file_exists($file)) {
2472 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2473 $this->error = 'ErrorFailToDeleteFile';
2474 $this->errors[] = 'ErrorFailToDeleteFile';
2475 $error++;
2476 }
2477 }
2478 if (file_exists($dir)) {
2479 $res = @dol_delete_dir_recursive($dir);
2480 if (!$res) {
2481 $this->error = 'ErrorFailToDeleteDir';
2482 $this->errors[] = 'ErrorFailToDeleteDir';
2483 $error++;
2484 }
2485 }
2486 }
2487 }
2488
2489 if (!$error) {
2490 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2491 $this->db->commit();
2492 return 1;
2493 } else {
2494 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2495 $this->db->rollback();
2496 return -$error;
2497 }
2498 }
2499
2500
2509 public function getDispachedLines($status = -1)
2510 {
2511 $ret = array();
2512
2513 // List of already dispatched lines
2514 $sql = "SELECT p.ref, p.label,";
2515 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2516 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2517 $sql .= " FROM ".$this->db->prefix()."product as p,";
2518 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2519 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2520 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2521 $sql .= " AND cfd.fk_product = p.rowid";
2522 if ($status >= 0) {
2523 $sql .= " AND cfd.status = ".((int) $status);
2524 }
2525 $sql .= " ORDER BY cfd.rowid ASC";
2526
2527 $resql = $this->db->query($sql);
2528 if ($resql) {
2529 $num = $this->db->num_rows($resql);
2530 $i = 0;
2531
2532 while ($i < $num) {
2533 $objp = $this->db->fetch_object($resql);
2534 if ($objp) {
2535 $ret[] = array(
2536 'id' => $objp->dispatchedlineid,
2537 'productid' => $objp->fk_product,
2538 'warehouseid' => $objp->warehouse_id,
2539 'qty' => $objp->qty,
2540 'orderlineid' => $objp->fk_elementdet
2541 );
2542 }
2543
2544 $i++;
2545 }
2546 } else {
2547 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2548 }
2549
2550 return $ret;
2551 }
2552
2553 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2563 public function Livraison($user, $date, $type, $comment)
2564 {
2565 // phpcs:enable
2566 global $conf, $langs;
2567
2568 $result = 0;
2569 $error = 0;
2570
2571 dol_syslog(get_class($this)."::Livraison");
2572
2573 $usercanreceive = 0;
2574 if (!isModEnabled('reception')) {
2575 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2576 } else {
2577 $usercanreceive = $user->hasRight("reception", "creer");
2578 }
2579
2580 if ($usercanreceive) {
2581 // Define the new status
2582 if ($type == 'par') {
2584 } elseif ($type == 'tot') {
2586 } elseif ($type == 'nev') {
2588 } elseif ($type == 'can') {
2590 } else {
2591 $error++;
2592 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2593 return -2;
2594 }
2595
2596 // Some checks to accept the record
2597 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2598 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2599 if (!$error && ($type == 'tot')) {
2600 $dispatchedlinearray = $this->getDispachedLines(0);
2601 if (count($dispatchedlinearray) > 0) {
2602 $result = -1;
2603 $error++;
2604 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2605 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2606 }
2607 }
2608 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)
2609 $dispatcheddenied = $this->getDispachedLines(2);
2610 if (count($dispatchedlinearray) > 0) {
2611 $result = -1;
2612 $error++;
2613 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2614 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2615 }
2616 }
2617 }
2618
2619 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2620
2621 if (empty($error)) {
2622 $this->db->begin();
2623
2624 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2625 $sql .= " SET fk_statut = ".((int) $statut);
2626 $sql .= " WHERE rowid = ".((int) $this->id);
2627 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2628
2629 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2630 $resql = $this->db->query($sql);
2631 if ($resql) {
2632 $result = 1;
2633 $old_statut = $this->statut;
2634 $this->statut = $statut;
2635 $this->context['actionmsg2'] = $comment;
2636
2637 // Call trigger
2638 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2639 if ($result_trigger < 0) {
2640 $error++;
2641 }
2642 // End call triggers
2643
2644 if (empty($error)) {
2645 $this->db->commit();
2646 } else {
2647 $this->statut = $old_statut;
2648 $this->db->rollback();
2649 $this->error = $this->db->lasterror();
2650 $result = -1;
2651 }
2652 } else {
2653 $this->db->rollback();
2654 $this->error = $this->db->lasterror();
2655 $result = -1;
2656 }
2657 }
2658 } else {
2659 $this->error = $langs->trans('NotAuthorized');
2660 $this->errors[] = $langs->trans('NotAuthorized');
2661 dol_syslog(get_class($this)."::Livraison Not Authorized");
2662 $result = -3;
2663 }
2664 return $result;
2665 }
2666
2667 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2677 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2678 {
2679 // phpcs:enable
2680 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2681 }
2682
2691 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2692 {
2693 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2694 $error = 0;
2695
2696 $this->db->begin();
2697
2698 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2699 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2700 $sql .= " WHERE rowid = ".((int) $this->id);
2701
2702 dol_syslog(__METHOD__, LOG_DEBUG);
2703 $resql = $this->db->query($sql);
2704 if (!$resql) {
2705 $this->errors[] = $this->db->error();
2706 $error++;
2707 }
2708
2709 if (!$error) {
2710 $this->oldcopy = clone $this;
2711 $this->delivery_date = $delivery_date;
2712 }
2713
2714 if (!$notrigger && empty($error)) {
2715 // Call trigger
2716 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2717 if ($result < 0) {
2718 $error++;
2719 }
2720 // End call triggers
2721 }
2722
2723 if (!$error) {
2724 $this->db->commit();
2725 return 1;
2726 } else {
2727 foreach ($this->errors as $errmsg) {
2728 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2729 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2730 }
2731 $this->db->rollback();
2732 return -1 * $error;
2733 }
2734 } else {
2735 return -2;
2736 }
2737 }
2738
2739 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2748 public function set_id_projet($user, $id_projet, $notrigger = 0)
2749 {
2750 // phpcs:enable
2751 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2752 $error = 0;
2753
2754 $this->db->begin();
2755
2756 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2757 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2758 $sql .= " WHERE rowid = ".((int) $this->id);
2759
2760 dol_syslog(__METHOD__, LOG_DEBUG);
2761 $resql = $this->db->query($sql);
2762 if (!$resql) {
2763 $this->errors[] = $this->db->error();
2764 $error++;
2765 }
2766
2767 if (!$error) {
2768 $this->oldcopy = clone $this;
2769 $this->fk_projet = $id_projet;
2770 $this->fk_project = $id_projet;
2771 }
2772
2773 if (!$notrigger && empty($error)) {
2774 // Call trigger
2775 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2776 if ($result < 0) {
2777 $error++;
2778 }
2779 // End call triggers
2780 }
2781
2782 if (!$error) {
2783 $this->db->commit();
2784 return 1;
2785 } else {
2786 foreach ($this->errors as $errmsg) {
2787 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2788 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2789 }
2790 $this->db->rollback();
2791 return -1 * $error;
2792 }
2793 } else {
2794 return -2;
2795 }
2796 }
2797
2806 public function updateFromCommandeClient($user, $idc, $comclientid)
2807 {
2808 $comclient = new Commande($this->db);
2809 $comclient->fetch($comclientid);
2810
2811 $this->id = $idc;
2812
2813 $this->lines = array();
2814
2815 $num = count($comclient->lines);
2816 for ($i = 0; $i < $num; $i++) {
2817 $prod = new Product($this->db);
2818 $label = '';
2819 $ref = '';
2820 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2821 $label = $prod->label;
2822 $ref = $prod->ref;
2823 }
2824
2825 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2826 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2827 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2828 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2829 $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);
2830 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2831 if ($this->db->query($sql)) {
2832 $this->update_price(1);
2833 }
2834 }
2835
2836 return 1;
2837 }
2838
2846 public function setStatus($user, $status)
2847 {
2848 global $conf, $langs;
2849 $error = 0;
2850
2851 $this->db->begin();
2852
2853 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2854 $sql .= " SET fk_statut = ".$status;
2855 $sql .= " WHERE rowid = ".((int) $this->id);
2856
2857 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2858 $resql = $this->db->query($sql);
2859 if ($resql) {
2860 // Trigger names for each status
2861 $triggerName = array();
2862 $triggerName[0] = 'DRAFT';
2863 $triggerName[1] = 'VALIDATED';
2864 $triggerName[2] = 'APPROVED';
2865 $triggerName[3] = 'ORDERED'; // Ordered
2866 $triggerName[4] = 'RECEIVED_PARTIALLY';
2867 $triggerName[5] = 'RECEIVED_COMPLETELY';
2868 $triggerName[6] = 'CANCELED';
2869 $triggerName[7] = 'CANCELED';
2870 $triggerName[9] = 'REFUSED';
2871
2872 // Call trigger
2873 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2874 if ($result < 0) {
2875 $error++;
2876 }
2877 // End call triggers
2878 } else {
2879 $error++;
2880 $this->error = $this->db->lasterror();
2881 dol_syslog(get_class($this)."::setStatus ".$this->error);
2882 }
2883
2884 if (!$error) {
2885 $this->statut = $status;
2886 $this->db->commit();
2887 return 1;
2888 } else {
2889 $this->db->rollback();
2890 return -1;
2891 }
2892 }
2893
2917 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 = '')
2918 {
2919 global $mysoc, $conf, $langs;
2920 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2921 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2922
2923 $error = 0;
2924
2925 if ($this->statut == self::STATUS_DRAFT) {
2926 // Clean parameters
2927 if (empty($qty)) {
2928 $qty = 0;
2929 }
2930 if (empty($info_bits)) {
2931 $info_bits = 0;
2932 }
2933 if (empty($txtva)) {
2934 $txtva = 0;
2935 }
2936 if (empty($txlocaltax1)) {
2937 $txlocaltax1 = 0;
2938 }
2939 if (empty($txlocaltax2)) {
2940 $txlocaltax2 = 0;
2941 }
2942 if (empty($remise_percent)) {
2943 $remise_percent = 0;
2944 }
2945
2946 $remise_percent = (float) price2num($remise_percent);
2947 $qty = price2num($qty);
2948 if (!$qty) {
2949 $qty = 1;
2950 }
2951 $pu = price2num($pu);
2952 $pu_ht_devise = price2num($pu_ht_devise);
2953 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2954 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2955 }
2956 $txlocaltax1 = (float) price2num($txlocaltax1);
2957 $txlocaltax2 = (float) price2num($txlocaltax2);
2958
2959 // Check parameters
2960 if ($type < 0) {
2961 return -1;
2962 }
2963 if ($date_start && $date_end && $date_start > $date_end) {
2964 $langs->load("errors");
2965 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2966 return -1;
2967 }
2968
2969 $this->db->begin();
2970
2971 // Calcul du total TTC et de la TVA pour la ligne a partir de
2972 // qty, pu, remise_percent et txtva
2973 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2974 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2975
2976 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2977
2978 // Clean vat code
2979 $reg = array();
2980 $vat_src_code = '';
2981 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2982 $vat_src_code = $reg[1];
2983 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2984 }
2985
2986 $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);
2987 $total_ht = $tabprice[0];
2988 $total_tva = $tabprice[1];
2989 $total_ttc = $tabprice[2];
2990 $total_localtax1 = $tabprice[9];
2991 $total_localtax2 = $tabprice[10];
2992 $pu_ht = $tabprice[3];
2993 $pu_tva = $tabprice[4];
2994 $pu_ttc = $tabprice[5];
2995
2996 // MultiCurrency
2997 $multicurrency_total_ht = $tabprice[16];
2998 $multicurrency_total_tva = $tabprice[17];
2999 $multicurrency_total_ttc = $tabprice[18];
3000 $pu_ht_devise = $tabprice[19];
3001
3002 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3003 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3004
3005 //Fetch current line from the database and then clone the object and set it in $oldline property
3006 $this->line = new CommandeFournisseurLigne($this->db);
3007 $this->line->fetch($rowid);
3008
3009 $oldline = clone $this->line;
3010 $this->line->oldline = $oldline;
3011
3012 $this->line->context = $this->context;
3013
3014 $this->line->fk_commande = $this->id;
3015 //$this->line->label=$label;
3016 $this->line->desc = $desc;
3017
3018 // redefine quantity according to packaging
3019 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3020 if ($qty < $this->line->packaging) {
3021 $qty = $this->line->packaging;
3022 } else {
3023 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
3024 $coeff = intval($qty / $this->line->packaging) + 1;
3025 $qty = $this->line->packaging * $coeff;
3026 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3027 }
3028 }
3029 }
3030
3031 $this->line->qty = $qty;
3032 $this->line->ref_supplier = $ref_supplier;
3033
3034 $this->line->vat_src_code = $vat_src_code;
3035 $this->line->tva_tx = $txtva;
3036 $this->line->localtax1_tx = $txlocaltax1;
3037 $this->line->localtax2_tx = $txlocaltax2;
3038 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3039 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3040 $this->line->remise_percent = $remise_percent;
3041 $this->line->subprice = $pu_ht;
3042 $this->line->info_bits = $info_bits;
3043 $this->line->total_ht = $total_ht;
3044 $this->line->total_tva = $total_tva;
3045 $this->line->total_localtax1 = $total_localtax1;
3046 $this->line->total_localtax2 = $total_localtax2;
3047 $this->line->total_ttc = $total_ttc;
3048 $this->line->product_type = $type;
3049 $this->line->special_code = $oldline->special_code;
3050 $this->line->rang = $oldline->rang;
3051 $this->line->origin = $this->origin;
3052 $this->line->fk_unit = $fk_unit;
3053
3054 $this->line->date_start = $date_start;
3055 $this->line->date_end = $date_end;
3056
3057 // Multicurrency
3058 $this->line->fk_multicurrency = $this->fk_multicurrency;
3059 $this->line->multicurrency_code = $this->multicurrency_code;
3060 $this->line->multicurrency_subprice = $pu_ht_devise;
3061 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3062 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3063 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3064
3065 $this->line->subprice = $pu_ht;
3066 $this->line->price = $this->line->subprice;
3067
3068 $this->line->remise_percent = $remise_percent;
3069
3070 if (is_array($array_options) && count($array_options) > 0) {
3071 // We replace values in this->line->array_options only for entries defined into $array_options
3072 foreach ($array_options as $key => $value) {
3073 $this->line->array_options[$key] = $array_options[$key];
3074 }
3075 }
3076
3077 $result = $this->line->update($notrigger);
3078
3079
3080 // Mise a jour info denormalisees au niveau facture
3081 if ($result >= 0) {
3082 $this->update_price('1', 'auto');
3083 $this->db->commit();
3084 return $result;
3085 } else {
3086 $this->errors[] = $this->line->error;
3087 $this->errors = array_merge($this->errors, $this->line->errors);
3088 $this->error = $this->db->lasterror();
3089 $this->db->rollback();
3090 return -1;
3091 }
3092 } else {
3093 $this->error = "Order status makes operation forbidden";
3094 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3095 return -2;
3096 }
3097 }
3098
3099
3107 public function initAsSpecimen()
3108 {
3109 global $user, $langs, $conf;
3110
3111 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3112
3113 dol_syslog(get_class($this)."::initAsSpecimen");
3114
3115 $now = dol_now();
3116
3117 // Find first product
3118 $prodid = 0;
3119 $product = new ProductFournisseur($this->db);
3120 $sql = "SELECT rowid";
3121 $sql .= " FROM ".$this->db->prefix()."product";
3122 $sql .= " WHERE entity IN (".getEntity('product').")";
3123 $sql .= $this->db->order("rowid", "ASC");
3124 $sql .= $this->db->plimit(1);
3125 $resql = $this->db->query($sql);
3126 if ($resql && $this->db->num_rows($resql)) {
3127 $obj = $this->db->fetch_object($resql);
3128 $prodid = $obj->rowid;
3129 }
3130
3131 // Initialise parameters
3132 $this->id = 0;
3133 $this->ref = 'SPECIMEN';
3134 $this->specimen = 1;
3135 $this->socid = 1;
3136 $this->date = $now;
3137 $this->date_commande = $now;
3138 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3139 $this->cond_reglement_code = 'RECEP';
3140 $this->mode_reglement_code = 'CHQ';
3141
3142 $this->note_public = 'This is a comment (public)';
3143 $this->note_private = 'This is a comment (private)';
3144
3145 $this->multicurrency_tx = 1;
3146 $this->multicurrency_code = $conf->currency;
3147
3148 $this->statut = 0;
3149
3150 // Lines
3151 $nbp = 5;
3152 $xnbp = 0;
3153 while ($xnbp < $nbp) {
3154 $line = new CommandeFournisseurLigne($this->db);
3155 $line->desc = $langs->trans("Description")." ".$xnbp;
3156 $line->qty = 1;
3157 $line->subprice = 100;
3158 $line->tva_tx = 19.6;
3159 $line->localtax1_tx = 0;
3160 $line->localtax2_tx = 0;
3161 if ($xnbp == 2) {
3162 $line->total_ht = 50;
3163 $line->total_ttc = 59.8;
3164 $line->total_tva = 9.8;
3165 $line->remise_percent = 50;
3166 } else {
3167 $line->total_ht = 100;
3168 $line->total_ttc = 119.6;
3169 $line->total_tva = 19.6;
3170 $line->remise_percent = 00;
3171 }
3172 $line->fk_product = $prodid;
3173
3174 $this->lines[$xnbp] = $line;
3175
3176 $this->total_ht += $line->total_ht;
3177 $this->total_tva += $line->total_tva;
3178 $this->total_ttc += $line->total_ttc;
3179
3180 $xnbp++;
3181 }
3182
3183 return 1;
3184 }
3185
3192 public function info($id)
3193 {
3194 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3195 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3196 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3197 $sql .= ' WHERE c.rowid = '.((int) $id);
3198
3199 $result = $this->db->query($sql);
3200 if ($result) {
3201 if ($this->db->num_rows($result)) {
3202 $obj = $this->db->fetch_object($result);
3203
3204 $this->id = $obj->rowid;
3205
3206 $this->user_creation_id = $obj->fk_user_author;
3207 $this->user_validation_id = $obj->fk_user_valid;
3208 $this->user_modification_id = $obj->fk_user_modif;
3209 $this->user_approve_id = $obj->fk_user_approve;
3210 $this->user_approve_id2 = $obj->fk_user_approve2;
3211
3212 $this->date_creation = $this->db->jdate($obj->datec);
3213 $this->date_modification = $this->db->jdate($obj->datem);
3214 $this->date_approve = $this->db->jdate($obj->datea);
3215 $this->date_approve2 = $this->db->jdate($obj->datea2);
3216 $this->date_validation = $this->db->jdate($obj->date_validation);
3217 }
3218 $this->db->free($result);
3219 } else {
3220 dol_print_error($this->db);
3221 }
3222 }
3223
3229 public function loadStateBoard()
3230 {
3231 global $conf, $user;
3232
3233 $this->nb = array();
3234 $clause = "WHERE";
3235
3236 $sql = "SELECT count(co.rowid) as nb";
3237 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3238 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3239 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3240 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3241 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3242 $clause = "AND";
3243 }
3244 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3245
3246 $resql = $this->db->query($sql);
3247 if ($resql) {
3248 while ($obj = $this->db->fetch_object($resql)) {
3249 $this->nb["supplier_orders"] = $obj->nb;
3250 }
3251 $this->db->free($resql);
3252 return 1;
3253 } else {
3254 dol_print_error($this->db);
3255 $this->error = $this->db->error();
3256 return -1;
3257 }
3258 }
3259
3260 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3268 public function load_board($user, $mode = 'opened')
3269 {
3270 // phpcs:enable
3271 global $conf, $langs;
3272
3273 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3274 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3275 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3276 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3277 }
3278 $sql .= " WHERE c.entity = ".$conf->entity;
3279 if ($mode === 'awaiting') {
3280 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3281 } else {
3282 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3283 }
3284 if ($user->socid) {
3285 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3286 }
3287
3288 $resql = $this->db->query($sql);
3289 if ($resql) {
3290 $commandestatic = new CommandeFournisseur($this->db);
3291
3292 $response = new WorkboardResponse();
3293 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3294 $response->label = $langs->trans("SuppliersOrdersToProcess");
3295 $response->labelShort = $langs->trans("Opened");
3296 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3297 $response->img = img_object('', "order");
3298
3299 if ($mode === 'awaiting') {
3300 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3301 $response->labelShort = $langs->trans("AwaitingReception");
3302 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3303 }
3304
3305 while ($obj = $this->db->fetch_object($resql)) {
3306 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3307 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3308 $commandestatic->statut = $obj->fk_statut;
3309
3310 $response->nbtodo++;
3311 $response->total += $obj->total_ht;
3312
3313 if ($commandestatic->hasDelay()) {
3314 $response->nbtodolate++;
3315 }
3316 }
3317
3318 return $response;
3319 } else {
3320 $this->error = $this->db->error();
3321 return -1;
3322 }
3323 }
3324
3331 public function getInputMethod()
3332 {
3333 global $langs;
3334
3335 if ($this->methode_commande_id > 0) {
3336 $sql = "SELECT rowid, code, libelle as label";
3337 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3338 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3339
3340 $resql = $this->db->query($sql);
3341 if ($resql) {
3342 if ($this->db->num_rows($resql)) {
3343 $obj = $this->db->fetch_object($resql);
3344
3345 $string = $langs->trans($obj->code);
3346 if ($string == $obj->code) {
3347 $string = $obj->label != '-' ? $obj->label : '';
3348 }
3349 return $string;
3350 }
3351 } else {
3352 dol_print_error($this->db);
3353 }
3354 }
3355
3356 return '';
3357 }
3358
3370 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3371 {
3372 global $conf, $langs;
3373
3374 if (!dol_strlen($modele)) {
3375 $modele = ''; // No doc template/generation by default
3376
3377 if (!empty($this->model_pdf)) {
3378 $modele = $this->model_pdf;
3379 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3380 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3381 }
3382 }
3383
3384 if (empty($modele)) {
3385 return 0;
3386 } else {
3387 $langs->load("suppliers");
3388 $outputlangs->load("products");
3389
3390 $modelpath = "core/modules/supplier_order/doc/";
3391 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3392 return $result;
3393 }
3394 }
3395
3402 public function getMaxDeliveryTimeDay($langs)
3403 {
3404 if (empty($this->lines)) {
3405 return '';
3406 }
3407
3408 $tmpproductfourn = new ProductFournisseur($this->db);
3409
3410 $nb = 0;
3411 foreach ($this->lines as $line) {
3412 if ($line->fk_product > 0) {
3413 // Load delivery_time_days, return id into product_fournisseur_price
3414 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3415 if ($idp > 0) {
3416 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3417 if ($tmpproductfourn->delivery_time_days > $nb) {
3418 $nb = $tmpproductfourn->delivery_time_days;
3419 }
3420 }
3421 }
3422 }
3423
3424 if ($nb === 0) {
3425 return '';
3426 } else {
3427 return $nb.' '.$langs->trans('days');
3428 }
3429 }
3430
3436 public function getRights()
3437 {
3438 global $user;
3439
3440 return $user->hasRight("fournisseur", "commande");
3441 }
3442
3443
3452 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3453 {
3454 $tables = array(
3455 'commande_fournisseur'
3456 );
3457
3458 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3459 }
3460
3469 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3470 {
3471 $tables = array(
3472 'commande_fournisseurdet'
3473 );
3474
3475 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3476 }
3477
3485 public function hasDelay()
3486 {
3487 global $conf;
3488
3489 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3490 $now = dol_now();
3491 if (!empty($this->delivery_date)) {
3492 $date_to_test = $this->delivery_date;
3493 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3494 } else {
3495 //$date_to_test = $this->date_commande;
3496 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3497 return false;
3498 }
3499 } else {
3500 $now = dol_now();
3501 $date_to_test = $this->date_commande;
3502
3503 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3504 }
3505 }
3506
3514 public function showDelay()
3515 {
3516 global $conf, $langs;
3517
3518 $langs->load('orders');
3519
3520 $text = '';
3521
3522 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3523 if (!empty($this->delivery_date)) {
3524 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3525 } else {
3526 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3527 }
3528 } else {
3529 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3530 }
3531 if ($text) {
3532 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3533 }
3534
3535 return $text;
3536 }
3537
3538
3547 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3548 {
3549 if (isModEnabled("supplier_order")) {
3550 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3551
3552 $qtydelivered = array();
3553 $qtywished = array();
3554
3555 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3556
3557 $filter = array('t.fk_element' => $this->id);
3558 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3559 $filter['t.status'] = 1; // Restrict to lines with status validated
3560 }
3561
3562 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3563 if ($ret < 0) {
3564 $this->error = $supplierorderdispatch->error;
3565 $this->errors = $supplierorderdispatch->errors;
3566 return $ret;
3567 } else {
3568 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3569 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3570 $date_liv = dol_now();
3571
3572 // Build array with quantity deliverd by product
3573 foreach ($supplierorderdispatch->lines as $line) {
3574 if (array_key_exists($line->fk_product, $qtydelivered)) {
3575 $qtydelivered[$line->fk_product] += $line->qty;
3576 } else {
3577 $qtydelivered[$line->fk_product] = $line->qty;
3578 }
3579 }
3580 foreach ($this->lines as $line) {
3581 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3582 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3583 continue;
3584 }
3585 if (array_key_exists($line->fk_product, $qtywished)) {
3586 $qtywished[$line->fk_product] += $line->qty;
3587 } else {
3588 $qtywished[$line->fk_product] = $line->qty;
3589 }
3590 }
3591
3592 //Compare array
3593 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3594 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3595 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3596 //var_dump(array_keys($qtydelivered));
3597 //var_dump(array_keys($qtywished));
3598 //var_dump($diff_array);
3599 //var_dump($keysinwishednotindelivered);
3600 //var_dump($keysindeliverednotinwished);
3601 //exit;
3602
3603 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3604 if ($closeopenorder) {
3605 //$ret=$this->setStatus($user,5);
3606 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3607 if ($ret < 0) {
3608 return -1;
3609 }
3610 return 5;
3611 } else {
3612 //Diff => received partially
3613 //$ret=$this->setStatus($user,4);
3614 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3615 if ($ret < 0) {
3616 return -1;
3617 }
3618 return 4;
3619 }
3620 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3621 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3622
3623 $close = 0;
3624
3625 if (count($diff_array) > 0) {
3626 //there are some difference between the two arrays
3627
3628 //scan the array of results
3629 foreach ($diff_array as $key => $value) {
3630 //if the quantity delivered is greater or equal to wish quantity
3631 if ($qtydelivered[$key] >= $qtywished[$key]) {
3632 $close++;
3633 }
3634 }
3635 }
3636
3637
3638 if ($close == count($diff_array)) {
3639 //all the products are received equal or more than the wished quantity
3640 if ($closeopenorder) {
3641 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3642 if ($ret < 0) {
3643 return -1;
3644 }
3645 return 5;
3646 } else {
3647 //Diff => received partially
3648 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3649 if ($ret < 0) {
3650 return -1;
3651 }
3652 return 4;
3653 }
3654 } else {
3655 //all the products are not received
3656 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3657 if ($ret < 0) {
3658 return -1;
3659 }
3660 return 4;
3661 }
3662 } else {
3663 //Diff => received partially
3664 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3665 if ($ret < 0) {
3666 return -1;
3667 }
3668 return 4;
3669 }
3670 }
3671 return 1;
3672 }
3673 }
3674 return 0;
3675 }
3676
3684 public function loadReceptions($filtre_statut = -1)
3685 {
3686 $this->receptions = array();
3687
3688 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3689
3690 $sql = 'SELECT cd.rowid, cd.fk_product,';
3691 $sql .= ' sum(cfd.qty) as qty';
3692 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3693 if ($filtre_statut >= 0) {
3694 $sql .= ' '.$this->db->prefix().'reception as e,';
3695 }
3696 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3697 $sql .= ' WHERE';
3698 if ($filtre_statut >= 0) {
3699 $sql .= ' cfd.fk_reception = e.rowid AND';
3700 }
3701 $sql .= ' cfd.fk_elementdet = cd.rowid';
3702 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3703 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3704 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3705 }
3706 if ($filtre_statut >= 0) {
3707 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3708 }
3709 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3710
3711 $resql = $this->db->query($sql);
3712 if ($resql) {
3713 $num = $this->db->num_rows($resql);
3714 $i = 0;
3715 while ($i < $num) {
3716 $obj = $this->db->fetch_object($resql);
3717 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3718 $i++;
3719 }
3720 $this->db->free($resql);
3721
3722 return $num;
3723 } else {
3724 $this->error = $this->db->lasterror();
3725 return -1;
3726 }
3727 }
3728
3736 public function getKanbanView($option = '', $arraydata = null)
3737 {
3738 global $langs;
3739
3740 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3741
3742 $return = '<div class="box-flex-item box-flex-grow-zero">';
3743 $return .= '<div class="info-box info-box-sm">';
3744 $return .= '<span class="info-box-icon bg-infobox-action">';
3745 $return .= img_picto('', $this->picto);
3746 $return .= '</span>';
3747 $return .= '<div class="info-box-content">';
3748 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3749 if ($selected >= 0) {
3750 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3751 }
3752 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3753 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3754 }
3755 if (property_exists($this, 'billed')) {
3756 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3757 }
3758 if (method_exists($this, 'getLibStatut')) {
3759 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3760 }
3761 $return .= '</div>';
3762 $return .= '</div>';
3763 $return .= '</div>';
3764 return $return;
3765 }
3766}
3767
3768
3769
3774{
3778 public $element = 'commande_fournisseurdet';
3779
3783 public $table_element = 'commande_fournisseurdet';
3784
3788 public $parent_element = 'commande_fournisseur';
3789
3793 public $fk_parent_attribute = 'fk_commande_fournisseur';
3794
3795 public $oldline;
3796
3801 public $fk_commande;
3802
3803 // From llx_commande_fournisseurdet
3807 public $fk_parent_line;
3808
3812 public $fk_facture;
3813
3814 public $rang = 0;
3815
3819 public $special_code = 0;
3820
3825 public $pu_ht;
3826
3827 public $date_start;
3828 public $date_end;
3829 public $fk_fournprice;
3830 public $packaging;
3831 public $pa_ht;
3832
3833 // From llx_product_fournisseur_price
3834
3839 public $ref_supplier;
3840
3846 public $ref_fourn;
3847
3848 public $remise;
3849
3850
3856 public function __construct($db)
3857 {
3858 $this->db = $db;
3859 }
3860
3867 public function fetch($rowid)
3868 {
3869 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3870 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3871 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3872 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3873 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3874 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3875 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3876 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3877 $sql .= ' c.fk_soc as socid';
3878 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
3879 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';
3880 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3881
3882 $result = $this->db->query($sql);
3883 if ($result) {
3884 $objp = $this->db->fetch_object($result);
3885
3886 if (!empty($objp)) {
3887 $this->rowid = $objp->rowid;
3888 $this->id = $objp->rowid;
3889 $this->fk_commande = $objp->fk_commande;
3890 $this->desc = $objp->description;
3891 $this->qty = $objp->qty;
3892 $this->ref_fourn = $objp->ref_supplier;
3893 $this->ref_supplier = $objp->ref_supplier;
3894 $this->subprice = $objp->subprice;
3895 $this->tva_tx = $objp->tva_tx;
3896 $this->localtax1_tx = $objp->localtax1_tx;
3897 $this->localtax2_tx = $objp->localtax2_tx;
3898 $this->localtax1_type = $objp->localtax1_type;
3899 $this->localtax2_type = $objp->localtax2_type;
3900 $this->remise = $objp->remise;
3901 $this->remise_percent = $objp->remise_percent;
3902 $this->fk_product = $objp->fk_product;
3903 $this->info_bits = $objp->info_bits;
3904 $this->total_ht = $objp->total_ht;
3905 $this->total_tva = $objp->total_tva;
3906 $this->total_localtax1 = $objp->total_localtax1;
3907 $this->total_localtax2 = $objp->total_localtax2;
3908 $this->total_ttc = $objp->total_ttc;
3909 $this->product_type = $objp->product_type;
3910 $this->special_code = $objp->special_code;
3911
3912 $this->ref = $objp->product_ref;
3913
3914 $this->product_ref = $objp->product_ref;
3915 $this->product_label = $objp->product_label;
3916 $this->product_desc = $objp->product_desc;
3917
3918 if (getDolGlobalInt('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3919 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3920 // Move this into another method and call it when required.
3921
3922 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3923 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
3924 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3925 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3926 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3927 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3928 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3929 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3930 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3931 $sqlsearchpackage .= " LIMIT 1";
3932
3933 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3934 if ($resqlsearchpackage) {
3935 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3936 if ($objsearchpackage) {
3937 $this->fk_fournprice = $objsearchpackage->rowid;
3938 $this->packaging = $objsearchpackage->packaging;
3939 }
3940 } else {
3941 $this->error = $this->db->lasterror();
3942 return -1;
3943 }
3944 }
3945
3946 $this->date_start = $this->db->jdate($objp->date_start);
3947 $this->date_end = $this->db->jdate($objp->date_end);
3948 $this->fk_unit = $objp->fk_unit;
3949
3950 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3951 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3952 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3953 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3954
3955 $this->fetch_optionals();
3956
3957 $this->db->free($result);
3958 return 1;
3959 } else {
3960 $this->error = 'Supplier order line with id='.$rowid.' not found';
3961 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3962 return 0;
3963 }
3964 } else {
3965 dol_print_error($this->db);
3966 return -1;
3967 }
3968 }
3969
3976 public function insert($notrigger = 0)
3977 {
3978 global $conf, $user;
3979
3980 $error = 0;
3981
3982 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3983
3984 // Clean parameters
3985 if (empty($this->tva_tx)) {
3986 $this->tva_tx = 0;
3987 }
3988 if (empty($this->localtax1_tx)) {
3989 $this->localtax1_tx = 0;
3990 }
3991 if (empty($this->localtax2_tx)) {
3992 $this->localtax2_tx = 0;
3993 }
3994 if (empty($this->localtax1_type)) {
3995 $this->localtax1_type = '0';
3996 }
3997 if (empty($this->localtax2_type)) {
3998 $this->localtax2_type = '0';
3999 }
4000 if (empty($this->total_localtax1)) {
4001 $this->total_localtax1 = 0;
4002 }
4003 if (empty($this->total_localtax2)) {
4004 $this->total_localtax2 = 0;
4005 }
4006 if (empty($this->rang)) {
4007 $this->rang = 0;
4008 }
4009 if (empty($this->remise_percent)) {
4010 $this->remise_percent = 0;
4011 }
4012 if (empty($this->info_bits)) {
4013 $this->info_bits = 0;
4014 }
4015 if (empty($this->special_code)) {
4016 $this->special_code = 0;
4017 }
4018 if (empty($this->fk_parent_line)) {
4019 $this->fk_parent_line = 0;
4020 }
4021 if (empty($this->pa_ht)) {
4022 $this->pa_ht = 0;
4023 }
4024
4025 // Multicurrency
4026 if (!empty($this->multicurrency_code)) {
4027 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
4028 }
4029 if (empty($this->fk_multicurrency)) {
4030 $this->multicurrency_code = $conf->currency;
4031 $this->fk_multicurrency = 0;
4032 $this->multicurrency_tx = 1;
4033 }
4034
4035 // Check parameters
4036 if ($this->product_type < 0) {
4037 return -1;
4038 }
4039
4040 $this->db->begin();
4041
4042 // Insertion dans base de la ligne
4043 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element;
4044 $sql .= " (fk_commande, label, description, date_start, date_end,";
4045 $sql .= " fk_product, product_type, special_code, rang,";
4046 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
4047 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
4048 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,";
4049 $sql .= " fk_parent_line)";
4050 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
4051 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4052 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4053 if ($this->fk_product) {
4054 $sql .= $this->fk_product.",";
4055 } else {
4056 $sql .= "null,";
4057 }
4058 $sql .= "'".$this->db->escape($this->product_type)."',";
4059 $sql .= (int) $this->special_code . ",";
4060 $sql .= "'".$this->db->escape($this->rang)."',";
4061 $sql .= "'".$this->db->escape($this->qty)."', ";
4062 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4063 $sql .= " ".price2num($this->tva_tx).", ";
4064 $sql .= " ".price2num($this->localtax1_tx).",";
4065 $sql .= " ".price2num($this->localtax2_tx).",";
4066 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4067 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4068 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
4069 $sql .= " ".price2num($this->total_ht).",";
4070 $sql .= " ".price2num($this->total_tva).",";
4071 $sql .= " ".price2num($this->total_localtax1).",";
4072 $sql .= " ".price2num($this->total_localtax2).",";
4073 $sql .= " ".price2num($this->total_ttc).",";
4074 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
4075 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
4076 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4077 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
4078 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
4079 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
4080 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
4081 $sql .= ", ".((!empty($this->fk_parent_line) && $this->fk_parent_line > 0) ? $this->fk_parent_line : 'null');
4082 $sql .= ")";
4083
4084 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4085 $resql = $this->db->query($sql);
4086 if ($resql) {
4087 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
4088 $this->rowid = $this->id;
4089
4090 if (!$error) {
4091 $result = $this->insertExtraFields();
4092 if ($result < 0) {
4093 $error++;
4094 }
4095 }
4096
4097 if (!$error && !$notrigger) {
4098 // Call trigger
4099 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
4100 if ($result < 0) {
4101 $error++;
4102 }
4103 // End call triggers
4104 }
4105
4106 if (!$error) {
4107 $this->db->commit();
4108 return 1;
4109 }
4110
4111 foreach ($this->errors as $errmsg) {
4112 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4113 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4114 }
4115 $this->db->rollback();
4116 return -1 * $error;
4117 } else {
4118 $this->errors[] = $this->db->error();
4119 $this->db->rollback();
4120 return -2;
4121 }
4122 }
4123
4130 public function update($notrigger = 0)
4131 {
4132 global $user;
4133
4134 $error = 0;
4135
4136 $this->db->begin();
4137
4138 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
4139 $sql .= " description='".$this->db->escape($this->desc)."'";
4140 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4141 $sql .= ", subprice='".price2num($this->subprice)."'";
4142 //$sql.= ",remise='".price2num($remise)."'";
4143 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4144
4145 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4146 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4147 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4148 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4149 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4150 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4151 $sql .= ", qty='".price2num($this->qty)."'";
4152 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4153 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4154 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4155 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4156 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4157 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4158 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4159 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4160 $sql .= ", product_type=".$this->product_type;
4161 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4162 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4163
4164 // Multicurrency
4165 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4166 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4167 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4168 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4169
4170 $sql .= " WHERE rowid = ".((int) $this->id);
4171
4172 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4173 $resql = $this->db->query($sql);
4174 if ($resql) {
4175 if (!$error) {
4176 $result = $this->insertExtraFields();
4177 if ($result < 0) {
4178 $error++;
4179 }
4180 }
4181
4182 if (!$error && !$notrigger) {
4183 // Call trigger
4184 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4185 if ($result < 0) {
4186 $this->db->rollback();
4187 return -1;
4188 }
4189 // End call triggers
4190 }
4191
4192 if (!$error) {
4193 $this->db->commit();
4194 return 1;
4195 } else {
4196 $this->db->rollback();
4197 return -1;
4198 }
4199 } else {
4200 $this->error = $this->db->lasterror();
4201 $this->db->rollback();
4202 return -1;
4203 }
4204 }
4205
4213 public function delete($user, $notrigger = 0)
4214 {
4215 if (empty($user)) {
4216 global $user;
4217 }
4218
4219 $error = 0;
4220
4221 $this->db->begin();
4222
4223 // extrafields
4224 $result = $this->deleteExtraFields();
4225 if ($result < 0) {
4226 $this->db->rollback();
4227 return -1;
4228 }
4229
4230 $sql1 = 'UPDATE '.$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4231 $resql = $this->db->query($sql1);
4232 if (!$resql) {
4233 $this->db->rollback();
4234 return -1;
4235 }
4236
4237 $sql2 = 'DELETE FROM '.$this->db->prefix()."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4238
4239 dol_syslog(__METHOD__, LOG_DEBUG);
4240 $resql = $this->db->query($sql2);
4241 if ($resql) {
4242 if (!$notrigger) {
4243 // Call trigger
4244 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4245 if ($result < 0) {
4246 $error++;
4247 }
4248 // End call triggers
4249 }
4250
4251 if (!$error) {
4252 $this->db->commit();
4253 return 1;
4254 }
4255
4256 foreach ($this->errors as $errmsg) {
4257 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4258 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4259 }
4260 $this->db->rollback();
4261 return -1 * $error;
4262 } else {
4263 $this->error = $this->db->lasterror();
4264 return -1;
4265 }
4266 }
4267}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:637
$object ref
Definition info.php:79
Class to manage table ReceptionLineBatch.
Class to manage predefined suppliers products.
const STATUS_CANCELED_AFTER_ORDER
Order canceled/never received.
const STATUS_RECEIVED_PARTIALLY
Received partially.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
updateFromCommandeClient($user, $idc, $comclientid)
Update a supplier order from a sales order.
getNomUrl($withpicto=0, $option='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clicable 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 clicable link of object (with eventually picto)
info($id)
Charge les information d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
getNextNumRef($soc)
Returns the next order reference not used, based on the numbering model defined within COMMANDE_SUPPL...
fetch_lines($only_product=0)
Load array lines.
getInputMethod()
Returns the translated input method of object (defined if $this->methode_commande_id > 0).
const STATUS_VALIDATED
Validated status.
const STATUS_RECEIVED_COMPLETELY
Received completely.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $fk_prod_fourn_price=0, $ref_supplier='', $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $type=0, $info_bits=0, $notrigger=0, $date_start=null, $date_end=null, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
loadStateBoard()
Load the indicators this->nb for the state board.
calcAndSetStatusDispatch(User $user, $closeopenorder=1, $comment='')
Calc status regarding to dispatched stock.
set_id_projet($user, $id_projet, $notrigger=0)
Set the id projet.
showDelay()
Show the customer delayed info.
getTooltipContentArray($params)
getTooltipContentArray
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.
update($notrigger=0)
Update the line object into db.
insert($notrigger=0)
Insert line into database.
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.
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.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0)
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...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:2037
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:2037