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