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