dolibarr 20.0.4
fournisseur.commande.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
8 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
13 * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2021 Josep Lluís Amador <joseplluis@lliuretic.cat>
15 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16 * Copyright (C) 2024 Solution Libre SAS <contact@solution-libre.fr>
17 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
18 *
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 .= ', i.libelle as label_incoterms';
483 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
484 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
485 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
486 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
487 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
488
489 if (empty($id)) {
490 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
491 } else {
492 $sql .= " WHERE c.rowid=".((int) $id);
493 }
494
495 if ($ref) {
496 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
497 }
498
499 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
500 $resql = $this->db->query($sql);
501 if ($resql) {
502 $obj = $this->db->fetch_object($resql);
503 if (!$obj) {
504 $this->error = 'Bill with id '.$id.' not found';
505 dol_syslog(get_class($this).'::fetch '.$this->error);
506 return 0;
507 }
508
509 $this->id = $obj->rowid;
510 $this->entity = $obj->entity;
511
512 $this->ref = $obj->ref;
513 $this->ref_supplier = $obj->ref_supplier;
514 $this->socid = $obj->fk_soc;
515 $this->fourn_id = $obj->fk_soc;
516 $this->statut = $obj->status; // deprecated
517 $this->status = $obj->status;
518 $this->billed = $obj->billed;
519 $this->user_author_id = $obj->user_author_id;
520 $this->user_validation_id = $obj->user_validation_id;
521 $this->user_approve_id = $obj->user_approve_id;
522 $this->user_approve_id2 = $obj->user_approve_id2;
523 $this->total_ht = $obj->total_ht;
524 $this->total_tva = $obj->total_tva;
525 $this->total_localtax1 = $obj->localtax1;
526 $this->total_localtax2 = $obj->localtax2;
527 $this->total_ttc = $obj->total_ttc;
528 $this->date_creation = $this->db->jdate($obj->date_creation);
529 $this->date_valid = $this->db->jdate($obj->date_valid);
530 $this->date_approve = $this->db->jdate($obj->date_approve);
531 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
532 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
533 if (isset($obj->date_commande)) {
534 $this->date = $this->date_commande;
535 } else {
536 $this->date = $this->date_creation;
537 }
538 $this->delivery_date = $this->db->jdate($obj->delivery_date);
539 $this->remise_percent = $obj->remise_percent;
540 $this->methode_commande_id = $obj->fk_input_method;
541 $this->methode_commande = $obj->methode_commande;
542
543 $this->source = $obj->source;
544 $this->fk_project = $obj->fk_project;
545 $this->cond_reglement_id = $obj->fk_cond_reglement;
546 $this->cond_reglement_code = $obj->cond_reglement_code;
547 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
548 $this->cond_reglement_label = $obj->cond_reglement_label;
549 $this->cond_reglement_doc = $obj->cond_reglement_doc;
550 $this->fk_account = $obj->fk_account;
551 $this->mode_reglement_id = $obj->fk_mode_reglement;
552 $this->mode_reglement_code = $obj->mode_reglement_code;
553 $this->mode_reglement = $obj->mode_reglement_libelle;
554 $this->note = $obj->note_private; // deprecated
555 $this->note_private = $obj->note_private;
556 $this->note_public = $obj->note_public;
557 $this->model_pdf = $obj->model_pdf;
558
559 //Incoterms
560 $this->fk_incoterms = $obj->fk_incoterms;
561 $this->location_incoterms = $obj->location_incoterms;
562 $this->label_incoterms = $obj->label_incoterms;
563
564 // Multicurrency
565 $this->fk_multicurrency = $obj->fk_multicurrency;
566 $this->multicurrency_code = $obj->multicurrency_code;
567 $this->multicurrency_tx = $obj->multicurrency_tx;
568 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
569 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
570 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
571
572 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
573
574 $this->db->free($resql);
575
576 // Retrieve all extrafield
577 // fetch optionals attributes and labels
578 $this->fetch_optionals();
579
580 // Lines
581 $result = $this->fetch_lines();
582
583 if ($result < 0) {
584 return -1;
585 } else {
586 return 1;
587 }
588 } else {
589 $this->error = $this->db->error()." sql=".$sql;
590 return -1;
591 }
592 }
593
594 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
601 public function fetch_lines($only_product = 0)
602 {
603 // phpcs:enable
604
605 $this->lines = array();
606
607 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
608 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
609 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
610 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
611 $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,";
612 $sql .= " l.fk_unit,";
613 $sql .= " l.date_start, l.date_end,";
614 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
615 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
616 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
617 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
618 if ($only_product) {
619 $sql .= ' AND p.fk_product_type = 0';
620 }
621 $sql .= " ORDER BY l.rang, l.rowid";
622 //print $sql;
623
624 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
625
626 $result = $this->db->query($sql);
627 if ($result) {
628 $num = $this->db->num_rows($result);
629 $i = 0;
630
631 while ($i < $num) {
632 $objp = $this->db->fetch_object($result);
633
634 $line = new CommandeFournisseurLigne($this->db);
635
636 $line->id = $objp->rowid;
637 $line->fk_commande = $objp->fk_commande;
638 $line->desc = $objp->description;
639 $line->description = $objp->description;
640 $line->qty = $objp->qty;
641 $line->tva_tx = $objp->tva_tx;
642 $line->localtax1_tx = $objp->localtax1_tx;
643 $line->localtax2_tx = $objp->localtax2_tx;
644 $line->localtax1_type = $objp->localtax1_type;
645 $line->localtax2_type = $objp->localtax2_type;
646 $line->subprice = $objp->subprice;
647 $line->pu_ht = $objp->subprice;
648 $line->remise_percent = $objp->remise_percent;
649
650 $line->vat_src_code = $objp->vat_src_code;
651 $line->total_ht = $objp->total_ht;
652 $line->total_tva = $objp->total_tva;
653 $line->total_localtax1 = $objp->total_localtax1;
654 $line->total_localtax2 = $objp->total_localtax2;
655 $line->total_ttc = $objp->total_ttc;
656 $line->product_type = $objp->product_type;
657
658 $line->fk_product = $objp->fk_product;
659
660 $line->libelle = $objp->product_label; // deprecated
661 $line->product_label = $objp->product_label;
662 $line->product_desc = $objp->product_desc;
663 $line->product_tobatch = $objp->product_tobatch;
664 $line->product_barcode = $objp->product_barcode;
665
666 $line->ref = $objp->product_ref; // Ref of product
667 $line->product_ref = $objp->product_ref; // Ref of product
668 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
669 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
670
671 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
672 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
673 // Move this into another method and call it when required.
674
675 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
676 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
677 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
678 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
679 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
680 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
681 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
682 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
683 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
684 $sqlsearchpackage .= " LIMIT 1";
685
686 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
687 if ($resqlsearchpackage) {
688 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
689 if ($objsearchpackage) {
690 $line->fk_fournprice = $objsearchpackage->rowid;
691 $line->packaging = $objsearchpackage->packaging;
692 }
693 } else {
694 $this->error = $this->db->lasterror();
695 return -1;
696 }
697 }
698
699 $line->date_start = $this->db->jdate($objp->date_start);
700 $line->date_end = $this->db->jdate($objp->date_end);
701 $line->fk_unit = $objp->fk_unit;
702
703 // Multicurrency
704 $line->fk_multicurrency = $objp->fk_multicurrency;
705 $line->multicurrency_code = $objp->multicurrency_code;
706 $line->multicurrency_subprice = $objp->multicurrency_subprice;
707 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
708 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
709 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
710
711 $line->info_bits = $objp->info_bits;
712 $line->special_code = $objp->special_code;
713 $line->fk_parent_line = $objp->fk_parent_line;
714
715 $line->rang = $objp->rang;
716
717 // Retrieve all extrafield
718 // fetch optionals attributes and labels
719 $line->fetch_optionals();
720
721 $this->lines[$i] = $line;
722
723 $i++;
724 }
725 $this->db->free($result);
726
727 return $num;
728 } else {
729 $this->error = $this->db->error()." sql=".$sql;
730 return -1;
731 }
732 }
733
742 public function valid($user, $idwarehouse = 0, $notrigger = 0)
743 {
744 global $conf;
745 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
746
747 $error = 0;
748
749 dol_syslog(get_class($this)."::valid");
750 $result = 0;
751 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
752 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
753 $this->db->begin();
754
755 // Definition of supplier order numbering model name
756 $soc = new Societe($this->db);
757 $soc->fetch($this->fourn_id);
758
759 // Check if object has a temporary ref
760 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
761 $num = $this->getNextNumRef($soc);
762 } else {
763 $num = $this->ref;
764 }
765 $this->newref = dol_sanitizeFileName($num);
766
767 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
768 $sql .= " SET ref='".$this->db->escape($num)."',";
769 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
770 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
771 $sql .= " fk_user_valid = ".((int) $user->id);
772 $sql .= " WHERE rowid = ".((int) $this->id);
773 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
774
775 $resql = $this->db->query($sql);
776 if (!$resql) {
777 dol_print_error($this->db);
778 $error++;
779 }
780
781 if (!$error && !$notrigger) {
782 // Call trigger
783 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
784 if ($result < 0) {
785 $error++;
786 }
787 // End call triggers
788 }
789
790 if (!$error) {
791 $this->oldref = $this->ref;
792
793 // Rename directory if dir was a temporary ref
794 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
795 // Now we rename also files into index
796 $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)."'";
797 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
798 $resql = $this->db->query($sql);
799 if (!$resql) {
800 $error++;
801 $this->error = $this->db->lasterror();
802 }
803 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
804 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
805 $resql = $this->db->query($sql);
806 if (!$resql) {
807 $error++;
808 $this->error = $this->db->lasterror();
809 }
810
811 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
812 $oldref = dol_sanitizeFileName($this->ref);
813 $newref = dol_sanitizeFileName($num);
814 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
815 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
816 if (!$error && file_exists($dirsource)) {
817 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
818
819 if (@rename($dirsource, $dirdest)) {
820 dol_syslog("Rename ok");
821 // Rename docs starting with $oldref with $newref
822 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
823 foreach ($listoffiles as $fileentry) {
824 $dirsource = $fileentry['name'];
825 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
826 $dirsource = $fileentry['path'].'/'.$dirsource;
827 $dirdest = $fileentry['path'].'/'.$dirdest;
828 @rename($dirsource, $dirdest);
829 }
830 }
831 }
832 }
833 }
834
835 if (!$error) {
836 $result = 1;
838 $this->statut = self::STATUS_VALIDATED; // deprecated
839 $this->ref = $num;
840 }
841
842 if (!$error) {
843 $this->db->commit();
844 return 1;
845 } else {
846 $this->db->rollback();
847 return -1;
848 }
849 } else {
850 $this->error = 'NotAuthorized';
851 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
852 return -1;
853 }
854 }
855
862 public function getLibStatut($mode = 0)
863 {
864 return $this->LibStatut($this->statut, $mode, $this->billed);
865 }
866
867 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
876 public function LibStatut($status, $mode = 0, $billed = 0)
877 {
878 // phpcs:enable
879 global $langs, $hookmanager;
880
881 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
882 $langs->load('orders');
883
884 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
885 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
886 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
887 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
888 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
889 } else {
890 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
891 }
892 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
893 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
894 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
895 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
896 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
897
898 // List of language codes for status
899 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
900 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
901 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
902 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
903 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
904 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
905 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
906 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
907 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
908 }
909
910 $statustrans = array(
911 0 => 'status0',
912 1 => 'status1b',
913 2 => 'status1',
914 3 => 'status4',
915 4 => 'status4b',
916 5 => 'status6',
917 6 => 'status9',
918 7 => 'status9',
919 9 => 'status9',
920 );
921
922 $statusClass = 'status0';
923 if (!empty($statustrans[$status])) {
924 $statusClass = $statustrans[$status];
925 }
926
927 $billedtext = '';
928 if ($billed) {
929 $billedtext = ' - '.$langs->trans("Billed");
930 }
931 if ($status == 5 && $billed) {
932 $statusClass = 'status6';
933 }
934
935 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
936 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
937
938 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
939 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
940 if ($reshook > 0) {
941 return $hookmanager->resPrint;
942 }
943
944 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
945 }
946
954 public function getTooltipContentArray($params)
955 {
956 global $conf, $langs, $user;
957
958 $langs->loadLangs(['bills', 'orders']);
959
960 $datas = [];
961 $nofetch = !empty($params['nofetch']);
962
963 if ($user->hasRight("fournisseur", "commande", "read")) {
964 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
965 if (isset($this->statut)) {
966 $datas['picto'] .= ' '.$this->getLibStatut(5);
967 }
968 if (!empty($this->ref)) {
969 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
970 }
971 if (!empty($this->ref_supplier)) {
972 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
973 }
974 if (!$nofetch) {
975 $langs->load('companies');
976 if (empty($this->thirdparty)) {
977 $this->fetch_thirdparty();
978 }
979 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
980 }
981 if (!empty($this->total_ht)) {
982 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
983 }
984 if (!empty($this->total_tva)) {
985 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
986 }
987 if (!empty($this->total_ttc)) {
988 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
989 }
990 if (!empty($this->date)) {
991 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
992 }
993 if (!empty($this->delivery_date)) {
994 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
995 }
996 }
997 return $datas;
998 }
999
1010 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1011 {
1012 global $langs, $user, $hookmanager;
1013
1014 $result = '';
1015 $params = [
1016 'id' => $this->id,
1017 'objecttype' => $this->element,
1018 'option' => $option,
1019 'nofetch' => 1
1020 ];
1021 $classfortooltip = 'classfortooltip';
1022 $dataparams = '';
1023 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1024 $classfortooltip = 'classforajaxtooltip';
1025 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1026 $label = '';
1027 } else {
1028 $label = implode($this->getTooltipContentArray($params));
1029 }
1030
1031 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1032
1033 if ($option !== 'nolink') {
1034 // Add param to save lastsearch_values or not
1035 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1036 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1037 $add_save_lastsearch_values = 1;
1038 }
1039 if ($add_save_lastsearch_values) {
1040 $url .= '&save_lastsearch_values=1';
1041 }
1042 }
1043
1044 $linkclose = '';
1045 if (empty($notooltip)) {
1046 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1047 $label = $langs->trans("ShowOrder");
1048 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1049 }
1050 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1051 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1052 }
1053
1054 $linkstart = '<a href="'.$url.'"';
1055 $linkstart .= $linkclose.'>';
1056 $linkend = '</a>';
1057
1058 $result .= $linkstart;
1059 if ($withpicto) {
1060 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1061 }
1062 if ($withpicto != 2) {
1063 $result .= $this->ref;
1064 }
1065 $result .= $linkend;
1066
1067 if ($addlinktonotes) {
1068 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1069 if ($txttoshow) {
1070 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1071 $result .= ' <span class="note inline-block">';
1072 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1073 $result .= img_picto('', 'note');
1074 $result .= '</a>';
1075 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1076 //$result.='</a>';
1077 $result .= '</span>';
1078 }
1079 }
1080
1081 global $action;
1082 $hookmanager->initHooks(array($this->element . 'dao'));
1083 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1084 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1085 if ($reshook > 0) {
1086 $result = $hookmanager->resPrint;
1087 } else {
1088 $result .= $hookmanager->resPrint;
1089 }
1090 return $result;
1091 }
1092
1093
1101 public function getNextNumRef($soc)
1102 {
1103 global $langs, $conf;
1104 $langs->load("orders");
1105
1106 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1107 $mybool = false;
1108
1109 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1110 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1111
1112 // Include file with class
1113 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1114
1115 foreach ($dirmodels as $reldir) {
1116 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1117
1118 // Load file with numbering class (if found)
1119 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1120 }
1121
1122 if ($mybool === false) {
1123 dol_print_error(null, "Failed to include file ".$file);
1124 return '';
1125 }
1126
1127 $obj = new $classname();
1128 $numref = $obj->getNextValue($soc, $this);
1129
1130 if ($numref != "") {
1131 return $numref;
1132 } else {
1133 $this->error = $obj->error;
1134 return -1;
1135 }
1136 } else {
1137 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1138 return -2;
1139 }
1140 }
1147 public function classifyBilled(User $user)
1148 {
1149 $error = 0;
1150
1151 if ($this->billed) {
1152 return 0;
1153 }
1154
1155 $this->db->begin();
1156
1157 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1158 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1159
1160 if ($this->db->query($sql)) {
1161 if (!$error) {
1162 // Call trigger
1163 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1164 if ($result < 0) {
1165 $error++;
1166 }
1167 // End call triggers
1168 }
1169
1170 if (!$error) {
1171 $this->billed = 1;
1172
1173 $this->db->commit();
1174 return 1;
1175 } else {
1176 $this->db->rollback();
1177 return -1;
1178 }
1179 } else {
1180 dol_print_error($this->db);
1181
1182 $this->db->rollback();
1183 return -1;
1184 }
1185 }
1186
1195 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1196 {
1197 global $langs, $conf;
1198 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1199
1200 $error = 0;
1201
1202 dol_syslog(get_class($this)."::approve");
1203
1204 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1205 $now = dol_now();
1206
1207 $this->db->begin();
1208
1209 // Definition of order numbering model name
1210 $soc = new Societe($this->db);
1211 $soc->fetch($this->fourn_id);
1212
1213 // Check if object has a temporary ref
1214 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1215 $num = $this->getNextNumRef($soc);
1216 } else {
1217 $num = $this->ref;
1218 }
1219 $this->newref = dol_sanitizeFileName($num);
1220
1221 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1222 $movetoapprovestatus = true;
1223 $comment = '';
1224
1225 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1226 $sql .= " SET ref='".$this->db->escape($num)."',";
1227 if (empty($secondlevel)) { // standard or first level approval
1228 $sql .= " date_approve='".$this->db->idate($now)."',";
1229 $sql .= " fk_user_approve = ".$user->id;
1230 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1231 if (empty($this->user_approve_id2)) {
1232 $movetoapprovestatus = false; // second level approval not done
1233 $comment = ' (first level)';
1234 }
1235 }
1236 } else { // request a second level approval
1237 $sql .= " date_approve2='".$this->db->idate($now)."',";
1238 $sql .= " fk_user_approve2 = ".((int) $user->id);
1239 if (empty($this->user_approve_id)) {
1240 $movetoapprovestatus = false; // first level approval not done
1241 }
1242 $comment = ' (second level)';
1243 }
1244 // If double approval is required and first approval, we keep status to 1 = validated
1245 if ($movetoapprovestatus) {
1246 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1247 } else {
1248 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1249 }
1250 $sql .= " WHERE rowid = ".((int) $this->id);
1251 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1252
1253 if ($this->db->query($sql)) {
1254 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1255 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1256 if ($result < 0 && $result != -2) { // -2 means already exists
1257 $error++;
1258 }
1259 }
1260
1261 // If stock is incremented on validate order, we must increment it
1262 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1263 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1264 $langs->load("agenda");
1265
1266 $cpt = count($this->lines);
1267 for ($i = 0; $i < $cpt; $i++) {
1268 // Product with reference
1269 if ($this->lines[$i]->fk_product > 0) {
1270 $this->line = $this->lines[$i];
1271 $mouvP = new MouvementStock($this->db);
1272 $mouvP->origin = &$this;
1273 $mouvP->setOrigin($this->element, $this->id);
1274 // We decrement stock of product (and sub-products)
1275 $up_ht_disc = $this->lines[$i]->subprice;
1276 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1277 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1278 }
1279 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1280 if ($result < 0) {
1281 $error++;
1282 }
1283 unset($this->line);
1284 }
1285 }
1286 }
1287
1288 if (!$error) {
1289 // Call trigger
1290 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1291 if ($result < 0) {
1292 $error++;
1293 }
1294 // End call triggers
1295 }
1296
1297 if (!$error) {
1298 $this->ref = $this->newref;
1299
1300 if ($movetoapprovestatus) {
1302 } else {
1304 }
1305 if (empty($secondlevel)) { // standard or first level approval
1306 $this->date_approve = $now;
1307 $this->user_approve_id = $user->id;
1308 } else { // request a second level approval
1309 $this->date_approve2 = $now;
1310 $this->user_approve_id2 = $user->id;
1311 }
1312
1313 $this->db->commit();
1314 return 1;
1315 } else {
1316 $this->db->rollback();
1317 return -1;
1318 }
1319 } else {
1320 $this->db->rollback();
1321 $this->error = $this->db->lasterror();
1322 return -1;
1323 }
1324 } else {
1325 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1326 }
1327 return -1;
1328 }
1329
1336 public function refuse($user)
1337 {
1338 global $conf, $langs;
1339
1340 $error = 0;
1341
1342 dol_syslog(get_class($this)."::refuse");
1343 $result = 0;
1344 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1345 $this->db->begin();
1346
1347 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1348 $sql .= " WHERE rowid = ".((int) $this->id);
1349
1350 if ($this->db->query($sql)) {
1351 $result = 0;
1352
1353 if ($error == 0) {
1354 // Call trigger
1355 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1356 if ($result < 0) {
1357 $error++;
1358 $this->db->rollback();
1359 } else {
1360 $this->db->commit();
1361 }
1362 // End call triggers
1363 }
1364 } else {
1365 $this->db->rollback();
1366 $this->error = $this->db->lasterror();
1367 dol_syslog(get_class($this)."::refuse Error -1");
1368 $result = -1;
1369 }
1370 } else {
1371 dol_syslog(get_class($this)."::refuse Not Authorized");
1372 }
1373 return $result;
1374 }
1375
1376 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1385 public function Cancel($user, $idwarehouse = -1)
1386 {
1387 // phpcs:enable
1388 global $langs, $conf;
1389
1390 $error = 0;
1391
1392 //dol_syslog("CommandeFournisseur::Cancel");
1393 $result = 0;
1394 if ($user->hasRight("fournisseur", "commande", "commander")) {
1395 $statut = self::STATUS_CANCELED;
1396
1397 $this->db->begin();
1398
1399 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1400 $sql .= " WHERE rowid = ".((int) $this->id);
1401 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1402 if ($this->db->query($sql)) {
1403 $result = 0;
1404
1405 // Call trigger
1406 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1407 if ($result < 0) {
1408 $error++;
1409 }
1410 // End call triggers
1411
1412 if ($error == 0) {
1413 $this->db->commit();
1414 return 1;
1415 } else {
1416 $this->db->rollback();
1417 return -1;
1418 }
1419 } else {
1420 $this->db->rollback();
1421 $this->error = $this->db->lasterror();
1422 dol_syslog(get_class($this)."::cancel ".$this->error);
1423 return -1;
1424 }
1425 } else {
1426 dol_syslog(get_class($this)."::cancel Not Authorized");
1427 return -1;
1428 }
1429 }
1430
1440 public function commande($user, $date, $methode, $comment = '')
1441 {
1442 global $langs;
1443 dol_syslog(get_class($this)."::commande");
1444 $error = 0;
1445 if ($user->hasRight("fournisseur", "commande", "commander")) {
1446 $this->db->begin();
1447
1448 $newnoteprivate = $this->note_private;
1449 if ($comment) {
1450 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1451 }
1452
1453 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1454 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1455 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1456 $sql .= " WHERE rowid=".((int) $this->id);
1457
1458 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1459 if ($this->db->query($sql)) {
1461 $this->methode_commande_id = $methode;
1462 $this->date_commande = $date;
1463 $this->context = array('comments' => $comment);
1464
1465 // Call trigger
1466 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1467 if ($result < 0) {
1468 $error++;
1469 }
1470 // End call triggers
1471 } else {
1472 $error++;
1473 $this->error = $this->db->lasterror();
1474 $this->errors[] = $this->db->lasterror();
1475 }
1476
1477 if (!$error) {
1478 $this->db->commit();
1479 } else {
1480 $this->db->rollback();
1481 }
1482 } else {
1483 $error++;
1484 $this->error = $langs->trans('NotAuthorized');
1485 $this->errors[] = $langs->trans('NotAuthorized');
1486 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1487 }
1488
1489 return ($error ? -1 : 1);
1490 }
1491
1499 public function create($user, $notrigger = 0)
1500 {
1501 global $langs, $conf, $hookmanager;
1502
1503 $this->db->begin();
1504
1505 $error = 0;
1506 $now = dol_now();
1507
1508 // set tmp vars
1509 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1510 if (empty($date)) {
1511 $date = $now;
1512 }
1513 $delivery_date = $this->delivery_date;
1514
1515 // Clean parameters
1516 if (empty($this->source)) {
1517 $this->source = 0;
1518 }
1519
1520 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1521 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1522 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1523 } else {
1524 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1525 }
1526 if (empty($this->fk_multicurrency)) {
1527 $this->multicurrency_code = $conf->currency;
1528 $this->fk_multicurrency = 0;
1529 $this->multicurrency_tx = 1;
1530 }
1531
1532 // We set order into draft status
1533 $this->statut = self::STATUS_DRAFT; // deprecated
1534 $this->status = self::STATUS_DRAFT;
1535
1536 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1537 $sql .= "ref";
1538 $sql .= ", ref_supplier";
1539 $sql .= ", note_private";
1540 $sql .= ", note_public";
1541 $sql .= ", entity";
1542 $sql .= ", fk_soc";
1543 $sql .= ", fk_projet";
1544 $sql .= ", date_creation";
1545 $sql .= ", date_livraison";
1546 $sql .= ", fk_user_author";
1547 $sql .= ", fk_statut";
1548 $sql .= ", source";
1549 $sql .= ", model_pdf";
1550 $sql .= ", fk_mode_reglement";
1551 $sql .= ", fk_cond_reglement";
1552 $sql .= ", fk_account";
1553 $sql .= ", fk_incoterms, location_incoterms";
1554 $sql .= ", fk_multicurrency";
1555 $sql .= ", multicurrency_code";
1556 $sql .= ", multicurrency_tx";
1557 $sql .= ") ";
1558 $sql .= " VALUES (";
1559 $sql .= "'(PROV)'";
1560 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1561 $sql .= ", '".$this->db->escape($this->note_private)."'";
1562 $sql .= ", '".$this->db->escape($this->note_public)."'";
1563 $sql .= ", ".setEntity($this);
1564 $sql .= ", ".((int) $this->socid);
1565 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1566 $sql .= ", '".$this->db->idate($date)."'";
1567 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1568 $sql .= ", ".((int) $user->id);
1569 $sql .= ", ".self::STATUS_DRAFT;
1570 $sql .= ", ".((int) $this->source);
1571 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1572 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1573 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1574 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1575 $sql .= ", ".(int) $this->fk_incoterms;
1576 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1577 $sql .= ", ".(int) $this->fk_multicurrency;
1578 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1579 $sql .= ", ".(float) $this->multicurrency_tx;
1580 $sql .= ")";
1581
1582 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1583 if ($this->db->query($sql)) {
1584 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1585
1586 if ($this->id) {
1587 $num = count($this->lines);
1588
1589 // insert products details into database
1590 for ($i = 0; $i < $num; $i++) {
1591 $line = $this->lines[$i];
1592 if (!is_object($line)) {
1593 $line = (object) $line;
1594 }
1595
1596 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1597
1598 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1599 $result = $this->addline(
1600 $line->desc,
1601 $line->subprice,
1602 $line->qty,
1603 $line->tva_tx,
1604 $line->localtax1_tx,
1605 $line->localtax2_tx,
1606 $line->fk_product,
1607 0,
1608 $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
1609 $line->remise_percent,
1610 'HT',
1611 0,
1612 $line->product_type,
1613 $line->info_bits,
1614 false,
1615 $line->date_start,
1616 $line->date_end,
1617 $line->array_options,
1618 $line->fk_unit,
1619 $line->multicurrency_subprice, // pu_ht_devise
1620 $line->origin, // origin
1621 $line->origin_id, // origin_id
1622 $line->rang, // rang
1623 $line->special_code
1624 );
1625 if ($result < 0) {
1626 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1627 $this->db->rollback();
1628 return -1;
1629 }
1630 }
1631
1632 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1633 $sql .= " SET ref='(PROV".$this->id.")'";
1634 $sql .= " WHERE rowid=".((int) $this->id);
1635
1636 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1637 if ($this->db->query($sql)) {
1638 // Add link with price request and supplier order
1639 if ($this->id) {
1640 $this->ref = "(PROV".$this->id.")";
1641
1642 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1643 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1644 }
1645
1646 // Add object linked
1647 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1648 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1649 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, ...))
1650 foreach ($tmp_origin_id as $origin_id) {
1651 $ret = $this->add_object_linked($origin, $origin_id);
1652 if (!$ret) {
1653 dol_print_error($this->db);
1654 $error++;
1655 }
1656 }
1657 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1658 $origin_id = $tmp_origin_id;
1659 $ret = $this->add_object_linked($origin, $origin_id);
1660 if (!$ret) {
1661 dol_print_error($this->db);
1662 $error++;
1663 }
1664 }
1665 }
1666 }
1667 }
1668
1669 if (!$error) {
1670 $result = $this->insertExtraFields();
1671 if ($result < 0) {
1672 $error++;
1673 }
1674 }
1675
1676 if (!$error && !$notrigger) {
1677 // Call trigger
1678 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1679 if ($result < 0) {
1680 $this->db->rollback();
1681
1682 return -1;
1683 }
1684 // End call triggers
1685 }
1686
1687 $this->db->commit();
1688 return $this->id;
1689 } else {
1690 $this->error = $this->db->lasterror();
1691 $this->db->rollback();
1692
1693 return -2;
1694 }
1695 } else {
1696 $this->error = 'Failed to get ID of inserted line';
1697
1698 return -1;
1699 }
1700 } else {
1701 $this->error = $this->db->lasterror();
1702 $this->db->rollback();
1703
1704 return -1;
1705 }
1706 }
1707
1715 public function update(User $user, $notrigger = 0)
1716 {
1717 global $conf;
1718
1719 $error = 0;
1720
1721 // Clean parameters
1722 if (isset($this->ref)) {
1723 $this->ref = trim($this->ref);
1724 }
1725 if (isset($this->ref_supplier)) {
1726 $this->ref_supplier = trim($this->ref_supplier);
1727 }
1728 if (isset($this->note_private)) {
1729 $this->note_private = trim($this->note_private);
1730 }
1731 if (isset($this->note_public)) {
1732 $this->note_public = trim($this->note_public);
1733 }
1734 if (isset($this->model_pdf)) {
1735 $this->model_pdf = trim($this->model_pdf);
1736 }
1737 if (isset($this->import_key)) {
1738 $this->import_key = trim($this->import_key);
1739 }
1740
1741 // Update request
1742 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1743
1744 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1745 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1746 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1747 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1748 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1749 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1750 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1751 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1752 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1753 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1754 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1755 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1756 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1757 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1758 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1759 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1760 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1761 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1762 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1763 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1764 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1765 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1766 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1767 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1768 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1769
1770 $sql .= " WHERE rowid=".((int) $this->id);
1771
1772 $this->db->begin();
1773
1774 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1775 $resql = $this->db->query($sql);
1776 if (!$resql) {
1777 $error++;
1778 $this->errors[] = "Error ".$this->db->lasterror();
1779 }
1780
1781 if (!$error) {
1782 $result = $this->insertExtraFields();
1783 if ($result < 0) {
1784 $error++;
1785 }
1786 }
1787
1788 if (!$error && !$notrigger) {
1789 // Call trigger
1790 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1791 if ($result < 0) {
1792 $error++;
1793 }
1794 // End call triggers
1795 }
1796
1797 // Commit or rollback
1798 if ($error) {
1799 foreach ($this->errors as $errmsg) {
1800 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1801 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1802 }
1803 $this->db->rollback();
1804 return -1 * $error;
1805 } else {
1806 $this->db->commit();
1807 return 1;
1808 }
1809 }
1810
1819 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1820 {
1821 global $conf, $user, $hookmanager;
1822
1823 $error = 0;
1824
1825 $this->db->begin();
1826
1827 // get extrafields so they will be clone
1828 foreach ($this->lines as $line) {
1829 $line->fetch_optionals();
1830 }
1831
1832 // Load source object
1833 $objFrom = clone $this;
1834
1835 // Change socid if needed
1836 if (!empty($socid) && $socid != $this->socid) {
1837 $objsoc = new Societe($this->db);
1838
1839 if ($objsoc->fetch($socid) > 0) {
1840 $this->socid = $objsoc->id;
1841 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1842 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1843 $this->fk_project = 0;
1844 $this->fk_delivery_address = 0;
1845 }
1846
1847 // TODO Change product price if multi-prices
1848 }
1849
1850 $this->id = 0;
1851 $this->statut = self::STATUS_DRAFT;
1852
1853 // Clear fields
1854 $this->user_author_id = $user->id;
1855 $this->user_validation_id = 0;
1856
1857 $this->date = dol_now();
1858 $this->date_creation = 0;
1859 $this->date_validation = 0;
1860 $this->date_commande = 0;
1861 $this->ref_supplier = '';
1862 $this->user_approve_id = 0;
1863 $this->user_approve_id2 = 0;
1864 $this->date_approve = 0;
1865 $this->date_approve2 = 0;
1866
1867 // Create clone
1868 $this->context['createfromclone'] = 'createfromclone';
1869 $result = $this->create($user, $notrigger);
1870 if ($result < 0) {
1871 $error++;
1872 }
1873
1874 if (!$error) {
1875 // Hook of thirdparty module
1876 if (is_object($hookmanager)) {
1877 $parameters = array('objFrom' => $objFrom);
1878 $action = '';
1879 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1880 if ($reshook < 0) {
1881 $this->setErrorsFromObject($hookmanager);
1882 $error++;
1883 }
1884 }
1885 }
1886
1887 unset($this->context['createfromclone']);
1888
1889 // End
1890 if (!$error) {
1891 $this->db->commit();
1892 return $this->id;
1893 } else {
1894 $this->db->rollback();
1895 return -1;
1896 }
1897 }
1898
1928 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)
1929 {
1930 global $langs, $mysoc, $conf;
1931
1932 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");
1933 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1934
1935 if ($this->statut == self::STATUS_DRAFT) {
1936 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1937
1938 // Clean parameters
1939 if (empty($qty)) {
1940 $qty = 0;
1941 }
1942 if (!$info_bits) {
1943 $info_bits = 0;
1944 }
1945 if (empty($txtva)) {
1946 $txtva = 0;
1947 }
1948 if (empty($rang)) {
1949 $rang = 0;
1950 }
1951 if (empty($txlocaltax1)) {
1952 $txlocaltax1 = 0;
1953 }
1954 if (empty($txlocaltax2)) {
1955 $txlocaltax2 = 0;
1956 }
1957 if (empty($remise_percent)) {
1958 $remise_percent = 0;
1959 }
1960
1961 $remise_percent = price2num($remise_percent);
1962 $qty = price2num($qty);
1963 $pu_ht = price2num($pu_ht);
1964 $pu_ht_devise = price2num($pu_ht_devise);
1965 $pu_ttc = price2num($pu_ttc);
1966 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1967 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1968 }
1969 $txlocaltax1 = price2num($txlocaltax1);
1970 $txlocaltax2 = price2num($txlocaltax2);
1971 if ($price_base_type == 'HT') {
1972 $pu = $pu_ht;
1973 } else {
1974 $pu = $pu_ttc;
1975 }
1976 $desc = trim($desc);
1977
1978 // Check parameters
1979 if ($qty < 0 && !$fk_product) {
1980 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1981 return -1;
1982 }
1983 if ($type < 0) {
1984 return -1;
1985 }
1986 if ($date_start && $date_end && $date_start > $date_end) {
1987 $langs->load("errors");
1988 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1989 return -1;
1990 }
1991
1992
1993 $this->db->begin();
1994
1995 $product_type = $type;
1996 $label = ''; // deprecated
1997
1998 if ($fk_product > 0) {
1999 if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
2000 // Check quantity is enough
2001 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);
2002 $prod = new ProductFournisseur($this->db);
2003 if ($prod->fetch($fk_product) > 0) {
2004 $product_type = $prod->type;
2005 $label = $prod->label;
2006
2007 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2008 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2009 $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
2010
2011 // If supplier order created from sales order, we take best supplier price
2012 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2013 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2014 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2015 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2016 // is remise percent not keyed but present for the product we add it
2017 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2018 $remise_percent = $prod->remise_percent;
2019 }
2020 }
2021 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2022 $langs->load("errors");
2023 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2024 $this->db->rollback();
2025 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2026 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2027 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2028 return -1;
2029 }
2030 if ($result == -1) {
2031 $langs->load("errors");
2032 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2033 $this->db->rollback();
2034 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2035 return -1;
2036 }
2037 if ($result < -1) {
2038 $this->error = $prod->error;
2039 $this->db->rollback();
2040 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2041 return -1;
2042 }
2043 } else {
2044 $this->error = $prod->error;
2045 $this->db->rollback();
2046 return -1;
2047 }
2048 }
2049
2050 // Predefine quantity according to packaging
2051 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2052 $prod = new Product($this->db);
2053 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2054
2055 if ($qty < $prod->packaging) {
2056 $qty = $prod->packaging;
2057 } else {
2058 if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
2059 $coeff = intval($qty / $prod->packaging) + 1;
2060 $qty = $prod->packaging * $coeff;
2061 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2062 }
2063 }
2064 }
2065 }
2066
2067 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2068 $pu = 0;
2069 }
2070
2071 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2072
2073 // Clean vat code
2074 $reg = array();
2075 $vat_src_code = '';
2076 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2077 $vat_src_code = $reg[1];
2078 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2079 }
2080
2081 // Calcul du total TTC et de la TVA pour la ligne a partir de
2082 // qty, pu, remise_percent et txtva
2083 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2084 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2085
2086 $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);
2087
2088 $total_ht = $tabprice[0];
2089 $total_tva = $tabprice[1];
2090 $total_ttc = $tabprice[2];
2091 $total_localtax1 = $tabprice[9];
2092 $total_localtax2 = $tabprice[10];
2093 $pu = $pu_ht = $tabprice[3];
2094
2095 // MultiCurrency
2096 $multicurrency_total_ht = $tabprice[16];
2097 $multicurrency_total_tva = $tabprice[17];
2098 $multicurrency_total_ttc = $tabprice[18];
2099 $pu_ht_devise = $tabprice[19];
2100
2101 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2102 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2103
2104 if ($rang < 0) {
2105 $rangmax = $this->line_max();
2106 $rang = $rangmax + 1;
2107 }
2108
2109 // Insert line
2110 $this->line = new CommandeFournisseurLigne($this->db);
2111
2112 $this->line->context = $this->context;
2113
2114 $this->line->fk_commande = $this->id;
2115 $this->line->label = $label;
2116 $this->line->ref_fourn = $ref_supplier;
2117 $this->line->ref_supplier = $ref_supplier;
2118 $this->line->desc = $desc;
2119 $this->line->qty = $qty;
2120 $this->line->tva_tx = $txtva;
2121 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2122 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2123 $this->line->localtax1_type = $localtax1_type;
2124 $this->line->localtax2_type = $localtax2_type;
2125 $this->line->fk_product = $fk_product;
2126 $this->line->product_type = $product_type;
2127 $this->line->remise_percent = $remise_percent;
2128 $this->line->subprice = $pu_ht;
2129 $this->line->rang = $rang;
2130 $this->line->info_bits = $info_bits;
2131
2132 $this->line->vat_src_code = $vat_src_code;
2133 $this->line->total_ht = $total_ht;
2134 $this->line->total_tva = $total_tva;
2135 $this->line->total_localtax1 = $total_localtax1;
2136 $this->line->total_localtax2 = $total_localtax2;
2137 $this->line->total_ttc = $total_ttc;
2138 $this->line->product_type = $type;
2139 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2140 $this->line->origin = $origin;
2141 $this->line->origin_id = $origin_id;
2142 $this->line->fk_unit = $fk_unit;
2143
2144 $this->line->date_start = $date_start;
2145 $this->line->date_end = $date_end;
2146
2147 // Multicurrency
2148 $this->line->fk_multicurrency = $this->fk_multicurrency;
2149 $this->line->multicurrency_code = $this->multicurrency_code;
2150 $this->line->multicurrency_subprice = $pu_ht_devise;
2151 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2152 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2153 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2154
2155 $this->line->subprice = $pu_ht;
2156 $this->line->price = $this->line->subprice;
2157
2158 $this->line->remise_percent = $remise_percent;
2159
2160 if (is_array($array_options) && count($array_options) > 0) {
2161 $this->line->array_options = $array_options;
2162 }
2163
2164 $result = $this->line->insert($notrigger);
2165 if ($result > 0) {
2166 // Reorder if child line
2167 if (!empty($this->line->fk_parent_line)) {
2168 $this->line_order(true, 'DESC');
2169 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2170 $linecount = count($this->lines);
2171 for ($ii = $rang; $ii <= $linecount; $ii++) {
2172 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2173 }
2174 }
2175
2176 // Mise a jour information denormalisees au niveau de la commande meme
2177 $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.
2178 if ($result > 0) {
2179 $this->db->commit();
2180 return $this->line->id;
2181 } else {
2182 $this->db->rollback();
2183 return -1;
2184 }
2185 } else {
2186 $this->error = $this->line->error;
2187 $this->errors = $this->line->errors;
2188 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2189 $this->db->rollback();
2190 return -1;
2191 }
2192 }
2193 return -1;
2194 }
2195
2196
2214 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2215 {
2216 global $conf, $langs;
2217
2218 $error = 0;
2219 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2220
2221 // Check parameters (if test are wrong here, there is bug into caller)
2222 if ($entrepot <= 0) {
2223 $this->error = 'ErrorBadValueForParameterWarehouse';
2224 return -1;
2225 }
2226 if ($qty == 0) {
2227 $this->error = 'ErrorBadValueForParameterQty';
2228 return -1;
2229 }
2230
2231 $dispatchstatus = 1;
2232 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2233 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2234 }
2235
2236 $now = dol_now();
2237
2238 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2239
2240 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2241 $this->db->begin();
2242
2243 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2244 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2245 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2246 $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");
2247 $sql .= ")";
2248
2249 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2250 $resql = $this->db->query($sql);
2251 if ($resql) {
2252 if (!$notrigger) {
2253 global $conf, $langs, $user;
2254 // Call trigger
2255 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2256 if ($result < 0) {
2257 $error++;
2258 }
2259 // End call triggers
2260 }
2261 } else {
2262 $this->error = $this->db->lasterror();
2263 $error++;
2264 }
2265
2266 // If module stock is enabled and the stock increase is done on purchase order dispatching
2267 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2268 $mouv = new MouvementStock($this->db);
2269 if ($product > 0) {
2270 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2271 $mouv->origin = &$this;
2272 $mouv->setOrigin($this->element, $this->id);
2273
2274 // Method change if qty < 0
2275 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2276 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2277 } else {
2278 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2279 }
2280
2281 if ($result < 0) {
2282 $this->error = $mouv->error;
2283 $this->errors = $mouv->errors;
2284 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2285 $error++;
2286 }
2287 }
2288 }
2289
2290 if ($error == 0) {
2291 $this->db->commit();
2292 return 1;
2293 } else {
2294 $this->db->rollback();
2295 return -1;
2296 }
2297 } else {
2298 $this->error = 'BadStatusForObject';
2299 return -2;
2300 }
2301 }
2302
2310 public function deleteLine($idline, $notrigger = 0)
2311 {
2312 global $user;
2313
2314 if ($this->statut == 0) {
2315 $line = new CommandeFournisseurLigne($this->db);
2316
2317 if ($line->fetch($idline) <= 0) {
2318 return 0;
2319 }
2320
2321 // check if not yet received
2322 $dispatchedLines = $this->getDispachedLines();
2323 foreach ($dispatchedLines as $dispatchLine) {
2324 if ($dispatchLine['orderlineid'] == $idline) {
2325 $this->error = "LineAlreadyDispatched";
2326 $this->errors[] = $this->error;
2327 return -3;
2328 }
2329 }
2330
2331 if ($line->delete($user, $notrigger) > 0) {
2332 $this->update_price(1);
2333 return 1;
2334 } else {
2335 $this->setErrorsFromObject($line);
2336 return -1;
2337 }
2338 } else {
2339 return -2;
2340 }
2341 }
2342
2350 public function delete(User $user, $notrigger = 0)
2351 {
2352 global $langs, $conf;
2353 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2354
2355 $error = 0;
2356
2357 $this->db->begin();
2358
2359 if (empty($notrigger)) {
2360 // Call trigger
2361 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2362 if ($result < 0) {
2363 $this->errors[] = 'ErrorWhenRunningTrigger';
2364 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2365 $this->db->rollback();
2366 return -1;
2367 }
2368 // End call triggers
2369 }
2370
2371 // Test we can delete
2372 $this->fetchObjectLinked(null, 'order_supplier');
2373 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2374 foreach ($this->linkedObjects['reception'] as $element) {
2375 if ($element->statut >= 0) {
2376 $this->errors[] = $langs->trans('ReceptionExist');
2377 $error++;
2378 break;
2379 }
2380 }
2381 }
2382
2383 $main = $this->db->prefix().'commande_fournisseurdet';
2384
2385 if (!$error) {
2386 $sql1 = "UPDATE ".$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet IN (SELECT rowid FROM ".$main." WHERE fk_commande = ".((int) $this->id).")";
2387 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2388 if (!$this->db->query($sql1)) {
2389 $error++;
2390 $this->error = $this->db->lasterror();
2391 $this->errors[] = $this->db->lasterror();
2392 }
2393 }
2394
2395 if (!$error) {
2396 $ef = $main."_extrafields";
2397 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2398 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2399 if (!$this->db->query($sql)) {
2400 $this->error = $this->db->lasterror();
2401 $this->errors[] = $this->db->lasterror();
2402 $error++;
2403 }
2404 }
2405
2406 if (!$error) {
2407 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2408 dol_syslog(get_class($this)."::delete", 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_fournisseur WHERE rowid =".((int) $this->id);
2418 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2419 if ($resql = $this->db->query($sql)) {
2420 if ($this->db->affected_rows($resql) < 1) {
2421 $this->error = $this->db->lasterror();
2422 $this->errors[] = $this->db->lasterror();
2423 $error++;
2424 }
2425 } else {
2426 $this->error = $this->db->lasterror();
2427 $this->errors[] = $this->db->lasterror();
2428 $error++;
2429 }
2430 }
2431
2432 // Remove extrafields
2433 if (!$error) {
2434 $result = $this->deleteExtraFields();
2435 if ($result < 0) {
2436 $this->error = 'FailToDeleteExtraFields';
2437 $this->errors[] = 'FailToDeleteExtraFields';
2438 $error++;
2439 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2440 }
2441 }
2442
2443 // Delete linked object
2444 $res = $this->deleteObjectLinked();
2445 if ($res < 0) {
2446 $this->error = 'FailToDeleteObjectLinked';
2447 $this->errors[] = 'FailToDeleteObjectLinked';
2448 $error++;
2449 }
2450
2451 if (!$error) {
2452 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2453 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2454 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2455
2456 // We remove directory
2457 $ref = dol_sanitizeFileName($this->ref);
2458 if ($conf->fournisseur->commande->dir_output) {
2459 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2460 $file = $dir."/".$ref.".pdf";
2461 if (file_exists($file)) {
2462 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2463 $this->error = 'ErrorFailToDeleteFile';
2464 $this->errors[] = 'ErrorFailToDeleteFile';
2465 $error++;
2466 }
2467 }
2468 if (file_exists($dir)) {
2469 $res = @dol_delete_dir_recursive($dir);
2470 if (!$res) {
2471 $this->error = 'ErrorFailToDeleteDir';
2472 $this->errors[] = 'ErrorFailToDeleteDir';
2473 $error++;
2474 }
2475 }
2476 }
2477 }
2478
2479 if (!$error) {
2480 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2481 $this->db->commit();
2482 return 1;
2483 } else {
2484 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2485 $this->db->rollback();
2486 return -$error;
2487 }
2488 }
2489
2490
2499 public function getDispachedLines($status = -1)
2500 {
2501 $ret = array();
2502
2503 // List of already dispatched lines
2504 $sql = "SELECT p.ref, p.label,";
2505 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2506 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2507 $sql .= " FROM ".$this->db->prefix()."product as p,";
2508 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2509 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2510 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2511 $sql .= " AND cfd.fk_product = p.rowid";
2512 if ($status >= 0) {
2513 $sql .= " AND cfd.status = ".((int) $status);
2514 }
2515 $sql .= " ORDER BY cfd.rowid ASC";
2516
2517 $resql = $this->db->query($sql);
2518 if ($resql) {
2519 $num = $this->db->num_rows($resql);
2520 $i = 0;
2521
2522 while ($i < $num) {
2523 $objp = $this->db->fetch_object($resql);
2524 if ($objp) {
2525 $ret[] = array(
2526 'id' => $objp->dispatchedlineid,
2527 'productid' => $objp->fk_product,
2528 'warehouseid' => $objp->warehouse_id,
2529 'qty' => $objp->qty,
2530 'orderlineid' => $objp->fk_elementdet
2531 );
2532 }
2533
2534 $i++;
2535 }
2536 } else {
2537 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2538 }
2539
2540 return $ret;
2541 }
2542
2543 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2553 public function Livraison($user, $date, $type, $comment)
2554 {
2555 // phpcs:enable
2556 global $conf, $langs;
2557
2558 $result = 0;
2559 $error = 0;
2560
2561 dol_syslog(get_class($this)."::Livraison");
2562
2563 $usercanreceive = 0;
2564 if (!isModEnabled('reception')) {
2565 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2566 } else {
2567 $usercanreceive = $user->hasRight("reception", "creer");
2568 }
2569
2570 if ($usercanreceive) {
2571 // Define the new status
2572 if ($type == 'par') {
2574 } elseif ($type == 'tot') {
2576 } elseif ($type == 'nev') {
2578 } elseif ($type == 'can') {
2580 } else {
2581 $error++;
2582 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2583 return -2;
2584 }
2585
2586 // Some checks to accept the record
2587 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2588 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2589 if (!$error && ($type == 'tot')) {
2590 $dispatchedlinearray = $this->getDispachedLines(0);
2591 if (count($dispatchedlinearray) > 0) {
2592 $result = -1;
2593 $error++;
2594 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2595 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2596 }
2597 }
2598 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)
2599 $dispatcheddenied = $this->getDispachedLines(2);
2600 if (count($dispatchedlinearray) > 0) {
2601 $result = -1;
2602 $error++;
2603 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2604 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2605 }
2606 }
2607 }
2608
2609 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2610
2611 if (empty($error)) {
2612 $this->db->begin();
2613
2614 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2615 $sql .= " SET fk_statut = ".((int) $statut);
2616 $sql .= " WHERE rowid = ".((int) $this->id);
2617 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2618
2619 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2620 $resql = $this->db->query($sql);
2621 if ($resql) {
2622 $result = 1;
2623 $old_statut = $this->statut;
2624 $this->statut = $statut;
2625 $this->context['actionmsg2'] = $comment;
2626
2627 // Call trigger
2628 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2629 if ($result_trigger < 0) {
2630 $error++;
2631 }
2632 // End call triggers
2633
2634 if (empty($error)) {
2635 $this->db->commit();
2636 } else {
2637 $this->statut = $old_statut;
2638 $this->db->rollback();
2639 $this->error = $this->db->lasterror();
2640 $result = -1;
2641 }
2642 } else {
2643 $this->db->rollback();
2644 $this->error = $this->db->lasterror();
2645 $result = -1;
2646 }
2647 }
2648 } else {
2649 $this->error = $langs->trans('NotAuthorized');
2650 $this->errors[] = $langs->trans('NotAuthorized');
2651 dol_syslog(get_class($this)."::Livraison Not Authorized");
2652 $result = -3;
2653 }
2654 return $result;
2655 }
2656
2657 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2667 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2668 {
2669 // phpcs:enable
2670 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2671 }
2672
2681 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2682 {
2683 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2684 $error = 0;
2685
2686 $this->db->begin();
2687
2688 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2689 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2690 $sql .= " WHERE rowid = ".((int) $this->id);
2691
2692 dol_syslog(__METHOD__, LOG_DEBUG);
2693 $resql = $this->db->query($sql);
2694 if (!$resql) {
2695 $this->errors[] = $this->db->error();
2696 $error++;
2697 }
2698
2699 if (!$error) {
2700 $this->oldcopy = clone $this;
2701 $this->delivery_date = $delivery_date;
2702 }
2703
2704 if (!$notrigger && empty($error)) {
2705 // Call trigger
2706 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2707 if ($result < 0) {
2708 $error++;
2709 }
2710 // End call triggers
2711 }
2712
2713 if (!$error) {
2714 $this->db->commit();
2715 return 1;
2716 } else {
2717 foreach ($this->errors as $errmsg) {
2718 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2719 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2720 }
2721 $this->db->rollback();
2722 return -1 * $error;
2723 }
2724 } else {
2725 return -2;
2726 }
2727 }
2728
2729 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2738 public function set_id_projet($user, $id_projet, $notrigger = 0)
2739 {
2740 // phpcs:enable
2741 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2742 $error = 0;
2743
2744 $this->db->begin();
2745
2746 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2747 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2748 $sql .= " WHERE rowid = ".((int) $this->id);
2749
2750 dol_syslog(__METHOD__, LOG_DEBUG);
2751 $resql = $this->db->query($sql);
2752 if (!$resql) {
2753 $this->errors[] = $this->db->error();
2754 $error++;
2755 }
2756
2757 if (!$error) {
2758 $this->oldcopy = clone $this;
2759 $this->fk_projet = $id_projet;
2760 $this->fk_project = $id_projet;
2761 }
2762
2763 if (!$notrigger && empty($error)) {
2764 // Call trigger
2765 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2766 if ($result < 0) {
2767 $error++;
2768 }
2769 // End call triggers
2770 }
2771
2772 if (!$error) {
2773 $this->db->commit();
2774 return 1;
2775 } else {
2776 foreach ($this->errors as $errmsg) {
2777 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2778 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2779 }
2780 $this->db->rollback();
2781 return -1 * $error;
2782 }
2783 } else {
2784 return -2;
2785 }
2786 }
2787
2796 public function updateFromCommandeClient($user, $idc, $comclientid)
2797 {
2798 $comclient = new Commande($this->db);
2799 $comclient->fetch($comclientid);
2800
2801 $this->id = $idc;
2802
2803 $this->lines = array();
2804
2805 $num = count($comclient->lines);
2806 for ($i = 0; $i < $num; $i++) {
2807 $prod = new Product($this->db);
2808 $label = '';
2809 $ref = '';
2810 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2811 $label = $prod->label;
2812 $ref = $prod->ref;
2813 }
2814
2815 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2816 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2817 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2818 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2819 $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);
2820 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2821 if ($this->db->query($sql)) {
2822 $this->update_price(1);
2823 }
2824 }
2825
2826 return 1;
2827 }
2828
2836 public function setStatus($user, $status)
2837 {
2838 global $conf, $langs;
2839 $error = 0;
2840
2841 $this->db->begin();
2842
2843 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2844 $sql .= " SET fk_statut = ".$status;
2845 $sql .= " WHERE rowid = ".((int) $this->id);
2846
2847 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2848 $resql = $this->db->query($sql);
2849 if ($resql) {
2850 // Trigger names for each status
2851 $triggerName = array();
2852 $triggerName[0] = 'DRAFT';
2853 $triggerName[1] = 'VALIDATED';
2854 $triggerName[2] = 'APPROVED';
2855 $triggerName[3] = 'ORDERED'; // Ordered
2856 $triggerName[4] = 'RECEIVED_PARTIALLY';
2857 $triggerName[5] = 'RECEIVED_COMPLETELY';
2858 $triggerName[6] = 'CANCELED';
2859 $triggerName[7] = 'CANCELED';
2860 $triggerName[9] = 'REFUSED';
2861
2862 // Call trigger
2863 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2864 if ($result < 0) {
2865 $error++;
2866 }
2867 // End call triggers
2868 } else {
2869 $error++;
2870 $this->error = $this->db->lasterror();
2871 dol_syslog(get_class($this)."::setStatus ".$this->error);
2872 }
2873
2874 if (!$error) {
2875 $this->statut = $status;
2876 $this->db->commit();
2877 return 1;
2878 } else {
2879 $this->db->rollback();
2880 return -1;
2881 }
2882 }
2883
2907 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 = '')
2908 {
2909 global $mysoc, $conf, $langs;
2910 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2911 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2912
2913 $error = 0;
2914
2915 if ($this->statut == self::STATUS_DRAFT) {
2916 // Clean parameters
2917 if (empty($qty)) {
2918 $qty = 0;
2919 }
2920 if (empty($info_bits)) {
2921 $info_bits = 0;
2922 }
2923 if (empty($txtva)) {
2924 $txtva = 0;
2925 }
2926 if (empty($txlocaltax1)) {
2927 $txlocaltax1 = 0;
2928 }
2929 if (empty($txlocaltax2)) {
2930 $txlocaltax2 = 0;
2931 }
2932 if (empty($remise_percent)) {
2933 $remise_percent = 0;
2934 }
2935
2936 $remise_percent = (float) price2num($remise_percent);
2937 $qty = price2num($qty);
2938 if (!$qty) {
2939 $qty = 1;
2940 }
2941 $pu = price2num($pu);
2942 $pu_ht_devise = price2num($pu_ht_devise);
2943 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2944 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2945 }
2946 $txlocaltax1 = (float) price2num($txlocaltax1);
2947 $txlocaltax2 = (float) price2num($txlocaltax2);
2948
2949 // Check parameters
2950 if ($type < 0) {
2951 return -1;
2952 }
2953 if ($date_start && $date_end && $date_start > $date_end) {
2954 $langs->load("errors");
2955 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2956 return -1;
2957 }
2958
2959 $this->db->begin();
2960
2961 // Calcul du total TTC et de la TVA pour la ligne a partir de
2962 // qty, pu, remise_percent et txtva
2963 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2964 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2965
2966 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2967
2968 // Clean vat code
2969 $reg = array();
2970 $vat_src_code = '';
2971 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2972 $vat_src_code = $reg[1];
2973 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2974 }
2975
2976 $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);
2977 $total_ht = $tabprice[0];
2978 $total_tva = $tabprice[1];
2979 $total_ttc = $tabprice[2];
2980 $total_localtax1 = $tabprice[9];
2981 $total_localtax2 = $tabprice[10];
2982 $pu_ht = $tabprice[3];
2983 $pu_tva = $tabprice[4];
2984 $pu_ttc = $tabprice[5];
2985
2986 // MultiCurrency
2987 $multicurrency_total_ht = $tabprice[16];
2988 $multicurrency_total_tva = $tabprice[17];
2989 $multicurrency_total_ttc = $tabprice[18];
2990 $pu_ht_devise = $tabprice[19];
2991
2992 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2993 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2994
2995 //Fetch current line from the database and then clone the object and set it in $oldline property
2996 $this->line = new CommandeFournisseurLigne($this->db);
2997 $this->line->fetch($rowid);
2998
2999 $oldline = clone $this->line;
3000 $this->line->oldline = $oldline;
3001
3002 $this->line->context = $this->context;
3003
3004 $this->line->fk_commande = $this->id;
3005 //$this->line->label=$label;
3006 $this->line->desc = $desc;
3007
3008 // redefine quantity according to packaging
3009 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3010 if ($qty < $this->line->packaging) {
3011 $qty = $this->line->packaging;
3012 } else {
3013 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
3014 $coeff = intval($qty / $this->line->packaging) + 1;
3015 $qty = $this->line->packaging * $coeff;
3016 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3017 }
3018 }
3019 }
3020
3021 $this->line->qty = $qty;
3022 $this->line->ref_supplier = $ref_supplier;
3023
3024 $this->line->vat_src_code = $vat_src_code;
3025 $this->line->tva_tx = $txtva;
3026 $this->line->localtax1_tx = $txlocaltax1;
3027 $this->line->localtax2_tx = $txlocaltax2;
3028 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3029 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3030 $this->line->remise_percent = $remise_percent;
3031 $this->line->subprice = $pu_ht;
3032 $this->line->info_bits = $info_bits;
3033 $this->line->total_ht = $total_ht;
3034 $this->line->total_tva = $total_tva;
3035 $this->line->total_localtax1 = $total_localtax1;
3036 $this->line->total_localtax2 = $total_localtax2;
3037 $this->line->total_ttc = $total_ttc;
3038 $this->line->product_type = $type;
3039 $this->line->special_code = $oldline->special_code;
3040 $this->line->rang = $oldline->rang;
3041 $this->line->origin = $this->origin;
3042 $this->line->fk_unit = $fk_unit;
3043
3044 $this->line->date_start = $date_start;
3045 $this->line->date_end = $date_end;
3046
3047 // Multicurrency
3048 $this->line->fk_multicurrency = $this->fk_multicurrency;
3049 $this->line->multicurrency_code = $this->multicurrency_code;
3050 $this->line->multicurrency_subprice = $pu_ht_devise;
3051 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3052 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3053 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3054
3055 $this->line->subprice = $pu_ht;
3056 $this->line->price = $this->line->subprice;
3057
3058 $this->line->remise_percent = $remise_percent;
3059
3060 if (is_array($array_options) && count($array_options) > 0) {
3061 // We replace values in this->line->array_options only for entries defined into $array_options
3062 foreach ($array_options as $key => $value) {
3063 $this->line->array_options[$key] = $array_options[$key];
3064 }
3065 }
3066
3067 $result = $this->line->update($notrigger);
3068
3069
3070 // Mise a jour info denormalisees au niveau facture
3071 if ($result >= 0) {
3072 $this->update_price('1', 'auto');
3073 $this->db->commit();
3074 return $result;
3075 } else {
3076 $this->error = $this->db->lasterror();
3077 $this->db->rollback();
3078 return -1;
3079 }
3080 } else {
3081 $this->error = "Order status makes operation forbidden";
3082 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3083 return -2;
3084 }
3085 }
3086
3087
3095 public function initAsSpecimen()
3096 {
3097 global $user, $langs, $conf;
3098
3099 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3100
3101 dol_syslog(get_class($this)."::initAsSpecimen");
3102
3103 $now = dol_now();
3104
3105 // Find first product
3106 $prodid = 0;
3107 $product = new ProductFournisseur($this->db);
3108 $sql = "SELECT rowid";
3109 $sql .= " FROM ".$this->db->prefix()."product";
3110 $sql .= " WHERE entity IN (".getEntity('product').")";
3111 $sql .= $this->db->order("rowid", "ASC");
3112 $sql .= $this->db->plimit(1);
3113 $resql = $this->db->query($sql);
3114 if ($resql && $this->db->num_rows($resql)) {
3115 $obj = $this->db->fetch_object($resql);
3116 $prodid = $obj->rowid;
3117 }
3118
3119 // Initialise parameters
3120 $this->id = 0;
3121 $this->ref = 'SPECIMEN';
3122 $this->specimen = 1;
3123 $this->socid = 1;
3124 $this->date = $now;
3125 $this->date_commande = $now;
3126 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3127 $this->cond_reglement_code = 'RECEP';
3128 $this->mode_reglement_code = 'CHQ';
3129
3130 $this->note_public = 'This is a comment (public)';
3131 $this->note_private = 'This is a comment (private)';
3132
3133 $this->multicurrency_tx = 1;
3134 $this->multicurrency_code = $conf->currency;
3135
3136 $this->statut = 0;
3137
3138 // Lines
3139 $nbp = 5;
3140 $xnbp = 0;
3141 while ($xnbp < $nbp) {
3142 $line = new CommandeFournisseurLigne($this->db);
3143 $line->desc = $langs->trans("Description")." ".$xnbp;
3144 $line->qty = 1;
3145 $line->subprice = 100;
3146 $line->tva_tx = 19.6;
3147 $line->localtax1_tx = 0;
3148 $line->localtax2_tx = 0;
3149 if ($xnbp == 2) {
3150 $line->total_ht = 50;
3151 $line->total_ttc = 59.8;
3152 $line->total_tva = 9.8;
3153 $line->remise_percent = 50;
3154 } else {
3155 $line->total_ht = 100;
3156 $line->total_ttc = 119.6;
3157 $line->total_tva = 19.6;
3158 $line->remise_percent = 00;
3159 }
3160 $line->fk_product = $prodid;
3161
3162 $this->lines[$xnbp] = $line;
3163
3164 $this->total_ht += $line->total_ht;
3165 $this->total_tva += $line->total_tva;
3166 $this->total_ttc += $line->total_ttc;
3167
3168 $xnbp++;
3169 }
3170
3171 return 1;
3172 }
3173
3180 public function info($id)
3181 {
3182 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3183 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3184 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3185 $sql .= ' WHERE c.rowid = '.((int) $id);
3186
3187 $result = $this->db->query($sql);
3188 if ($result) {
3189 if ($this->db->num_rows($result)) {
3190 $obj = $this->db->fetch_object($result);
3191
3192 $this->id = $obj->rowid;
3193
3194 $this->user_creation_id = $obj->fk_user_author;
3195 $this->user_validation_id = $obj->fk_user_valid;
3196 $this->user_modification_id = $obj->fk_user_modif;
3197 $this->user_approve_id = $obj->fk_user_approve;
3198 $this->user_approve_id2 = $obj->fk_user_approve2;
3199
3200 $this->date_creation = $this->db->jdate($obj->datec);
3201 $this->date_modification = $this->db->jdate($obj->datem);
3202 $this->date_approve = $this->db->jdate($obj->datea);
3203 $this->date_approve2 = $this->db->jdate($obj->datea2);
3204 $this->date_validation = $this->db->jdate($obj->date_validation);
3205 }
3206 $this->db->free($result);
3207 } else {
3208 dol_print_error($this->db);
3209 }
3210 }
3211
3217 public function loadStateBoard()
3218 {
3219 global $conf, $user;
3220
3221 $this->nb = array();
3222 $clause = "WHERE";
3223
3224 $sql = "SELECT count(co.rowid) as nb";
3225 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3226 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3227 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3228 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3229 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3230 $clause = "AND";
3231 }
3232 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3233
3234 $resql = $this->db->query($sql);
3235 if ($resql) {
3236 while ($obj = $this->db->fetch_object($resql)) {
3237 $this->nb["supplier_orders"] = $obj->nb;
3238 }
3239 $this->db->free($resql);
3240 return 1;
3241 } else {
3242 dol_print_error($this->db);
3243 $this->error = $this->db->error();
3244 return -1;
3245 }
3246 }
3247
3248 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3256 public function load_board($user, $mode = 'opened')
3257 {
3258 // phpcs:enable
3259 global $conf, $langs;
3260
3261 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3262 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3263 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir") && !$user->socid) {
3264 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3265 }
3266 $sql .= " WHERE c.entity = ".$conf->entity;
3267 if ($mode === 'awaiting') {
3268 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3269 } else {
3270 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3271 }
3272 if ($user->socid) {
3273 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3274 }
3275
3276 $resql = $this->db->query($sql);
3277 if ($resql) {
3278 $commandestatic = new CommandeFournisseur($this->db);
3279
3280 $response = new WorkboardResponse();
3281 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3282 $response->label = $langs->trans("SuppliersOrdersToProcess");
3283 $response->labelShort = $langs->trans("Opened");
3284 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3285 $response->img = img_object('', "order");
3286
3287 if ($mode === 'awaiting') {
3288 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3289 $response->labelShort = $langs->trans("AwaitingReception");
3290 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3291 }
3292
3293 while ($obj = $this->db->fetch_object($resql)) {
3294 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3295 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3296 $commandestatic->statut = $obj->fk_statut;
3297
3298 $response->nbtodo++;
3299 $response->total += $obj->total_ht;
3300
3301 if ($commandestatic->hasDelay()) {
3302 $response->nbtodolate++;
3303 }
3304 }
3305
3306 return $response;
3307 } else {
3308 $this->error = $this->db->error();
3309 return -1;
3310 }
3311 }
3312
3319 public function getInputMethod()
3320 {
3321 global $langs;
3322
3323 if ($this->methode_commande_id > 0) {
3324 $sql = "SELECT rowid, code, libelle as label";
3325 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3326 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3327
3328 $resql = $this->db->query($sql);
3329 if ($resql) {
3330 if ($this->db->num_rows($resql)) {
3331 $obj = $this->db->fetch_object($resql);
3332
3333 $string = $langs->trans($obj->code);
3334 if ($string == $obj->code) {
3335 $string = $obj->label != '-' ? $obj->label : '';
3336 }
3337 return $string;
3338 }
3339 } else {
3340 dol_print_error($this->db);
3341 }
3342 }
3343
3344 return '';
3345 }
3346
3358 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3359 {
3360 global $conf, $langs;
3361
3362 if (!dol_strlen($modele)) {
3363 $modele = ''; // No doc template/generation by default
3364
3365 if (!empty($this->model_pdf)) {
3366 $modele = $this->model_pdf;
3367 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3368 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3369 }
3370 }
3371
3372 if (empty($modele)) {
3373 return 0;
3374 } else {
3375 $langs->load("suppliers");
3376 $outputlangs->load("products");
3377
3378 $modelpath = "core/modules/supplier_order/doc/";
3379 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3380 return $result;
3381 }
3382 }
3383
3390 public function getMaxDeliveryTimeDay($langs)
3391 {
3392 if (empty($this->lines)) {
3393 return '';
3394 }
3395
3396 $tmpproductfourn = new ProductFournisseur($this->db);
3397
3398 $nb = 0;
3399 foreach ($this->lines as $line) {
3400 if ($line->fk_product > 0) {
3401 // Load delivery_time_days, return id into product_fournisseur_price
3402 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3403 if ($idp > 0) {
3404 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3405 if ($tmpproductfourn->delivery_time_days > $nb) {
3406 $nb = $tmpproductfourn->delivery_time_days;
3407 }
3408 }
3409 }
3410 }
3411
3412 if ($nb === 0) {
3413 return '';
3414 } else {
3415 return $nb.' '.$langs->trans('days');
3416 }
3417 }
3418
3424 public function getRights()
3425 {
3426 global $user;
3427
3428 return $user->hasRight("fournisseur", "commande");
3429 }
3430
3431
3440 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3441 {
3442 $tables = array(
3443 'commande_fournisseur'
3444 );
3445
3446 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3447 }
3448
3457 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3458 {
3459 $tables = array(
3460 'commande_fournisseurdet'
3461 );
3462
3463 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3464 }
3465
3473 public function hasDelay()
3474 {
3475 global $conf;
3476
3477 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3478 $now = dol_now();
3479 if (!empty($this->delivery_date)) {
3480 $date_to_test = $this->delivery_date;
3481 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3482 } else {
3483 //$date_to_test = $this->date_commande;
3484 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3485 return false;
3486 }
3487 } else {
3488 $now = dol_now();
3489 $date_to_test = $this->date_commande;
3490
3491 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3492 }
3493 }
3494
3502 public function showDelay()
3503 {
3504 global $conf, $langs;
3505
3506 $langs->load('orders');
3507
3508 $text = '';
3509
3510 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3511 if (!empty($this->delivery_date)) {
3512 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3513 } else {
3514 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3515 }
3516 } else {
3517 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3518 }
3519 if ($text) {
3520 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3521 }
3522
3523 return $text;
3524 }
3525
3526
3535 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3536 {
3537 if (isModEnabled("supplier_order")) {
3538 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3539
3540 $qtydelivered = array();
3541 $qtywished = array();
3542
3543 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3544
3545 $filter = array('t.fk_element' => $this->id);
3546 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3547 $filter['t.status'] = 1; // Restrict to lines with status validated
3548 }
3549
3550 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3551 if ($ret < 0) {
3552 $this->error = $supplierorderdispatch->error;
3553 $this->errors = $supplierorderdispatch->errors;
3554 return $ret;
3555 } else {
3556 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3557 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3558 $date_liv = dol_now();
3559
3560 // Build array with quantity deliverd by product
3561 foreach ($supplierorderdispatch->lines as $line) {
3562 if (array_key_exists($line->fk_product, $qtydelivered)) {
3563 $qtydelivered[$line->fk_product] += $line->qty;
3564 } else {
3565 $qtydelivered[$line->fk_product] = $line->qty;
3566 }
3567 }
3568 foreach ($this->lines as $line) {
3569 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3570 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3571 continue;
3572 }
3573 if (array_key_exists($line->fk_product, $qtywished)) {
3574 $qtywished[$line->fk_product] += $line->qty;
3575 } else {
3576 $qtywished[$line->fk_product] = $line->qty;
3577 }
3578 }
3579
3580 //Compare array
3581 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3582 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3583 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3584 //var_dump(array_keys($qtydelivered));
3585 //var_dump(array_keys($qtywished));
3586 //var_dump($diff_array);
3587 //var_dump($keysinwishednotindelivered);
3588 //var_dump($keysindeliverednotinwished);
3589 //exit;
3590
3591 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3592 if ($closeopenorder) {
3593 //$ret=$this->setStatus($user,5);
3594 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3595 if ($ret < 0) {
3596 return -1;
3597 }
3598 return 5;
3599 } else {
3600 //Diff => received partially
3601 //$ret=$this->setStatus($user,4);
3602 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3603 if ($ret < 0) {
3604 return -1;
3605 }
3606 return 4;
3607 }
3608 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3609 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3610
3611 $close = 0;
3612
3613 if (count($diff_array) > 0) {
3614 //there are some difference between the two arrays
3615
3616 //scan the array of results
3617 foreach ($diff_array as $key => $value) {
3618 //if the quantity delivered is greater or equal to wish quantity
3619 if ($qtydelivered[$key] >= $qtywished[$key]) {
3620 $close++;
3621 }
3622 }
3623 }
3624
3625
3626 if ($close == count($diff_array)) {
3627 //all the products are received equal or more than the wished quantity
3628 if ($closeopenorder) {
3629 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3630 if ($ret < 0) {
3631 return -1;
3632 }
3633 return 5;
3634 } else {
3635 //Diff => received partially
3636 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3637 if ($ret < 0) {
3638 return -1;
3639 }
3640 return 4;
3641 }
3642 } else {
3643 //all the products are not received
3644 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3645 if ($ret < 0) {
3646 return -1;
3647 }
3648 return 4;
3649 }
3650 } else {
3651 //Diff => received partially
3652 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3653 if ($ret < 0) {
3654 return -1;
3655 }
3656 return 4;
3657 }
3658 }
3659 return 1;
3660 }
3661 }
3662 return 0;
3663 }
3664
3672 public function loadReceptions($filtre_statut = -1)
3673 {
3674 $this->receptions = array();
3675
3676 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3677
3678 $sql = 'SELECT cd.rowid, cd.fk_product,';
3679 $sql .= ' sum(cfd.qty) as qty';
3680 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3681 if ($filtre_statut >= 0) {
3682 $sql .= ' '.$this->db->prefix().'reception as e,';
3683 }
3684 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3685 $sql .= ' WHERE';
3686 if ($filtre_statut >= 0) {
3687 $sql .= ' cfd.fk_reception = e.rowid AND';
3688 }
3689 $sql .= ' cfd.fk_elementdet = cd.rowid';
3690 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3691 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3692 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3693 }
3694 if ($filtre_statut >= 0) {
3695 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3696 }
3697 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3698
3699 $resql = $this->db->query($sql);
3700 if ($resql) {
3701 $num = $this->db->num_rows($resql);
3702 $i = 0;
3703 while ($i < $num) {
3704 $obj = $this->db->fetch_object($resql);
3705 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3706 $i++;
3707 }
3708 $this->db->free($resql);
3709
3710 return $num;
3711 } else {
3712 $this->error = $this->db->lasterror();
3713 return -1;
3714 }
3715 }
3716
3724 public function getKanbanView($option = '', $arraydata = null)
3725 {
3726 global $langs;
3727
3728 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3729
3730 $return = '<div class="box-flex-item box-flex-grow-zero">';
3731 $return .= '<div class="info-box info-box-sm">';
3732 $return .= '<span class="info-box-icon bg-infobox-action">';
3733 $return .= img_picto('', $this->picto);
3734 $return .= '</span>';
3735 $return .= '<div class="info-box-content">';
3736 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3737 if ($selected >= 0) {
3738 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3739 }
3740 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3741 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3742 }
3743 if (property_exists($this, 'billed')) {
3744 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3745 }
3746 if (method_exists($this, 'getLibStatut')) {
3747 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3748 }
3749 $return .= '</div>';
3750 $return .= '</div>';
3751 $return .= '</div>';
3752 return $return;
3753 }
3754}
3755
3756
3757
3762{
3766 public $element = 'commande_fournisseurdet';
3767
3771 public $table_element = 'commande_fournisseurdet';
3772
3776 public $parent_element = 'commande_fournisseur';
3777
3781 public $fk_parent_attribute = 'fk_commande_fournisseur';
3782
3783 public $oldline;
3784
3789 public $fk_commande;
3790
3791 // From llx_commande_fournisseurdet
3795 public $fk_parent_line;
3796
3800 public $fk_facture;
3801
3802 public $rang = 0;
3803
3807 public $special_code = 0;
3808
3813 public $pu_ht;
3814
3815 public $date_start;
3816 public $date_end;
3817 public $fk_fournprice;
3818 public $packaging;
3819 public $pa_ht;
3820
3821 // From llx_product_fournisseur_price
3822
3827 public $ref_supplier;
3828
3834 public $ref_fourn;
3835
3836 public $remise;
3837
3838
3844 public function __construct($db)
3845 {
3846 $this->db = $db;
3847 }
3848
3855 public function fetch($rowid)
3856 {
3857 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3858 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3859 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3860 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3861 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3862 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3863 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3864 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3865 $sql .= ' c.fk_soc as socid';
3866 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
3867 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';
3868 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3869
3870 $result = $this->db->query($sql);
3871 if ($result) {
3872 $objp = $this->db->fetch_object($result);
3873
3874 if (!empty($objp)) {
3875 $this->rowid = $objp->rowid;
3876 $this->id = $objp->rowid;
3877 $this->fk_commande = $objp->fk_commande;
3878 $this->desc = $objp->description;
3879 $this->qty = $objp->qty;
3880 $this->ref_fourn = $objp->ref_supplier;
3881 $this->ref_supplier = $objp->ref_supplier;
3882 $this->subprice = $objp->subprice;
3883 $this->tva_tx = $objp->tva_tx;
3884 $this->localtax1_tx = $objp->localtax1_tx;
3885 $this->localtax2_tx = $objp->localtax2_tx;
3886 $this->localtax1_type = $objp->localtax1_type;
3887 $this->localtax2_type = $objp->localtax2_type;
3888 $this->remise = $objp->remise;
3889 $this->remise_percent = $objp->remise_percent;
3890 $this->fk_product = $objp->fk_product;
3891 $this->info_bits = $objp->info_bits;
3892 $this->total_ht = $objp->total_ht;
3893 $this->total_tva = $objp->total_tva;
3894 $this->total_localtax1 = $objp->total_localtax1;
3895 $this->total_localtax2 = $objp->total_localtax2;
3896 $this->total_ttc = $objp->total_ttc;
3897 $this->product_type = $objp->product_type;
3898 $this->special_code = $objp->special_code;
3899
3900 $this->ref = $objp->product_ref;
3901
3902 $this->product_ref = $objp->product_ref;
3903 $this->product_label = $objp->product_label;
3904 $this->product_desc = $objp->product_desc;
3905
3906 if (getDolGlobalInt('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3907 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3908 // Move this into another method and call it when required.
3909
3910 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3911 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
3912 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3913 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3914 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3915 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3916 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3917 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3918 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3919 $sqlsearchpackage .= " LIMIT 1";
3920
3921 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3922 if ($resqlsearchpackage) {
3923 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3924 if ($objsearchpackage) {
3925 $this->fk_fournprice = $objsearchpackage->rowid;
3926 $this->packaging = $objsearchpackage->packaging;
3927 }
3928 } else {
3929 $this->error = $this->db->lasterror();
3930 return -1;
3931 }
3932 }
3933
3934 $this->date_start = $this->db->jdate($objp->date_start);
3935 $this->date_end = $this->db->jdate($objp->date_end);
3936 $this->fk_unit = $objp->fk_unit;
3937
3938 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3939 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3940 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3941 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3942
3943 $this->fetch_optionals();
3944
3945 $this->db->free($result);
3946 return 1;
3947 } else {
3948 $this->error = 'Supplier order line with id='.$rowid.' not found';
3949 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3950 return 0;
3951 }
3952 } else {
3953 dol_print_error($this->db);
3954 return -1;
3955 }
3956 }
3957
3964 public function insert($notrigger = 0)
3965 {
3966 global $conf, $user;
3967
3968 $error = 0;
3969
3970 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3971
3972 // Clean parameters
3973 if (empty($this->tva_tx)) {
3974 $this->tva_tx = 0;
3975 }
3976 if (empty($this->localtax1_tx)) {
3977 $this->localtax1_tx = 0;
3978 }
3979 if (empty($this->localtax2_tx)) {
3980 $this->localtax2_tx = 0;
3981 }
3982 if (empty($this->localtax1_type)) {
3983 $this->localtax1_type = '0';
3984 }
3985 if (empty($this->localtax2_type)) {
3986 $this->localtax2_type = '0';
3987 }
3988 if (empty($this->total_localtax1)) {
3989 $this->total_localtax1 = 0;
3990 }
3991 if (empty($this->total_localtax2)) {
3992 $this->total_localtax2 = 0;
3993 }
3994 if (empty($this->rang)) {
3995 $this->rang = 0;
3996 }
3997 if (empty($this->remise_percent)) {
3998 $this->remise_percent = 0;
3999 }
4000 if (empty($this->info_bits)) {
4001 $this->info_bits = 0;
4002 }
4003 if (empty($this->special_code)) {
4004 $this->special_code = 0;
4005 }
4006 if (empty($this->fk_parent_line)) {
4007 $this->fk_parent_line = 0;
4008 }
4009 if (empty($this->pa_ht)) {
4010 $this->pa_ht = 0;
4011 }
4012
4013 // Multicurrency
4014 if (!empty($this->multicurrency_code)) {
4015 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
4016 }
4017 if (empty($this->fk_multicurrency)) {
4018 $this->multicurrency_code = $conf->currency;
4019 $this->fk_multicurrency = 0;
4020 $this->multicurrency_tx = 1;
4021 }
4022
4023 // Check parameters
4024 if ($this->product_type < 0) {
4025 return -1;
4026 }
4027
4028 $this->db->begin();
4029
4030 // Insertion dans base de la ligne
4031 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element;
4032 $sql .= " (fk_commande, label, description, date_start, date_end,";
4033 $sql .= " fk_product, product_type, special_code, rang,";
4034 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
4035 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
4036 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,";
4037 $sql .= " fk_parent_line)";
4038 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
4039 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4040 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4041 if ($this->fk_product) {
4042 $sql .= $this->fk_product.",";
4043 } else {
4044 $sql .= "null,";
4045 }
4046 $sql .= "'".$this->db->escape($this->product_type)."',";
4047 $sql .= (int) $this->special_code . ",";
4048 $sql .= "'".$this->db->escape($this->rang)."',";
4049 $sql .= "'".$this->db->escape($this->qty)."', ";
4050 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4051 $sql .= " ".price2num($this->tva_tx).", ";
4052 $sql .= " ".price2num($this->localtax1_tx).",";
4053 $sql .= " ".price2num($this->localtax2_tx).",";
4054 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4055 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4056 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
4057 $sql .= " ".price2num($this->total_ht).",";
4058 $sql .= " ".price2num($this->total_tva).",";
4059 $sql .= " ".price2num($this->total_localtax1).",";
4060 $sql .= " ".price2num($this->total_localtax2).",";
4061 $sql .= " ".price2num($this->total_ttc).",";
4062 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
4063 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
4064 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4065 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
4066 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
4067 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
4068 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
4069 $sql .= ", ".((!empty($this->fk_parent_line) && $this->fk_parent_line > 0) ? $this->fk_parent_line : 'null');
4070 $sql .= ")";
4071
4072 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4073 $resql = $this->db->query($sql);
4074 if ($resql) {
4075 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
4076 $this->rowid = $this->id;
4077
4078 if (!$error) {
4079 $result = $this->insertExtraFields();
4080 if ($result < 0) {
4081 $error++;
4082 }
4083 }
4084
4085 if (!$error && !$notrigger) {
4086 // Call trigger
4087 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
4088 if ($result < 0) {
4089 $error++;
4090 }
4091 // End call triggers
4092 }
4093
4094 if (!$error) {
4095 $this->db->commit();
4096 return 1;
4097 }
4098
4099 foreach ($this->errors as $errmsg) {
4100 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4101 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4102 }
4103 $this->db->rollback();
4104 return -1 * $error;
4105 } else {
4106 $this->errors[] = $this->db->error();
4107 $this->db->rollback();
4108 return -2;
4109 }
4110 }
4111
4118 public function update($notrigger = 0)
4119 {
4120 global $user;
4121
4122 $error = 0;
4123
4124 $this->db->begin();
4125
4126 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
4127 $sql .= " description='".$this->db->escape($this->desc)."'";
4128 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4129 $sql .= ", subprice='".price2num($this->subprice)."'";
4130 //$sql.= ",remise='".price2num($remise)."'";
4131 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4132
4133 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4134 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4135 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4136 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4137 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4138 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4139 $sql .= ", qty='".price2num($this->qty)."'";
4140 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4141 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4142 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4143 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4144 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4145 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4146 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4147 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4148 $sql .= ", product_type=".$this->product_type;
4149 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4150 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4151
4152 // Multicurrency
4153 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4154 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4155 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4156 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4157
4158 $sql .= " WHERE rowid = ".((int) $this->id);
4159
4160 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4161 $resql = $this->db->query($sql);
4162 if ($resql) {
4163 if (!$error) {
4164 $result = $this->insertExtraFields();
4165 if ($result < 0) {
4166 $error++;
4167 }
4168 }
4169
4170 if (!$error && !$notrigger) {
4171 // Call trigger
4172 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4173 if ($result < 0) {
4174 $this->db->rollback();
4175 return -1;
4176 }
4177 // End call triggers
4178 }
4179
4180 if (!$error) {
4181 $this->db->commit();
4182 return 1;
4183 } else {
4184 $this->db->rollback();
4185 return -1;
4186 }
4187 } else {
4188 $this->error = $this->db->lasterror();
4189 $this->db->rollback();
4190 return -1;
4191 }
4192 }
4193
4201 public function delete($user, $notrigger = 0)
4202 {
4203 if (empty($user)) {
4204 global $user;
4205 }
4206
4207 $error = 0;
4208
4209 $this->db->begin();
4210
4211 // extrafields
4212 $result = $this->deleteExtraFields();
4213 if ($result < 0) {
4214 $this->db->rollback();
4215 return -1;
4216 }
4217
4218 $sql1 = 'UPDATE '.$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4219 $resql = $this->db->query($sql1);
4220 if (!$resql) {
4221 $this->db->rollback();
4222 return -1;
4223 }
4224
4225 $sql2 = 'DELETE FROM '.$this->db->prefix()."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4226
4227 dol_syslog(__METHOD__, LOG_DEBUG);
4228 $resql = $this->db->query($sql2);
4229 if ($resql) {
4230 if (!$notrigger) {
4231 // Call trigger
4232 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4233 if ($result < 0) {
4234 $error++;
4235 }
4236 // End call triggers
4237 }
4238
4239 if (!$error) {
4240 $this->db->commit();
4241 return 1;
4242 }
4243
4244 foreach ($this->errors as $errmsg) {
4245 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4246 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4247 }
4248 $this->db->rollback();
4249 return -1 * $error;
4250 } else {
4251 $this->error = $this->db->lasterror();
4252 return -1;
4253 }
4254 }
4255}
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:2010
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:2010