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