dolibarr 18.0.6
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-2023 Frédéric France <frederic.france@netlogic.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 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
37require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
38require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
39require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
40if (isModEnabled('productbatch')) {
41 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
42}
43
44
49{
53 public $element = 'order_supplier';
54
58 public $table_element = 'commande_fournisseur';
59
63 public $table_element_line = 'commande_fournisseurdet';
64
68 public $fk_element = 'fk_commande';
69
73 public $picto = 'supplier_order';
74
79 public $ismultientitymanaged = 1;
80
85 public $restrictiononfksoc = 1;
86
90 protected $table_ref_field = 'ref';
91
95 public $id;
96
101 public $ref;
102
106 public $ref_supplier;
107
113 public $ref_fourn;
114
115 public $brouillon;
119 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process runing -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
120 // -> 7=Canceled/Never received -> (reopen) 3=Process runing
121 // -> 6=Canceled -> (reopen) 2=Approved
122 // -> 9=Refused -> (reopen) 1=Validated
123 // Note: billed or not is on another field "billed"
124
128 public $statuts;
129
133 public $statuts_short;
134
135 public $billed;
136
137 public $socid;
138 public $fourn_id;
139 public $date;
140 public $date_creation;
141 public $date_valid;
142 public $date_approve;
143 public $date_approve2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
144 public $date_commande;
145
150 public $date_livraison;
151
155 public $delivery_date;
156
157 public $total_ht;
158 public $total_tva;
159 public $total_localtax1; // Total Local tax 1
160 public $total_localtax2; // Total Local tax 2
161 public $total_ttc;
162 public $source;
163
167 public $fk_project;
168
169 public $cond_reglement_id;
170 public $cond_reglement_code;
171 public $cond_reglement_label; // Label
172 public $cond_reglement_doc; // Label on documents
173
177 public $fk_account;
178
182 public $mode_reglement_id;
183
187 public $mode_reglement_code;
188
192 public $mode_reglement;
193 public $user_author_id;
194 public $user_valid_id;
195 public $user_approve_id;
196 public $user_approve_id2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
197
198 public $refuse_note;
199
200 public $extraparams = array();
201
205 public $lines = array();
206
210 public $line;
211
212 // Add for supplier_proposal
213 public $origin;
214 public $origin_id;
215 public $linked_objects = array();
216
217 // Multicurrency
221 public $fk_multicurrency;
222
223 public $multicurrency_code;
224 public $multicurrency_tx;
225 public $multicurrency_total_ht;
226 public $multicurrency_total_tva;
227 public $multicurrency_total_ttc;
228
229
257 public $fields = array(
258 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>10),
259 'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>25, 'searchall'=>1),
260 'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>35),
261 'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefOrderSupplierShort', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'searchall'=>1),
262 'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>45),
263 'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>710),
264 'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>720),
265 'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>725),
266 'date_commande' =>array('type'=>'date', 'label'=>'OrderDateShort', 'enabled'=>1, 'visible'=>1, 'position'=>70),
267 'date_livraison' =>array('type'=>'datetime', 'label'=>'DeliveryDate', 'enabled'=>'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible'=>1, 'position'=>74),
268 'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>41),
269 'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>3, 'notnull'=>-1, 'position'=>80),
270 'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>711),
271 'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>721),
272 'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>726),
273 'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>100),
274 'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>710),
275 'total_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
276 'total_tva' =>array('type'=>'double(24,8)', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>1, 'position'=>135, 'isameasure'=>1),
277 'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LT1', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
278 'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LT2', 'enabled'=>1, 'visible'=>3, 'position'=>145, 'isameasure'=>1),
279 'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
280 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>750, 'searchall'=>1),
281 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>760, 'searchall'=>1),
282 'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>165),
283 'fk_input_method' =>array('type'=>'integer', 'label'=>'OrderMode', 'enabled'=>1, 'visible'=>3, 'position'=>170),
284 'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>3, 'position'=>175),
285 'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>3, 'position'=>180),
286 'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>0, 'position'=>190),
287 'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>3, 'position'=>200),
288 'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>3, 'position'=>205),
289 'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>3, 'position'=>210),
290 'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>0, 'position'=>215),
291 'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Currency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>220),
292 'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'CurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>225),
293 'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>230),
294 'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>235),
295 'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
296 'date_creation' =>array('type'=>'datetime', 'label'=>'Date creation', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
297 'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>1, 'notnull'=>1, 'position'=>50),
298 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1000, 'index'=>1),
299 'tms'=>array('type'=>'datetime', 'label'=>"DateModificationShort", 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
300 'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'position'=>700),
301 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>701),
302 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>0, 'position'=>900),
303 );
304
305
309 const STATUS_DRAFT = 0;
310
315
320
325
330
335
340
345
349 const STATUS_REFUSED = 9;
350
351
356
357
358
364 public function __construct($db)
365 {
366 $this->db = $db;
367 }
368
369
377 public function fetch($id, $ref = '')
378 {
379 global $conf;
380
381 // Check parameters
382 if (empty($id) && empty($ref)) {
383 return -1;
384 }
385
386 $sql = "SELECT c.rowid, c.entity, c.ref, ref_supplier, c.fk_soc, c.fk_statut, c.amount_ht, c.total_ht, c.total_ttc, c.total_tva,";
387 $sql .= " c.localtax1, c.localtax2, ";
388 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
389 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_approve, c.fk_user_approve2,";
390 $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,";
391 $sql .= " c.fk_account,";
392 $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
393 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
394 $sql .= " cm.libelle as methode_commande,";
395 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
396 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
397 $sql .= ', c.fk_incoterms, c.location_incoterms';
398 $sql .= ', i.libelle as label_incoterms';
399 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
400 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
401 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON c.fk_mode_reglement = p.id";
402 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_input_method as cm ON cm.rowid = c.fk_input_method";
403 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
404
405 if (empty($id)) {
406 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
407 } else {
408 $sql .= " WHERE c.rowid=".((int) $id);
409 }
410
411 if ($ref) {
412 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
413 }
414
415 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
416 $resql = $this->db->query($sql);
417 if ($resql) {
418 $obj = $this->db->fetch_object($resql);
419 if (!$obj) {
420 $this->error = 'Bill with id '.$id.' not found';
421 dol_syslog(get_class($this).'::fetch '.$this->error);
422 return 0;
423 }
424
425 $this->id = $obj->rowid;
426 $this->entity = $obj->entity;
427
428 $this->ref = $obj->ref;
429 $this->ref_supplier = $obj->ref_supplier;
430 $this->socid = $obj->fk_soc;
431 $this->fourn_id = $obj->fk_soc;
432 $this->statut = $obj->fk_statut;
433 $this->status = $obj->fk_statut;
434 $this->billed = $obj->billed;
435 $this->user_author_id = $obj->fk_user_author;
436 $this->user_valid_id = $obj->fk_user_valid;
437 $this->user_approve_id = $obj->fk_user_approve;
438 $this->user_approve_id2 = $obj->fk_user_approve2;
439 $this->total_ht = $obj->total_ht;
440 $this->total_tva = $obj->total_tva;
441 $this->total_localtax1 = $obj->localtax1;
442 $this->total_localtax2 = $obj->localtax2;
443 $this->total_ttc = $obj->total_ttc;
444 $this->date_creation = $this->db->jdate($obj->date_creation);
445 $this->date_valid = $this->db->jdate($obj->date_valid);
446 $this->date_approve = $this->db->jdate($obj->date_approve);
447 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
448 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
449 if (isset($obj->date_commande)) {
450 $this->date = $this->date_commande;
451 } else {
452 $this->date = $this->date_creation;
453 }
454 $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
455 $this->delivery_date = $this->db->jdate($obj->delivery_date);
456 $this->remise_percent = $obj->remise_percent;
457 $this->methode_commande_id = $obj->fk_input_method;
458 $this->methode_commande = $obj->methode_commande;
459
460 $this->source = $obj->source;
461 $this->fk_project = $obj->fk_project;
462 $this->cond_reglement_id = $obj->fk_cond_reglement;
463 $this->cond_reglement_code = $obj->cond_reglement_code;
464 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
465 $this->cond_reglement_label = $obj->cond_reglement_label;
466 $this->cond_reglement_doc = $obj->cond_reglement_doc;
467 $this->fk_account = $obj->fk_account;
468 $this->mode_reglement_id = $obj->fk_mode_reglement;
469 $this->mode_reglement_code = $obj->mode_reglement_code;
470 $this->mode_reglement = $obj->mode_reglement_libelle;
471 $this->note = $obj->note_private; // deprecated
472 $this->note_private = $obj->note_private;
473 $this->note_public = $obj->note_public;
474 $this->model_pdf = $obj->model_pdf;
475 $this->modelpdf = $obj->model_pdf; // deprecated
476
477 //Incoterms
478 $this->fk_incoterms = $obj->fk_incoterms;
479 $this->location_incoterms = $obj->location_incoterms;
480 $this->label_incoterms = $obj->label_incoterms;
481
482 // Multicurrency
483 $this->fk_multicurrency = $obj->fk_multicurrency;
484 $this->multicurrency_code = $obj->multicurrency_code;
485 $this->multicurrency_tx = $obj->multicurrency_tx;
486 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
487 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
488 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
489
490 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
491
492 $this->db->free($resql);
493
494 // Retrieve all extrafield
495 // fetch optionals attributes and labels
496 $this->fetch_optionals();
497
498 if ($this->statut == 0) {
499 $this->brouillon = 1;
500 }
501
502 /*
503 * Lines
504 */
505 $result = $this->fetch_lines();
506
507 if ($result < 0) {
508 return -1;
509 } else {
510 return 1;
511 }
512 } else {
513 $this->error = $this->db->error()." sql=".$sql;
514 return -1;
515 }
516 }
517
518 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
525 public function fetch_lines($only_product = 0)
526 {
527 global $conf;
528 // phpcs:enable
529
530 $this->lines = array();
531
532 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
533 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
534 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
535 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
536 $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,";
537 $sql .= " l.fk_unit,";
538 $sql .= " l.date_start, l.date_end,";
539 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
540 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
541 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
542 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
543 if ($only_product) {
544 $sql .= ' AND p.fk_product_type = 0';
545 }
546 $sql .= " ORDER BY l.rang, l.rowid";
547 //print $sql;
548
549 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
550
551 $result = $this->db->query($sql);
552 if ($result) {
553 $num = $this->db->num_rows($result);
554 $i = 0;
555
556 while ($i < $num) {
557 $objp = $this->db->fetch_object($result);
558
559 $line = new CommandeFournisseurLigne($this->db);
560
561 $line->id = $objp->rowid;
562 $line->fk_commande = $objp->fk_commande;
563 $line->desc = $objp->description;
564 $line->description = $objp->description;
565 $line->qty = $objp->qty;
566 $line->tva_tx = $objp->tva_tx;
567 $line->localtax1_tx = $objp->localtax1_tx;
568 $line->localtax2_tx = $objp->localtax2_tx;
569 $line->localtax1_type = $objp->localtax1_type;
570 $line->localtax2_type = $objp->localtax2_type;
571 $line->subprice = $objp->subprice;
572 $line->pu_ht = $objp->subprice;
573 $line->remise_percent = $objp->remise_percent;
574
575 $line->vat_src_code = $objp->vat_src_code;
576 $line->total_ht = $objp->total_ht;
577 $line->total_tva = $objp->total_tva;
578 $line->total_localtax1 = $objp->total_localtax1;
579 $line->total_localtax2 = $objp->total_localtax2;
580 $line->total_ttc = $objp->total_ttc;
581 $line->product_type = $objp->product_type;
582
583 $line->fk_product = $objp->fk_product;
584
585 $line->libelle = $objp->product_label; // deprecated
586 $line->product_label = $objp->product_label;
587 $line->product_desc = $objp->product_desc;
588 $line->product_tobatch = $objp->product_tobatch;
589 $line->product_barcode = $objp->product_barcode;
590
591 $line->ref = $objp->product_ref; // Ref of product
592 $line->product_ref = $objp->product_ref; // Ref of product
593 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
594 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
595
596 if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
597 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
598 // Move this into another method and call it when required.
599
600 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
601 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
602 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
603 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
604 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
605 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
606 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
607 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
608 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
609 $sqlsearchpackage .= " LIMIT 1";
610
611 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
612 if ($resqlsearchpackage) {
613 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
614 if ($objsearchpackage) {
615 $line->fk_fournprice = $objsearchpackage->rowid;
616 $line->packaging = $objsearchpackage->packaging;
617 }
618 } else {
619 $this->error = $this->db->lasterror();
620 return -1;
621 }
622 }
623
624 $line->date_start = $this->db->jdate($objp->date_start);
625 $line->date_end = $this->db->jdate($objp->date_end);
626 $line->fk_unit = $objp->fk_unit;
627
628 // Multicurrency
629 $line->fk_multicurrency = $objp->fk_multicurrency;
630 $line->multicurrency_code = $objp->multicurrency_code;
631 $line->multicurrency_subprice = $objp->multicurrency_subprice;
632 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
633 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
634 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
635
636 $line->info_bits = $objp->info_bits;
637 $line->special_code = $objp->special_code;
638 $line->fk_parent_line = $objp->fk_parent_line;
639
640 $line->rang = $objp->rang;
641
642 // Retrieve all extrafield
643 // fetch optionals attributes and labels
644 $line->fetch_optionals();
645
646 $this->lines[$i] = $line;
647
648 $i++;
649 }
650 $this->db->free($result);
651
652 return $num;
653 } else {
654 $this->error = $this->db->error()." sql=".$sql;
655 return -1;
656 }
657 }
658
667 public function valid($user, $idwarehouse = 0, $notrigger = 0)
668 {
669 global $langs, $conf;
670 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
671
672 $error = 0;
673
674 dol_syslog(get_class($this)."::valid");
675 $result = 0;
676 if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
677 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
678 $this->db->begin();
679
680 // Definition of supplier order numbering model name
681 $soc = new Societe($this->db);
682 $soc->fetch($this->fourn_id);
683
684 // Check if object has a temporary ref
685 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
686 $num = $this->getNextNumRef($soc);
687 } else {
688 $num = $this->ref;
689 }
690 $this->newref = dol_sanitizeFileName($num);
691
692 $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
693 $sql .= " SET ref='".$this->db->escape($num)."',";
694 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
695 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
696 $sql .= " fk_user_valid = ".((int) $user->id);
697 $sql .= " WHERE rowid = ".((int) $this->id);
698 $sql .= " AND fk_statut = ".self::STATUS_DRAFT;
699
700 $resql = $this->db->query($sql);
701 if (!$resql) {
702 dol_print_error($this->db);
703 $error++;
704 }
705
706 if (!$error && !$notrigger) {
707 // Call trigger
708 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
709 if ($result < 0) {
710 $error++;
711 }
712 // End call triggers
713 }
714
715 if (!$error) {
716 $this->oldref = $this->ref;
717
718 // Rename directory if dir was a temporary ref
719 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
720 // Now we rename also files into index
721 $sql = 'UPDATE '.MAIN_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)."'";
722 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
723 $resql = $this->db->query($sql);
724 if (!$resql) {
725 $error++; $this->error = $this->db->lasterror();
726 }
727 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
728 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
729 $resql = $this->db->query($sql);
730 if (!$resql) {
731 $error++; $this->error = $this->db->lasterror();
732 }
733
734 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
735 $oldref = dol_sanitizeFileName($this->ref);
736 $newref = dol_sanitizeFileName($num);
737 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
738 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
739 if (!$error && file_exists($dirsource)) {
740 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
741
742 if (@rename($dirsource, $dirdest)) {
743 dol_syslog("Rename ok");
744 // Rename docs starting with $oldref with $newref
745 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
746 foreach ($listoffiles as $fileentry) {
747 $dirsource = $fileentry['name'];
748 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
749 $dirsource = $fileentry['path'].'/'.$dirsource;
750 $dirdest = $fileentry['path'].'/'.$dirdest;
751 @rename($dirsource, $dirdest);
752 }
753 }
754 }
755 }
756 }
757
758 if (!$error) {
759 $result = 1;
761 $this->ref = $num;
762 }
763
764 if (!$error) {
765 $this->db->commit();
766 return 1;
767 } else {
768 $this->db->rollback();
769 return -1;
770 }
771 } else {
772 $this->error = 'NotAuthorized';
773 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
774 return -1;
775 }
776 }
777
784 public function getLibStatut($mode = 0)
785 {
786 return $this->LibStatut($this->statut, $mode, $this->billed);
787 }
788
789 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
798 public function LibStatut($status, $mode = 0, $billed = 0)
799 {
800 // phpcs:enable
801 global $conf, $langs, $hookmanager;
802
803 if (empty($this->statuts) || empty($this->statuts_short)) {
804 $langs->load('orders');
805
806 $this->statuts[0] = 'StatusSupplierOrderDraft';
807 $this->statuts[1] = 'StatusSupplierOrderValidated';
808 $this->statuts[2] = 'StatusSupplierOrderApproved';
809 if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
810 $this->statuts[3] = 'StatusSupplierOrderOnProcess';
811 } else {
812 $this->statuts[3] = 'StatusSupplierOrderOnProcessWithValidation';
813 }
814 $this->statuts[4] = 'StatusSupplierOrderReceivedPartially';
815 $this->statuts[5] = 'StatusSupplierOrderReceivedAll';
816 $this->statuts[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
817 $this->statuts[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
818 $this->statuts[9] = 'StatusSupplierOrderRefused';
819
820 // List of language codes for status
821 $this->statuts_short[0] = 'StatusSupplierOrderDraftShort';
822 $this->statuts_short[1] = 'StatusSupplierOrderValidatedShort';
823 $this->statuts_short[2] = 'StatusSupplierOrderApprovedShort';
824 $this->statuts_short[3] = 'StatusSupplierOrderOnProcessShort';
825 $this->statuts_short[4] = 'StatusSupplierOrderReceivedPartiallyShort';
826 $this->statuts_short[5] = 'StatusSupplierOrderReceivedAllShort';
827 $this->statuts_short[6] = 'StatusSupplierOrderCanceledShort';
828 $this->statuts_short[7] = 'StatusSupplierOrderCanceledShort';
829 $this->statuts_short[9] = 'StatusSupplierOrderRefusedShort';
830 }
831
832 $statustrans = array(
833 0 => 'status0',
834 1 => 'status1b',
835 2 => 'status1',
836 3 => 'status4',
837 4 => 'status4b',
838 5 => 'status6',
839 6 => 'status9',
840 7 => 'status9',
841 9 => 'status9',
842 );
843
844 $statusClass = 'status0';
845 if (!empty($statustrans[$status])) {
846 $statusClass = $statustrans[$status];
847 }
848
849 $billedtext = '';
850 if ($billed) {
851 $billedtext = ' - '.$langs->trans("Billed");
852 }
853 if ($status == 5 && $billed) {
854 $statusClass = 'status6';
855 }
856
857 $statusLong = $langs->transnoentitiesnoconv($this->statuts[$status]).$billedtext;
858 $statusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
859
860 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
861 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
862 if ($reshook > 0) {
863 return $hookmanager->resPrint;
864 }
865
866 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
867 }
868
876 public function getTooltipContentArray($params)
877 {
878 global $conf, $langs, $user;
879
880 $langs->loadLangs(['bills', 'orders']);
881
882 $datas = [];
883 $nofetch = !empty($params['nofetch']);
884
885 if ($user->hasRight("fournisseur", "commande", "read")) {
886 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
887 if (isset($this->statut)) {
888 $datas['picto'] .= ' '.$this->getLibStatut(5);
889 }
890 if (!empty($this->ref)) {
891 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
892 }
893 if (!empty($this->ref_supplier)) {
894 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
895 }
896 if (!$nofetch) {
897 $langs->load('companies');
898 if (empty($this->thirdparty)) {
899 $this->fetch_thirdparty();
900 }
901 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
902 }
903 if (!empty($this->total_ht)) {
904 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
905 }
906 if (!empty($this->total_tva)) {
907 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
908 }
909 if (!empty($this->total_ttc)) {
910 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
911 }
912 if (!empty($this->date)) {
913 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
914 }
915 if (!empty($this->delivery_date)) {
916 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
917 }
918 }
919 return $datas;
920 }
921
932 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
933 {
934 global $langs, $conf, $user, $hookmanager;
935
936 $result = '';
937 $params = [
938 'id' => $this->id,
939 'objecttype' => $this->element,
940 'option' => $option,
941 'nofetch' => 1
942 ];
943 $classfortooltip = 'classfortooltip';
944 $dataparams = '';
945 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
946 $classfortooltip = 'classforajaxtooltip';
947 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
948 $label = '';
949 } else {
950 $label = implode($this->getTooltipContentArray($params));
951 }
952
953 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
954
955 if ($option !== 'nolink') {
956 // Add param to save lastsearch_values or not
957 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
958 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
959 $add_save_lastsearch_values = 1;
960 }
961 if ($add_save_lastsearch_values) {
962 $url .= '&save_lastsearch_values=1';
963 }
964 }
965
966 $linkclose = '';
967 if (empty($notooltip)) {
968 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
969 $label = $langs->trans("ShowOrder");
970 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
971 }
972 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
973 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
974 }
975
976 $linkstart = '<a href="'.$url.'"';
977 $linkstart .= $linkclose.'>';
978 $linkend = '</a>';
979
980 $result .= $linkstart;
981 if ($withpicto) {
982 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
983 }
984 if ($withpicto != 2) {
985 $result .= $this->ref;
986 }
987 $result .= $linkend;
988
989 if ($addlinktonotes) {
990 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
991 if ($txttoshow) {
992 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
993 $result .= ' <span class="note inline-block">';
994 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
995 $result .= img_picto('', 'note');
996 $result .= '</a>';
997 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
998 //$result.='</a>';
999 $result .= '</span>';
1000 }
1001 }
1002
1003 global $action;
1004 $hookmanager->initHooks(array($this->element . 'dao'));
1005 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1006 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1007 if ($reshook > 0) {
1008 $result = $hookmanager->resPrint;
1009 } else {
1010 $result .= $hookmanager->resPrint;
1011 }
1012 return $result;
1013 }
1014
1015
1023 public function getNextNumRef($soc)
1024 {
1025 global $db, $langs, $conf;
1026 $langs->load("orders");
1027
1028 if (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER)) {
1029 $mybool = false;
1030
1031 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1032 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1033
1034 // Include file with class
1035 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1036
1037 foreach ($dirmodels as $reldir) {
1038 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1039
1040 // Load file with numbering class (if found)
1041 $mybool |= @include_once $dir.$file;
1042 }
1043
1044 if ($mybool === false) {
1045 dol_print_error('', "Failed to include file ".$file);
1046 return '';
1047 }
1048
1049 $obj = new $classname();
1050 $numref = $obj->getNextValue($soc, $this);
1051
1052 if ($numref != "") {
1053 return $numref;
1054 } else {
1055 $this->error = $obj->error;
1056 return -1;
1057 }
1058 } else {
1059 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1060 return -2;
1061 }
1062 }
1069 public function classifyBilled(User $user)
1070 {
1071 $error = 0;
1072
1073 if ($this->billed) {
1074 return 0;
1075 }
1076
1077 $this->db->begin();
1078
1079 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
1080 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1081
1082 if ($this->db->query($sql)) {
1083 if (!$error) {
1084 // Call trigger
1085 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1086 if ($result < 0) {
1087 $error++;
1088 }
1089 // End call triggers
1090 }
1091
1092 if (!$error) {
1093 $this->billed = 1;
1094
1095 $this->db->commit();
1096 return 1;
1097 } else {
1098 $this->db->rollback();
1099 return -1;
1100 }
1101 } else {
1102 dol_print_error($this->db);
1103
1104 $this->db->rollback();
1105 return -1;
1106 }
1107 }
1108
1117 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1118 {
1119 global $langs, $conf;
1120 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1121
1122 $error = 0;
1123
1124 dol_syslog(get_class($this)."::approve");
1125
1126 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1127 $now = dol_now();
1128
1129 $this->db->begin();
1130
1131 // Definition of order numbering model name
1132 $soc = new Societe($this->db);
1133 $soc->fetch($this->fourn_id);
1134
1135 // Check if object has a temporary ref
1136 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1137 $num = $this->getNextNumRef($soc);
1138 } else {
1139 $num = $this->ref;
1140 }
1141 $this->newref = dol_sanitizeFileName($num);
1142
1143 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1144 $movetoapprovestatus = true;
1145 $comment = '';
1146
1147 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1148 $sql .= " SET ref='".$this->db->escape($num)."',";
1149 if (empty($secondlevel)) { // standard or first level approval
1150 $sql .= " date_approve='".$this->db->idate($now)."',";
1151 $sql .= " fk_user_approve = ".$user->id;
1152 if (!empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1153 if (empty($this->user_approve_id2)) {
1154 $movetoapprovestatus = false; // second level approval not done
1155 $comment = ' (first level)';
1156 }
1157 }
1158 } else // request a second level approval
1159 {
1160 $sql .= " date_approve2='".$this->db->idate($now)."',";
1161 $sql .= " fk_user_approve2 = ".((int) $user->id);
1162 if (empty($this->user_approve_id)) {
1163 $movetoapprovestatus = false; // first level approval not done
1164 }
1165 $comment = ' (second level)';
1166 }
1167 // If double approval is required and first approval, we keep status to 1 = validated
1168 if ($movetoapprovestatus) {
1169 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1170 } else {
1171 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1172 }
1173 $sql .= " WHERE rowid = ".((int) $this->id);
1174 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1175
1176 if ($this->db->query($sql)) {
1177 if (!empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT)) {
1178 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1179 if ($result < 0 && $result != -2) { // -2 means already exists
1180 $error++;
1181 }
1182 }
1183
1184 // If stock is incremented on validate order, we must increment it
1185 if (!$error && $movetoapprovestatus && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) {
1186 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1187 $langs->load("agenda");
1188
1189 $cpt = count($this->lines);
1190 for ($i = 0; $i < $cpt; $i++) {
1191 // Product with reference
1192 if ($this->lines[$i]->fk_product > 0) {
1193 $this->line = $this->lines[$i];
1194 $mouvP = new MouvementStock($this->db);
1195 $mouvP->origin = &$this;
1196 $mouvP->setOrigin($this->element, $this->id);
1197 // We decrement stock of product (and sub-products)
1198 $up_ht_disc = $this->lines[$i]->subprice;
1199 if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1200 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1201 }
1202 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1203 if ($result < 0) {
1204 $error++;
1205 }
1206 unset($this->line);
1207 }
1208 }
1209 }
1210
1211 if (!$error) {
1212 // Call trigger
1213 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1214 if ($result < 0) {
1215 $error++;
1216 }
1217 // End call triggers
1218 }
1219
1220 if (!$error) {
1221 $this->ref = $this->newref;
1222
1223 if ($movetoapprovestatus) {
1225 } else {
1227 }
1228 if (empty($secondlevel)) { // standard or first level approval
1229 $this->date_approve = $now;
1230 $this->user_approve_id = $user->id;
1231 } else // request a second level approval
1232 {
1233 $this->date_approve2 = $now;
1234 $this->user_approve_id2 = $user->id;
1235 }
1236
1237 $this->db->commit();
1238 return 1;
1239 } else {
1240 $this->db->rollback();
1241 return -1;
1242 }
1243 } else {
1244 $this->db->rollback();
1245 $this->error = $this->db->lasterror();
1246 return -1;
1247 }
1248 } else {
1249 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1250 }
1251 return -1;
1252 }
1253
1260 public function refuse($user)
1261 {
1262 global $conf, $langs;
1263
1264 $error = 0;
1265
1266 dol_syslog(get_class($this)."::refuse");
1267 $result = 0;
1268 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1269 $this->db->begin();
1270
1271 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1272 $sql .= " WHERE rowid = ".((int) $this->id);
1273
1274 if ($this->db->query($sql)) {
1275 $result = 0;
1276
1277 if ($error == 0) {
1278 // Call trigger
1279 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1280 if ($result < 0) {
1281 $error++;
1282 $this->db->rollback();
1283 } else {
1284 $this->db->commit();
1285 }
1286 // End call triggers
1287 }
1288 } else {
1289 $this->db->rollback();
1290 $this->error = $this->db->lasterror();
1291 dol_syslog(get_class($this)."::refuse Error -1");
1292 $result = -1;
1293 }
1294 } else {
1295 dol_syslog(get_class($this)."::refuse Not Authorized");
1296 }
1297 return $result;
1298 }
1299
1300 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1309 public function Cancel($user, $idwarehouse = -1)
1310 {
1311 // phpcs:enable
1312 global $langs, $conf;
1313
1314 $error = 0;
1315
1316 //dol_syslog("CommandeFournisseur::Cancel");
1317 $result = 0;
1318 if ($user->hasRight("fournisseur", "commande", "commander")) {
1319 $statut = self::STATUS_CANCELED;
1320
1321 $this->db->begin();
1322
1323 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".((int) $statut);
1324 $sql .= " WHERE rowid = ".((int) $this->id);
1325 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1326 if ($this->db->query($sql)) {
1327 $result = 0;
1328
1329 // Call trigger
1330 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1331 if ($result < 0) {
1332 $error++;
1333 }
1334 // End call triggers
1335
1336 if ($error == 0) {
1337 $this->db->commit();
1338 return 1;
1339 } else {
1340 $this->db->rollback();
1341 return -1;
1342 }
1343 } else {
1344 $this->db->rollback();
1345 $this->error = $this->db->lasterror();
1346 dol_syslog(get_class($this)."::cancel ".$this->error);
1347 return -1;
1348 }
1349 } else {
1350 dol_syslog(get_class($this)."::cancel Not Authorized");
1351 return -1;
1352 }
1353 }
1354
1364 public function commande($user, $date, $methode, $comment = '')
1365 {
1366 global $langs;
1367 dol_syslog(get_class($this)."::commande");
1368 $error = 0;
1369 if ($user->hasRight("fournisseur", "commande", "commander")) {
1370 $this->db->begin();
1371
1372 $newnoteprivate = $this->note_private;
1373 if ($comment) {
1374 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1375 }
1376
1377 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1378 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1379 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1380 $sql .= " WHERE rowid=".((int) $this->id);
1381
1382 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1383 if ($this->db->query($sql)) {
1385 $this->methode_commande_id = $methode;
1386 $this->date_commande = $date;
1387 $this->context = array('comments' => $comment);
1388
1389 // Call trigger
1390 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1391 if ($result < 0) {
1392 $error++;
1393 }
1394 // End call triggers
1395 } else {
1396 $error++;
1397 $this->error = $this->db->lasterror();
1398 $this->errors[] = $this->db->lasterror();
1399 }
1400
1401 if (!$error) {
1402 $this->db->commit();
1403 } else {
1404 $this->db->rollback();
1405 }
1406 } else {
1407 $error++;
1408 $this->error = $langs->trans('NotAuthorized');
1409 $this->errors[] = $langs->trans('NotAuthorized');
1410 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1411 }
1412
1413 return ($error ? -1 : 1);
1414 }
1415
1423 public function create($user, $notrigger = 0)
1424 {
1425 global $langs, $conf, $hookmanager;
1426
1427 $this->db->begin();
1428
1429 $error = 0;
1430 $now = dol_now();
1431
1432 // set tmp vars
1433 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1434 if (empty($date)) {
1435 $date = $now;
1436 }
1437 $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1438
1439 // Clean parameters
1440 if (empty($this->source)) {
1441 $this->source = 0;
1442 }
1443
1444 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1445 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1446 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1447 } else {
1448 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1449 }
1450 if (empty($this->fk_multicurrency)) {
1451 $this->multicurrency_code = $conf->currency;
1452 $this->fk_multicurrency = 0;
1453 $this->multicurrency_tx = 1;
1454 }
1455
1456 // We set order into draft status
1457 $this->brouillon = 1;
1458 $this->statut = self::STATUS_DRAFT; // deprecated
1459 $this->status = self::STATUS_DRAFT;
1460
1461 $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1462 $sql .= "ref";
1463 $sql .= ", ref_supplier";
1464 $sql .= ", note_private";
1465 $sql .= ", note_public";
1466 $sql .= ", entity";
1467 $sql .= ", fk_soc";
1468 $sql .= ", fk_projet";
1469 $sql .= ", date_creation";
1470 $sql .= ", date_livraison";
1471 $sql .= ", fk_user_author";
1472 $sql .= ", fk_statut";
1473 $sql .= ", source";
1474 $sql .= ", model_pdf";
1475 $sql .= ", fk_mode_reglement";
1476 $sql .= ", fk_cond_reglement";
1477 $sql .= ", fk_account";
1478 $sql .= ", fk_incoterms, location_incoterms";
1479 $sql .= ", fk_multicurrency";
1480 $sql .= ", multicurrency_code";
1481 $sql .= ", multicurrency_tx";
1482 $sql .= ") ";
1483 $sql .= " VALUES (";
1484 $sql .= "'(PROV)'";
1485 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
1486 $sql .= ", '".$this->db->escape($this->note_private)."'";
1487 $sql .= ", '".$this->db->escape($this->note_public)."'";
1488 $sql .= ", ".setEntity($this);
1489 $sql .= ", ".((int) $this->socid);
1490 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1491 $sql .= ", '".$this->db->idate($date)."'";
1492 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1493 $sql .= ", ".((int) $user->id);
1494 $sql .= ", ".self::STATUS_DRAFT;
1495 $sql .= ", ".((int) $this->source);
1496 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1497 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1498 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1499 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1500 $sql .= ", ".(int) $this->fk_incoterms;
1501 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1502 $sql .= ", ".(int) $this->fk_multicurrency;
1503 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1504 $sql .= ", ".(double) $this->multicurrency_tx;
1505 $sql .= ")";
1506
1507 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1508 if ($this->db->query($sql)) {
1509 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1510
1511 if ($this->id) {
1512 $num = count($this->lines);
1513
1514 // insert products details into database
1515 for ($i = 0; $i < $num; $i++) {
1516 $line = $this->lines[$i];
1517 if (!is_object($line)) {
1518 $line = (object) $line;
1519 }
1520
1521
1522 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1523
1524 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1525 $result = $this->addline(
1526 $line->desc,
1527 $line->subprice,
1528 $line->qty,
1529 $line->tva_tx,
1530 $line->localtax1_tx,
1531 $line->localtax2_tx,
1532 $line->fk_product,
1533 0,
1534 $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
1535 $line->remise_percent,
1536 'HT',
1537 0,
1538 $line->product_type,
1539 $line->info_bits,
1540 false,
1541 $line->date_start,
1542 $line->date_end,
1543 $line->array_options,
1544 $line->fk_unit,
1545 $line->multicurrency_subprice, // pu_ht_devise
1546 $line->origin, // origin
1547 $line->origin_id, // origin_id
1548 $line->rang, // rang
1549 $line->special_code
1550 );
1551 if ($result < 0) {
1552 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functionnal error
1553 $this->db->rollback();
1554 return -1;
1555 }
1556 }
1557
1558 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1559 $sql .= " SET ref='(PROV".$this->id.")'";
1560 $sql .= " WHERE rowid=".((int) $this->id);
1561 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1562 if ($this->db->query($sql)) {
1563 // Add link with price request and supplier order
1564 if ($this->id) {
1565 $this->ref = "(PROV".$this->id.")";
1566
1567 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1568 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1569 }
1570
1571 // Add object linked
1572 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1573 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1574 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, ...))
1575 foreach ($tmp_origin_id as $origin_id) {
1576 $ret = $this->add_object_linked($origin, $origin_id);
1577 if (!$ret) {
1578 dol_print_error($this->db);
1579 $error++;
1580 }
1581 }
1582 } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1583 {
1584 $origin_id = $tmp_origin_id;
1585 $ret = $this->add_object_linked($origin, $origin_id);
1586 if (!$ret) {
1587 dol_print_error($this->db);
1588 $error++;
1589 }
1590 }
1591 }
1592 }
1593 }
1594
1595 if (!$error) {
1596 $result = $this->insertExtraFields();
1597 if ($result < 0) {
1598 $error++;
1599 }
1600 }
1601
1602 if (!$error && !$notrigger) {
1603 // Call trigger
1604 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1605 if ($result < 0) {
1606 $this->db->rollback();
1607
1608 return -1;
1609 }
1610 // End call triggers
1611 }
1612
1613 if (!$error) {
1614 $this->db->commit();
1615 return $this->id;
1616 } else {
1617 $this->db->rollback();
1618 return -4;
1619 }
1620 } else {
1621 $this->error = $this->db->lasterror();
1622 $this->db->rollback();
1623
1624 return -2;
1625 }
1626 } else {
1627 $this->error = 'Failed to get ID of inserted line';
1628
1629 return -1;
1630 }
1631 } else {
1632 $this->error = $this->db->lasterror();
1633 $this->db->rollback();
1634
1635 return -1;
1636 }
1637 }
1638
1646 public function update(User $user, $notrigger = 0)
1647 {
1648 global $conf;
1649
1650 $error = 0;
1651
1652 // Clean parameters
1653 if (isset($this->ref)) {
1654 $this->ref = trim($this->ref);
1655 }
1656 if (isset($this->ref_supplier)) {
1657 $this->ref_supplier = trim($this->ref_supplier);
1658 }
1659 if (isset($this->note_private)) {
1660 $this->note_private = trim($this->note_private);
1661 }
1662 if (isset($this->note_public)) {
1663 $this->note_public = trim($this->note_public);
1664 }
1665 if (isset($this->model_pdf)) {
1666 $this->model_pdf = trim($this->model_pdf);
1667 }
1668 if (isset($this->import_key)) {
1669 $this->import_key = trim($this->import_key);
1670 }
1671
1672 // Update request
1673 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1674
1675 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1676 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1677 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1678 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1679 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1680 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1681 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1682 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1683 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1684 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1685 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1686 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1687 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1688 $sql .= " fk_user_valid=".(isset($this->user_valid) && $this->user_valid > 0 ? $this->user_valid : "null").",";
1689 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1690 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1691 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1692 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1693 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1694 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1695 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1696 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1697 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1698 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1699 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1700
1701 $sql .= " WHERE rowid=".((int) $this->id);
1702
1703 $this->db->begin();
1704
1705 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1706 $resql = $this->db->query($sql);
1707 if (!$resql) {
1708 $error++;
1709 $this->errors[] = "Error ".$this->db->lasterror();
1710 }
1711
1712 if (!$error) {
1713 $result = $this->insertExtraFields();
1714 if ($result < 0) {
1715 $error++;
1716 }
1717 }
1718
1719 if (!$error && !$notrigger) {
1720 // Call trigger
1721 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1722 if ($result < 0) {
1723 $error++;
1724 }
1725 // End call triggers
1726 }
1727
1728 // Commit or rollback
1729 if ($error) {
1730 foreach ($this->errors as $errmsg) {
1731 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1732 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1733 }
1734 $this->db->rollback();
1735 return -1 * $error;
1736 } else {
1737 $this->db->commit();
1738 return 1;
1739 }
1740 }
1741
1750 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1751 {
1752 global $conf, $user, $hookmanager;
1753
1754 $error = 0;
1755
1756 $this->db->begin();
1757
1758 // get extrafields so they will be clone
1759 foreach ($this->lines as $line) {
1760 $line->fetch_optionals();
1761 }
1762
1763 // Load source object
1764 $objFrom = clone $this;
1765
1766 // Change socid if needed
1767 if (!empty($socid) && $socid != $this->socid) {
1768 $objsoc = new Societe($this->db);
1769
1770 if ($objsoc->fetch($socid) > 0) {
1771 $this->socid = $objsoc->id;
1772 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1773 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1774 $this->fk_project = 0;
1775 $this->fk_delivery_address = 0;
1776 }
1777
1778 // TODO Change product price if multi-prices
1779 }
1780
1781 $this->id = 0;
1782 $this->statut = self::STATUS_DRAFT;
1783
1784 // Clear fields
1785 $this->user_author_id = $user->id;
1786 $this->user_valid = 0;
1787 $this->date_creation = '';
1788 $this->date_validation = '';
1789 $this->ref_supplier = '';
1790 $this->user_approve_id = '';
1791 $this->user_approve_id2 = '';
1792 $this->date_approve = '';
1793 $this->date_approve2 = '';
1794
1795 // Create clone
1796 $this->context['createfromclone'] = 'createfromclone';
1797 $result = $this->create($user, $notrigger);
1798 if ($result < 0) {
1799 $error++;
1800 }
1801
1802 if (!$error) {
1803 // Hook of thirdparty module
1804 if (is_object($hookmanager)) {
1805 $parameters = array('objFrom'=>$objFrom);
1806 $action = '';
1807 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1808 if ($reshook < 0) {
1809 $this->setErrorsFromObject($hookmanager);
1810 $error++;
1811 }
1812 }
1813 }
1814
1815 unset($this->context['createfromclone']);
1816
1817 // End
1818 if (!$error) {
1819 $this->db->commit();
1820 return $this->id;
1821 } else {
1822 $this->db->rollback();
1823 return -1;
1824 }
1825 }
1826
1856 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 = false, $date_start = null, $date_end = null, $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0, $rang = -1, $special_code = 0)
1857 {
1858 global $langs, $mysoc, $conf;
1859
1860 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");
1861 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1862
1863 if ($this->statut == self::STATUS_DRAFT) {
1864 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1865
1866 // Clean parameters
1867 if (empty($qty)) {
1868 $qty = 0;
1869 }
1870 if (!$info_bits) {
1871 $info_bits = 0;
1872 }
1873 if (empty($txtva)) {
1874 $txtva = 0;
1875 }
1876 if (empty($rang)) {
1877 $rang = 0;
1878 }
1879 if (empty($txlocaltax1)) {
1880 $txlocaltax1 = 0;
1881 }
1882 if (empty($txlocaltax2)) {
1883 $txlocaltax2 = 0;
1884 }
1885 if (empty($remise_percent)) {
1886 $remise_percent = 0;
1887 }
1888
1889 $remise_percent = price2num($remise_percent);
1890 $qty = price2num($qty);
1891 $pu_ht = price2num($pu_ht);
1892 $pu_ht_devise = price2num($pu_ht_devise);
1893 $pu_ttc = price2num($pu_ttc);
1894 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1895 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1896 }
1897 $txlocaltax1 = price2num($txlocaltax1);
1898 $txlocaltax2 = price2num($txlocaltax2);
1899 if ($price_base_type == 'HT') {
1900 $pu = $pu_ht;
1901 } else {
1902 $pu = $pu_ttc;
1903 }
1904 $desc = trim($desc);
1905
1906 // Check parameters
1907 if ($qty < 0 && !$fk_product) {
1908 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1909 return -1;
1910 }
1911 if ($type < 0) {
1912 return -1;
1913 }
1914 if ($date_start && $date_end && $date_start > $date_end) {
1915 $langs->load("errors");
1916 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1917 return -1;
1918 }
1919
1920
1921 $this->db->begin();
1922
1923 $product_type = $type;
1924 $label = ''; // deprecated
1925
1926 if ($fk_product > 0) {
1927 if (!empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY)) { // Not the common case
1928 // Check quantity is enough
1929 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);
1930 $prod = new ProductFournisseur($this->db);
1931 if ($prod->fetch($fk_product) > 0) {
1932 $product_type = $prod->type;
1933 $label = $prod->label;
1934
1935 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1936 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1937 $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
1938
1939 // If supplier order created from sales order, we take best supplier price
1940 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1941 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1942 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1943 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1944 // is remise percent not keyed but present for the product we add it
1945 if ($remise_percent == 0 && $prod->remise_percent != 0) {
1946 $remise_percent = $prod->remise_percent;
1947 }
1948 }
1949 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1950 $langs->load("errors");
1951 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1952 $this->db->rollback();
1953 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1954 //$pu = $prod->fourn_pu; // We do not overwrite unit price
1955 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1956 return -1;
1957 }
1958 if ($result == -1) {
1959 $langs->load("errors");
1960 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1961 $this->db->rollback();
1962 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1963 return -1;
1964 }
1965 if ($result < -1) {
1966 $this->error = $prod->error;
1967 $this->db->rollback();
1968 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1969 return -1;
1970 }
1971 } else {
1972 $this->error = $prod->error;
1973 $this->db->rollback();
1974 return -1;
1975 }
1976 }
1977
1978 // Predefine quantity according to packaging
1979 if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
1980 $prod = new Product($this->db);
1981 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
1982
1983 if ($qty < $prod->packaging) {
1984 $qty = $prod->packaging;
1985 } else {
1986 if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1987 $coeff = intval($qty / $prod->packaging) + 1;
1988 $qty = $prod->packaging * $coeff;
1989 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1990 }
1991 }
1992 }
1993 }
1994
1995 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1996 $pu = 0;
1997 }
1998
1999 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2000
2001 // Clean vat code
2002 $reg = array();
2003 $vat_src_code = '';
2004 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2005 $vat_src_code = $reg[1];
2006 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2007 }
2008
2009 // Calcul du total TTC et de la TVA pour la ligne a partir de
2010 // qty, pu, remise_percent et txtva
2011 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2012 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2013
2014 $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);
2015
2016 $total_ht = $tabprice[0];
2017 $total_tva = $tabprice[1];
2018 $total_ttc = $tabprice[2];
2019 $total_localtax1 = $tabprice[9];
2020 $total_localtax2 = $tabprice[10];
2021 $pu = $pu_ht = $tabprice[3];
2022
2023 // MultiCurrency
2024 $multicurrency_total_ht = $tabprice[16];
2025 $multicurrency_total_tva = $tabprice[17];
2026 $multicurrency_total_ttc = $tabprice[18];
2027 $pu_ht_devise = $tabprice[19];
2028
2029 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2030 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2031
2032 if ($rang < 0) {
2033 $rangmax = $this->line_max();
2034 $rang = $rangmax + 1;
2035 }
2036
2037 // Insert line
2038 $this->line = new CommandeFournisseurLigne($this->db);
2039
2040 $this->line->context = $this->context;
2041
2042 $this->line->fk_commande = $this->id;
2043 $this->line->label = $label;
2044 $this->line->ref_fourn = $ref_supplier;
2045 $this->line->ref_supplier = $ref_supplier;
2046 $this->line->desc = $desc;
2047 $this->line->qty = $qty;
2048 $this->line->tva_tx = $txtva;
2049 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2050 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2051 $this->line->localtax1_type = $localtax1_type;
2052 $this->line->localtax2_type = $localtax2_type;
2053 $this->line->fk_product = $fk_product;
2054 $this->line->product_type = $product_type;
2055 $this->line->remise_percent = $remise_percent;
2056 $this->line->subprice = $pu_ht;
2057 $this->line->rang = $rang;
2058 $this->line->info_bits = $info_bits;
2059
2060 $this->line->vat_src_code = $vat_src_code;
2061 $this->line->total_ht = $total_ht;
2062 $this->line->total_tva = $total_tva;
2063 $this->line->total_localtax1 = $total_localtax1;
2064 $this->line->total_localtax2 = $total_localtax2;
2065 $this->line->total_ttc = $total_ttc;
2066 $this->line->product_type = $type;
2067 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2068 $this->line->origin = $origin;
2069 $this->line->origin_id = $origin_id;
2070 $this->line->fk_unit = $fk_unit;
2071
2072 $this->line->date_start = $date_start;
2073 $this->line->date_end = $date_end;
2074
2075 // Multicurrency
2076 $this->line->fk_multicurrency = $this->fk_multicurrency;
2077 $this->line->multicurrency_code = $this->multicurrency_code;
2078 $this->line->multicurrency_subprice = $pu_ht_devise;
2079 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2080 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2081 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2082
2083 $this->line->subprice = $pu_ht;
2084 $this->line->price = $this->line->subprice;
2085
2086 $this->line->remise_percent = $remise_percent;
2087
2088 if (is_array($array_options) && count($array_options) > 0) {
2089 $this->line->array_options = $array_options;
2090 }
2091
2092 $result = $this->line->insert($notrigger);
2093 if ($result > 0) {
2094 // Reorder if child line
2095 if (!empty($fk_parent_line)) {
2096 $this->line_order(true, 'DESC');
2097 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2098 $linecount = count($this->lines);
2099 for ($ii = $rang; $ii <= $linecount; $ii++) {
2100 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2101 }
2102 }
2103
2104 // Mise a jour informations denormalisees au niveau de la commande meme
2105 $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.
2106 if ($result > 0) {
2107 $this->db->commit();
2108 return $this->line->id;
2109 } else {
2110 $this->db->rollback();
2111 return -1;
2112 }
2113 } else {
2114 $this->error = $this->line->error;
2115 $this->errors = $this->line->errors;
2116 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2117 $this->db->rollback();
2118 return -1;
2119 }
2120 }
2121 return -1;
2122 }
2123
2124
2142 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2143 {
2144 global $conf, $langs;
2145
2146 $error = 0;
2147 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2148
2149 // Check parameters (if test are wrong here, there is bug into caller)
2150 if ($entrepot <= 0) {
2151 $this->error = 'ErrorBadValueForParameterWarehouse';
2152 return -1;
2153 }
2154 if ($qty == 0) {
2155 $this->error = 'ErrorBadValueForParameterQty';
2156 return -1;
2157 }
2158
2159 $dispatchstatus = 1;
2160 if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2161 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2162 }
2163
2164 $now = dol_now();
2165
2166 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2167
2168 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2169 $this->db->begin();
2170
2171 $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
2172 $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2173 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2174 $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");
2175 $sql .= ")";
2176
2177 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2178 $resql = $this->db->query($sql);
2179 if ($resql) {
2180 if (!$notrigger) {
2181 global $conf, $langs, $user;
2182 // Call trigger
2183 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2184 if ($result < 0) {
2185 $error++;
2186 }
2187 // End call triggers
2188 }
2189 } else {
2190 $this->error = $this->db->lasterror();
2191 $error++;
2192 }
2193
2194 // If module stock is enabled and the stock increase is done on purchase order dispatching
2195 if (!$error && $entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
2196 $mouv = new MouvementStock($this->db);
2197 if ($product > 0) {
2198 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2199 $mouv->origin = &$this;
2200 $mouv->setOrigin($this->element, $this->id);
2201
2202 // Method change if qty < 0
2203 if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qty < 0) {
2204 $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2205 } else {
2206 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2207 }
2208
2209 if ($result < 0) {
2210 $this->error = $mouv->error;
2211 $this->errors = $mouv->errors;
2212 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2213 $error++;
2214 }
2215 }
2216 }
2217
2218 if ($error == 0) {
2219 $this->db->commit();
2220 return 1;
2221 } else {
2222 $this->db->rollback();
2223 return -1;
2224 }
2225 } else {
2226 $this->error = 'BadStatusForObject';
2227 return -2;
2228 }
2229 }
2230
2238 public function deleteline($idline, $notrigger = 0)
2239 {
2240 if ($this->statut == 0) {
2241 $line = new CommandeFournisseurLigne($this->db);
2242
2243 if ($line->fetch($idline) <= 0) {
2244 return 0;
2245 }
2246
2247 // check if not yet received
2248 $dispatchedLines = $this->getDispachedLines();
2249 foreach ($dispatchedLines as $dispatchLine) {
2250 if ($dispatchLine['orderlineid'] == $idline) {
2251 $this->error = "LineAlreadyDispatched";
2252 $this->errors[] = $this->error;
2253 return -3;
2254 }
2255 }
2256
2257 if ($line->delete($notrigger) > 0) {
2258 $this->update_price(1);
2259 return 1;
2260 } else {
2261 $this->error = $line->error;
2262 $this->errors = $line->errors;
2263 return -1;
2264 }
2265 } else {
2266 return -2;
2267 }
2268 }
2269
2277 public function delete(User $user, $notrigger = 0)
2278 {
2279 global $langs, $conf;
2280 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2281
2282 $error = 0;
2283
2284 $this->db->begin();
2285
2286 if (empty($notrigger)) {
2287 // Call trigger
2288 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2289 if ($result < 0) {
2290 $this->errors[] = 'ErrorWhenRunningTrigger';
2291 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2292 $this->db->rollback();
2293 return -1;
2294 }
2295 // End call triggers
2296 }
2297
2298 // Test we can delete
2299 $this->fetchObjectLinked(null, 'order_supplier');
2300 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2301 foreach ($this->linkedObjects['reception'] as $element) {
2302 if ($element->statut >= 0) {
2303 $this->errors[] = $langs->trans('ReceptionExist');
2304 $error++;
2305 break;
2306 }
2307 }
2308 }
2309
2310 $main = $this->db->prefix().'commande_fournisseurdet';
2311 $ef = $main."_extrafields";
2312 $sql = "DELETE FROM ".$this->db->sanitize($ef)." WHERE fk_object IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2313 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2314 if (!$this->db->query($sql)) {
2315 $this->error = $this->db->lasterror();
2316 $this->errors[] = $this->db->lasterror();
2317 $error++;
2318 }
2319
2320 if (!$error) {
2321 $sql1 = "UPDATE ".$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet IN (SELECT rowid FROM ".$this->db->sanitize($main)." WHERE fk_commande = ".((int) $this->id).")";
2322 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2323 if (!$this->db->query($sql1)) {
2324 $error++;
2325 $this->error = $this->db->lasterror();
2326 $this->errors[] = $this->db->lasterror();
2327 }
2328 }
2329
2330 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2331 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2332 if (!$this->db->query($sql)) {
2333 $this->error = $this->db->lasterror();
2334 $this->errors[] = $this->db->lasterror();
2335 $error++;
2336 }
2337
2338 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2339 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2340 if ($resql = $this->db->query($sql)) {
2341 if ($this->db->affected_rows($resql) < 1) {
2342 $this->error = $this->db->lasterror();
2343 $this->errors[] = $this->db->lasterror();
2344 $error++;
2345 }
2346 } else {
2347 $this->error = $this->db->lasterror();
2348 $this->errors[] = $this->db->lasterror();
2349 $error++;
2350 }
2351
2352 // Remove extrafields
2353 if (!$error) {
2354 $result = $this->deleteExtraFields();
2355 if ($result < 0) {
2356 $this->error = 'FailToDeleteExtraFields';
2357 $this->errors[] = 'FailToDeleteExtraFields';
2358 $error++;
2359 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2360 }
2361 }
2362
2363 // Delete linked object
2364 $res = $this->deleteObjectLinked();
2365 if ($res < 0) {
2366 $this->error = 'FailToDeleteObjectLinked';
2367 $this->errors[] = 'FailToDeleteObjectLinked';
2368 $error++;
2369 }
2370
2371 if (!$error) {
2372 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2373 $this->deleteEcmFiles();
2374
2375 // We remove directory
2376 $ref = dol_sanitizeFileName($this->ref);
2377 if ($conf->fournisseur->commande->dir_output) {
2378 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2379 $file = $dir."/".$ref.".pdf";
2380 if (file_exists($file)) {
2381 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2382 $this->error = 'ErrorFailToDeleteFile';
2383 $this->errors[] = 'ErrorFailToDeleteFile';
2384 $error++;
2385 }
2386 }
2387 if (file_exists($dir)) {
2388 $res = @dol_delete_dir_recursive($dir);
2389 if (!$res) {
2390 $this->error = 'ErrorFailToDeleteDir';
2391 $this->errors[] = 'ErrorFailToDeleteDir';
2392 $error++;
2393 }
2394 }
2395 }
2396 }
2397
2398 if (!$error) {
2399 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2400 $this->db->commit();
2401 return 1;
2402 } else {
2403 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2404 $this->db->rollback();
2405 return -$error;
2406 }
2407 }
2408
2409 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2415 public function get_methodes_commande()
2416 {
2417 // phpcs:enable
2418 $sql = "SELECT rowid, libelle";
2419 $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method";
2420 $sql .= " WHERE active = 1";
2421
2422 $resql = $this->db->query($sql);
2423 if ($resql) {
2424 $i = 0;
2425 $num = $this->db->num_rows($resql);
2426 $this->methodes_commande = array();
2427 while ($i < $num) {
2428 $row = $this->db->fetch_row($resql);
2429
2430 $this->methodes_commande[$row[0]] = $row[1];
2431
2432 $i++;
2433 }
2434 return 0;
2435 } else {
2436 return -1;
2437 }
2438 }
2439
2448 public function getDispachedLines($status = -1)
2449 {
2450 $ret = array();
2451
2452 // List of already dispatched lines
2453 $sql = "SELECT p.ref, p.label,";
2454 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2455 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_commandefourndet";
2456 $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
2457 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2458 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2459 $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2460 $sql .= " AND cfd.fk_product = p.rowid";
2461 if ($status >= 0) {
2462 $sql .= " AND cfd.status = ".((int) $status);
2463 }
2464 $sql .= " ORDER BY cfd.rowid ASC";
2465
2466 $resql = $this->db->query($sql);
2467 if ($resql) {
2468 $num = $this->db->num_rows($resql);
2469 $i = 0;
2470
2471 while ($i < $num) {
2472 $objp = $this->db->fetch_object($resql);
2473 if ($objp) {
2474 $ret[] = array(
2475 'id' => $objp->dispatchedlineid,
2476 'productid' => $objp->fk_product,
2477 'warehouseid' => $objp->warehouse_id,
2478 'qty' => $objp->qty,
2479 'orderlineid' => $objp->fk_commandefourndet
2480 );
2481 }
2482
2483 $i++;
2484 }
2485 } else {
2486 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2487 }
2488
2489 return $ret;
2490 }
2491
2492 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2502 public function Livraison($user, $date, $type, $comment)
2503 {
2504 // phpcs:enable
2505 global $conf, $langs;
2506
2507 $result = 0;
2508 $error = 0;
2509
2510 dol_syslog(get_class($this)."::Livraison");
2511
2512 $usercanreceive = 0;
2513 if (!isModEnabled('reception')) {
2514 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2515 } else {
2516 $usercanreceive = $user->hasRight("reception", "creer");
2517 }
2518
2519 if ($usercanreceive) {
2520 // Define the new status
2521 if ($type == 'par') {
2523 } elseif ($type == 'tot') {
2525 } elseif ($type == 'nev') {
2527 } elseif ($type == 'can') {
2529 } else {
2530 $error++;
2531 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2532 return -2;
2533 }
2534
2535 // Some checks to accept the record
2536 if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2537 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2538 if (!$error && ($type == 'tot')) {
2539 $dispatchedlinearray = $this->getDispachedLines(0);
2540 if (count($dispatchedlinearray) > 0) {
2541 $result = -1;
2542 $error++;
2543 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2544 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2545 }
2546 }
2547 if (!$error && !empty($conf->global->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)
2548 $dispatcheddenied = $this->getDispachedLines(2);
2549 if (count($dispatchedlinearray) > 0) {
2550 $result = -1;
2551 $error++;
2552 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2553 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2554 }
2555 }
2556 }
2557
2558 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2559
2560 if (empty($error)) {
2561 $this->db->begin();
2562
2563 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2564 $sql .= " SET fk_statut = ".((int) $statut);
2565 $sql .= " WHERE rowid = ".((int) $this->id);
2566 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2567
2568 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2569 $resql = $this->db->query($sql);
2570 if ($resql) {
2571 $result = 1;
2572 $old_statut = $this->statut;
2573 $this->statut = $statut;
2574 $this->actionmsg2 = $comment;
2575
2576 // Call trigger
2577 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2578 if ($result_trigger < 0) {
2579 $error++;
2580 }
2581 // End call triggers
2582
2583 if (empty($error)) {
2584 $this->db->commit();
2585 } else {
2586 $this->statut = $old_statut;
2587 $this->db->rollback();
2588 $this->error = $this->db->lasterror();
2589 $result = -1;
2590 }
2591 } else {
2592 $this->db->rollback();
2593 $this->error = $this->db->lasterror();
2594 $result = -1;
2595 }
2596 }
2597 } else {
2598 $this->error = $langs->trans('NotAuthorized');
2599 $this->errors[] = $langs->trans('NotAuthorized');
2600 dol_syslog(get_class($this)."::Livraison Not Authorized");
2601 $result = -3;
2602 }
2603 return $result;
2604 }
2605
2606 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2616 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2617 {
2618 // phpcs:enable
2619 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2620 }
2621
2630 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2631 {
2632 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2633 $error = 0;
2634
2635 $this->db->begin();
2636
2637 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2638 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2639 $sql .= " WHERE rowid = ".((int) $this->id);
2640
2641 dol_syslog(__METHOD__, LOG_DEBUG);
2642 $resql = $this->db->query($sql);
2643 if (!$resql) {
2644 $this->errors[] = $this->db->error();
2645 $error++;
2646 }
2647
2648 if (!$error) {
2649 $this->oldcopy = clone $this;
2650 $this->date_livraison = $delivery_date;
2651 $this->delivery_date = $delivery_date;
2652 }
2653
2654 if (!$notrigger && empty($error)) {
2655 // Call trigger
2656 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2657 if ($result < 0) {
2658 $error++;
2659 }
2660 // End call triggers
2661 }
2662
2663 if (!$error) {
2664 $this->db->commit();
2665 return 1;
2666 } else {
2667 foreach ($this->errors as $errmsg) {
2668 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2669 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2670 }
2671 $this->db->rollback();
2672 return -1 * $error;
2673 }
2674 } else {
2675 return -2;
2676 }
2677 }
2678
2679 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2688 public function set_id_projet($user, $id_projet, $notrigger = 0)
2689 {
2690 // phpcs:enable
2691 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2692 $error = 0;
2693
2694 $this->db->begin();
2695
2696 $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2697 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2698 $sql .= " WHERE rowid = ".((int) $this->id);
2699
2700 dol_syslog(__METHOD__, LOG_DEBUG);
2701 $resql = $this->db->query($sql);
2702 if (!$resql) {
2703 $this->errors[] = $this->db->error();
2704 $error++;
2705 }
2706
2707 if (!$error) {
2708 $this->oldcopy = clone $this;
2709 $this->fk_projet = $id_projet;
2710 $this->fk_project = $id_projet;
2711 }
2712
2713 if (!$notrigger && empty($error)) {
2714 // Call trigger
2715 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2716 if ($result < 0) {
2717 $error++;
2718 }
2719 // End call triggers
2720 }
2721
2722 if (!$error) {
2723 $this->db->commit();
2724 return 1;
2725 } else {
2726 foreach ($this->errors as $errmsg) {
2727 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2728 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2729 }
2730 $this->db->rollback();
2731 return -1 * $error;
2732 }
2733 } else {
2734 return -2;
2735 }
2736 }
2737
2746 public function updateFromCommandeClient($user, $idc, $comclientid)
2747 {
2748 $comclient = new Commande($this->db);
2749 $comclient->fetch($comclientid);
2750
2751 $this->id = $idc;
2752
2753 $this->lines = array();
2754
2755 $num = count($comclient->lines);
2756 for ($i = 0; $i < $num; $i++) {
2757 $prod = new Product($this->db);
2758 $label = '';
2759 $ref = '';
2760 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2761 $label = $prod->label;
2762 $ref = $prod->ref;
2763 }
2764
2765 $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2766 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2767 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2768 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2769 $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);
2770 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2771 if ($this->db->query($sql)) {
2772 $this->update_price(1);
2773 }
2774 }
2775
2776 return 1;
2777 }
2778
2786 public function setStatus($user, $status)
2787 {
2788 global $conf, $langs;
2789 $error = 0;
2790
2791 $this->db->begin();
2792
2793 $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2794 $sql .= " SET fk_statut = ".$status;
2795 $sql .= " WHERE rowid = ".((int) $this->id);
2796
2797 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2798 $resql = $this->db->query($sql);
2799 if ($resql) {
2800 // Trigger names for each status
2801 $triggerName = array();
2802 $triggerName[0] = 'DRAFT';
2803 $triggerName[1] = 'VALIDATED';
2804 $triggerName[2] = 'APPROVED';
2805 $triggerName[3] = 'ORDERED'; // Ordered
2806 $triggerName[4] = 'RECEIVED_PARTIALLY';
2807 $triggerName[5] = 'RECEIVED_COMPLETELY';
2808 $triggerName[6] = 'CANCELED';
2809 $triggerName[7] = 'CANCELED';
2810 $triggerName[9] = 'REFUSED';
2811
2812 // Call trigger
2813 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2814 if ($result < 0) {
2815 $error++;
2816 }
2817 // End call triggers
2818 } else {
2819 $error++;
2820 $this->error = $this->db->lasterror();
2821 dol_syslog(get_class($this)."::setStatus ".$this->error);
2822 }
2823
2824 if (!$error) {
2825 $this->statut = $status;
2826 $this->db->commit();
2827 return 1;
2828 } else {
2829 $this->db->rollback();
2830 return -1;
2831 }
2832 }
2833
2857 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 = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
2858 {
2859 global $mysoc, $conf, $langs;
2860 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2861 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2862
2863 $error = 0;
2864
2865 if ($this->brouillon) {
2866 // Clean parameters
2867 if (empty($qty)) {
2868 $qty = 0;
2869 }
2870 if (empty($info_bits)) {
2871 $info_bits = 0;
2872 }
2873 if (empty($txtva)) {
2874 $txtva = 0;
2875 }
2876 if (empty($txlocaltax1)) {
2877 $txlocaltax1 = 0;
2878 }
2879 if (empty($txlocaltax2)) {
2880 $txlocaltax2 = 0;
2881 }
2882 if (empty($remise)) {
2883 $remise = 0;
2884 }
2885 if (empty($remise_percent)) {
2886 $remise_percent = 0;
2887 }
2888
2889 $remise_percent = price2num($remise_percent);
2890 $qty = price2num($qty);
2891 if (!$qty) {
2892 $qty = 1;
2893 }
2894 $pu = price2num($pu);
2895 $pu_ht_devise = price2num($pu_ht_devise);
2896 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2897 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2898 }
2899 $txlocaltax1 = price2num($txlocaltax1);
2900 $txlocaltax2 = price2num($txlocaltax2);
2901
2902 // Check parameters
2903 if ($type < 0) {
2904 return -1;
2905 }
2906 if ($date_start && $date_end && $date_start > $date_end) {
2907 $langs->load("errors");
2908 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2909 return -1;
2910 }
2911
2912 $this->db->begin();
2913
2914 // Calcul du total TTC et de la TVA pour la ligne a partir de
2915 // qty, pu, remise_percent et txtva
2916 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2917 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2918
2919 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2920
2921 // Clean vat code
2922 $reg = array();
2923 $vat_src_code = '';
2924 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2925 $vat_src_code = $reg[1];
2926 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2927 }
2928
2929 $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);
2930 $total_ht = $tabprice[0];
2931 $total_tva = $tabprice[1];
2932 $total_ttc = $tabprice[2];
2933 $total_localtax1 = $tabprice[9];
2934 $total_localtax2 = $tabprice[10];
2935 $pu_ht = $tabprice[3];
2936 $pu_tva = $tabprice[4];
2937 $pu_ttc = $tabprice[5];
2938
2939 // MultiCurrency
2940 $multicurrency_total_ht = $tabprice[16];
2941 $multicurrency_total_tva = $tabprice[17];
2942 $multicurrency_total_ttc = $tabprice[18];
2943 $pu_ht_devise = $tabprice[19];
2944
2945 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2946 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2947
2948 //Fetch current line from the database and then clone the object and set it in $oldline property
2949 $this->line = new CommandeFournisseurLigne($this->db);
2950 $this->line->fetch($rowid);
2951
2952 $oldline = clone $this->line;
2953 $this->line->oldline = $oldline;
2954
2955 $this->line->context = $this->context;
2956
2957 $this->line->fk_commande = $this->id;
2958 //$this->line->label=$label;
2959 $this->line->desc = $desc;
2960
2961 // redefine quantity according to packaging
2962 if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
2963 if ($qty < $this->line->packaging) {
2964 $qty = $this->line->packaging;
2965 } else {
2966 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2967 $coeff = intval($qty / $this->line->packaging) + 1;
2968 $qty = $this->line->packaging * $coeff;
2969 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2970 }
2971 }
2972 }
2973
2974 $this->line->qty = $qty;
2975 $this->line->ref_supplier = $ref_supplier;
2976
2977 $this->line->vat_src_code = $vat_src_code;
2978 $this->line->tva_tx = $txtva;
2979 $this->line->localtax1_tx = $txlocaltax1;
2980 $this->line->localtax2_tx = $txlocaltax2;
2981 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2982 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2983 $this->line->remise_percent = $remise_percent;
2984 $this->line->subprice = $pu_ht;
2985 $this->line->info_bits = $info_bits;
2986 $this->line->total_ht = $total_ht;
2987 $this->line->total_tva = $total_tva;
2988 $this->line->total_localtax1 = $total_localtax1;
2989 $this->line->total_localtax2 = $total_localtax2;
2990 $this->line->total_ttc = $total_ttc;
2991 $this->line->product_type = $type;
2992 $this->line->special_code = $oldline->special_code;
2993 $this->line->rang = $oldline->rang;
2994 $this->line->origin = $this->origin;
2995 $this->line->fk_unit = $fk_unit;
2996
2997 $this->line->date_start = $date_start;
2998 $this->line->date_end = $date_end;
2999
3000 // Multicurrency
3001 $this->line->fk_multicurrency = $this->fk_multicurrency;
3002 $this->line->multicurrency_code = $this->multicurrency_code;
3003 $this->line->multicurrency_subprice = $pu_ht_devise;
3004 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3005 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3006 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3007
3008 $this->line->subprice = $pu_ht;
3009 $this->line->price = $this->line->subprice;
3010
3011 $this->line->remise_percent = $remise_percent;
3012
3013 if (is_array($array_options) && count($array_options) > 0) {
3014 // We replace values in this->line->array_options only for entries defined into $array_options
3015 foreach ($array_options as $key => $value) {
3016 $this->line->array_options[$key] = $array_options[$key];
3017 }
3018 }
3019
3020 $result = $this->line->update($notrigger);
3021
3022
3023 // Mise a jour info denormalisees au niveau facture
3024 if ($result >= 0) {
3025 $this->update_price('1', 'auto');
3026 $this->db->commit();
3027 return $result;
3028 } else {
3029 $this->errors[] = $this->line->error;
3030 $this->errors = array_merge($this->errors, $this->line->errors);
3031 $this->error = $this->db->lasterror();
3032 $this->db->rollback();
3033 return -1;
3034 }
3035 } else {
3036 $this->error = "Order status makes operation forbidden";
3037 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3038 return -2;
3039 }
3040 }
3041
3042
3050 public function initAsSpecimen()
3051 {
3052 global $user, $langs, $conf;
3053
3054 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3055
3056 dol_syslog(get_class($this)."::initAsSpecimen");
3057
3058 $now = dol_now();
3059
3060 // Find first product
3061 $prodid = 0;
3062 $product = new ProductFournisseur($this->db);
3063 $sql = "SELECT rowid";
3064 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3065 $sql .= " WHERE entity IN (".getEntity('product').")";
3066 $sql .= $this->db->order("rowid", "ASC");
3067 $sql .= $this->db->plimit(1);
3068 $resql = $this->db->query($sql);
3069 if ($resql && $this->db->num_rows($resql)) {
3070 $obj = $this->db->fetch_object($resql);
3071 $prodid = $obj->rowid;
3072 }
3073
3074 // Initialise parametres
3075 $this->id = 0;
3076 $this->ref = 'SPECIMEN';
3077 $this->specimen = 1;
3078 $this->socid = 1;
3079 $this->date = $now;
3080 $this->date_commande = $now;
3081 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3082 $this->cond_reglement_code = 'RECEP';
3083 $this->mode_reglement_code = 'CHQ';
3084
3085 $this->note_public = 'This is a comment (public)';
3086 $this->note_private = 'This is a comment (private)';
3087
3088 $this->multicurrency_tx = 1;
3089 $this->multicurrency_code = $conf->currency;
3090
3091 $this->statut = 0;
3092
3093 // Lines
3094 $nbp = 5;
3095 $xnbp = 0;
3096 while ($xnbp < $nbp) {
3097 $line = new CommandeFournisseurLigne($this->db);
3098 $line->desc = $langs->trans("Description")." ".$xnbp;
3099 $line->qty = 1;
3100 $line->subprice = 100;
3101 $line->price = 100;
3102 $line->tva_tx = 19.6;
3103 $line->localtax1_tx = 0;
3104 $line->localtax2_tx = 0;
3105 if ($xnbp == 2) {
3106 $line->total_ht = 50;
3107 $line->total_ttc = 59.8;
3108 $line->total_tva = 9.8;
3109 $line->remise_percent = 50;
3110 } else {
3111 $line->total_ht = 100;
3112 $line->total_ttc = 119.6;
3113 $line->total_tva = 19.6;
3114 $line->remise_percent = 00;
3115 }
3116 $line->fk_product = $prodid;
3117
3118 $this->lines[$xnbp] = $line;
3119
3120 $this->total_ht += $line->total_ht;
3121 $this->total_tva += $line->total_tva;
3122 $this->total_ttc += $line->total_ttc;
3123
3124 $xnbp++;
3125 }
3126 }
3127
3134 public function info($id)
3135 {
3136 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3137 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3138 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
3139 $sql .= ' WHERE c.rowid = '.((int) $id);
3140
3141 $result = $this->db->query($sql);
3142 if ($result) {
3143 if ($this->db->num_rows($result)) {
3144 $obj = $this->db->fetch_object($result);
3145 $this->id = $obj->rowid;
3146 if ($obj->fk_user_author) {
3147 $this->user_creation_id = $obj->fk_user_author;
3148 }
3149 if ($obj->fk_user_valid) {
3150 $this->user_validation_id = $obj->fk_user_valid;
3151 }
3152 if ($obj->fk_user_modif) {
3153 $this->user_modification_id = $obj->fk_user_modif;
3154 }
3155 if ($obj->fk_user_approve) {
3156 $this->user_approve_id = $obj->fk_user_approve;
3157 }
3158 if ($obj->fk_user_approve2) {
3159 $this->user_approve_id2 = $obj->fk_user_approve2;
3160 }
3161
3162 $this->date_creation = $this->db->jdate($obj->datec);
3163 $this->date_modification = $this->db->jdate($obj->datem);
3164 $this->date_approve = $this->db->jdate($obj->datea);
3165 $this->date_approve2 = $this->db->jdate($obj->datea2);
3166 $this->date_validation = $this->db->jdate($obj->date_validation);
3167 }
3168 $this->db->free($result);
3169 } else {
3170 dol_print_error($this->db);
3171 }
3172 }
3173
3174 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3180 public function load_state_board()
3181 {
3182 // phpcs:enable
3183 global $conf, $user;
3184
3185 $this->nb = array();
3186 $clause = "WHERE";
3187
3188 $sql = "SELECT count(co.rowid) as nb";
3189 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
3190 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3191 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3192 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3193 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3194 $clause = "AND";
3195 }
3196 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3197
3198 $resql = $this->db->query($sql);
3199 if ($resql) {
3200 while ($obj = $this->db->fetch_object($resql)) {
3201 $this->nb["supplier_orders"] = $obj->nb;
3202 }
3203 $this->db->free($resql);
3204 return 1;
3205 } else {
3206 dol_print_error($this->db);
3207 $this->error = $this->db->error();
3208 return -1;
3209 }
3210 }
3211
3212 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3220 public function load_board($user, $mode = 'opened')
3221 {
3222 // phpcs:enable
3223 global $conf, $langs;
3224
3225 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3226 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
3227 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3228 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3229 }
3230 $sql .= " WHERE c.entity = ".$conf->entity;
3231 if ($mode === 'awaiting') {
3232 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3233 } else {
3234 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3235 }
3236 if ($user->socid) {
3237 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3238 }
3239
3240 $resql = $this->db->query($sql);
3241 if ($resql) {
3242 $commandestatic = new CommandeFournisseur($this->db);
3243
3244 $response = new WorkboardResponse();
3245 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3246 $response->label = $langs->trans("SuppliersOrdersToProcess");
3247 $response->labelShort = $langs->trans("Opened");
3248 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3249 $response->img = img_object('', "order");
3250
3251 if ($mode === 'awaiting') {
3252 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3253 $response->labelShort = $langs->trans("AwaitingReception");
3254 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3255 }
3256
3257 while ($obj = $this->db->fetch_object($resql)) {
3258 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3259 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3260 $commandestatic->statut = $obj->fk_statut;
3261
3262 $response->nbtodo++;
3263 $response->total += $obj->total_ht;
3264
3265 if ($commandestatic->hasDelay()) {
3266 $response->nbtodolate++;
3267 }
3268 }
3269
3270 return $response;
3271 } else {
3272 $this->error = $this->db->error();
3273 return -1;
3274 }
3275 }
3276
3283 public function getInputMethod()
3284 {
3285 global $db, $langs;
3286
3287 if ($this->methode_commande_id > 0) {
3288 $sql = "SELECT rowid, code, libelle as label";
3289 $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_method';
3290 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3291
3292 $resql = $this->db->query($sql);
3293 if ($resql) {
3294 if ($this->db->num_rows($resql)) {
3295 $obj = $this->db->fetch_object($resql);
3296
3297 $string = $langs->trans($obj->code);
3298 if ($string == $obj->code) {
3299 $string = $obj->label != '-' ? $obj->label : '';
3300 }
3301 return $string;
3302 }
3303 } else {
3304 dol_print_error($this->db);
3305 }
3306 }
3307
3308 return '';
3309 }
3310
3322 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3323 {
3324 global $conf, $langs;
3325
3326 if (!dol_strlen($modele)) {
3327 $modele = ''; // No doc template/generation by default
3328
3329 if (!empty($this->model_pdf)) {
3330 $modele = $this->model_pdf;
3331 } elseif (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
3332 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3333 }
3334 }
3335
3336 if (empty($modele)) {
3337 return 0;
3338 } else {
3339 $langs->load("suppliers");
3340 $outputlangs->load("products");
3341
3342 $modelpath = "core/modules/supplier_order/doc/";
3343 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3344 return $result;
3345 }
3346 }
3347
3354 public function getMaxDeliveryTimeDay($langs)
3355 {
3356 if (empty($this->lines)) {
3357 return '';
3358 }
3359
3360 $obj = new ProductFournisseur($this->db);
3361
3362 $nb = 0;
3363 foreach ($this->lines as $line) {
3364 if ($line->fk_product > 0) {
3365 $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3366 if ($idp) {
3367 $obj->fetch($idp);
3368 if ($obj->delivery_time_days > $nb) {
3369 $nb = $obj->delivery_time_days;
3370 }
3371 }
3372 }
3373 }
3374
3375 if ($nb === 0) {
3376 return '';
3377 } else {
3378 return $nb.' '.$langs->trans('Days');
3379 }
3380 }
3381
3386 public function getRights()
3387 {
3388 global $user;
3389
3390 return $user->hasRight("fournisseur", "commande");
3391 }
3392
3393
3402 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3403 {
3404 $tables = array(
3405 'commande_fournisseur'
3406 );
3407
3408 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3409 }
3410
3419 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3420 {
3421 $tables = array(
3422 'commande_fournisseurdet'
3423 );
3424
3425 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3426 }
3427
3435 public function hasDelay()
3436 {
3437 global $conf;
3438
3439 if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3440 $this->delivery_date = $this->date_livraison; // For backward compatibility
3441 }
3442
3443 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3444 $now = dol_now();
3445 if (!empty($this->delivery_date)) {
3446 $date_to_test = $this->delivery_date;
3447 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3448 } else {
3449 //$date_to_test = $this->date_commande;
3450 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3451 return false;
3452 }
3453 } else {
3454 $now = dol_now();
3455 $date_to_test = $this->date_commande;
3456
3457 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3458 }
3459 }
3460
3468 public function showDelay()
3469 {
3470 global $conf, $langs;
3471
3472 if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3473 $this->delivery_date = $this->date_livraison; // For backward compatibility
3474 }
3475
3476 $text = '';
3477
3478 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3479 if (!empty($this->delivery_date)) {
3480 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3481 } else {
3482 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3483 }
3484 } else {
3485 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3486 }
3487 if ($text) {
3488 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3489 }
3490
3491 return $text;
3492 }
3493
3494
3503 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3504 {
3505 global $conf, $langs;
3506
3507 if (isModEnabled("supplier_order")) {
3508 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3509
3510 $qtydelivered = array();
3511 $qtywished = array();
3512
3513 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3514 $filter = array('t.fk_commande'=>$this->id);
3515 if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3516 $filter['t.status'] = 1; // Restrict to lines with status validated
3517 }
3518
3519 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3520 if ($ret < 0) {
3521 $this->error = $supplierorderdispatch->error; $this->errors = $supplierorderdispatch->errors;
3522 return $ret;
3523 } else {
3524 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3525 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3526 $date_liv = dol_now();
3527
3528 // Build array with quantity deliverd by product
3529 foreach ($supplierorderdispatch->lines as $line) {
3530 $qtydelivered[$line->fk_product] += $line->qty;
3531 }
3532 foreach ($this->lines as $line) {
3533 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3534 if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $line->product_type > 0) {
3535 continue;
3536 }
3537 $qtywished[$line->fk_product] += $line->qty;
3538 }
3539
3540 //Compare array
3541 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3542 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3543 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3544 //var_dump(array_keys($qtydelivered));
3545 //var_dump(array_keys($qtywished));
3546 //var_dump($diff_array);
3547 //var_dump($keysinwishednotindelivered);
3548 //var_dump($keysindeliverednotinwished);
3549 //exit;
3550
3551 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3552 if ($closeopenorder) {
3553 //$ret=$this->setStatus($user,5);
3554 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3555 if ($ret < 0) {
3556 return -1;
3557 }
3558 return 5;
3559 } else {
3560 //Diff => received partially
3561 //$ret=$this->setStatus($user,4);
3562 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3563 if ($ret < 0) {
3564 return -1;
3565 }
3566 return 4;
3567 }
3568 } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
3569 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3570
3571 $close = 0;
3572
3573 if (count($diff_array) > 0) {
3574 //there are some difference between the two arrays
3575
3576 //scan the array of results
3577 foreach ($diff_array as $key => $value) {
3578 //if the quantity delivered is greater or equal to wish quantity
3579 if ($qtydelivered[$key] >= $qtywished[$key]) {
3580 $close++;
3581 }
3582 }
3583 }
3584
3585
3586 if ($close == count($diff_array)) {
3587 //all the products are received equal or more than the wished quantity
3588 if ($closeopenorder) {
3589 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3590 if ($ret < 0) {
3591 return -1;
3592 }
3593 return 5;
3594 } else {
3595 //Diff => received partially
3596 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3597 if ($ret < 0) {
3598 return -1;
3599 }
3600 return 4;
3601 }
3602 } else {
3603 //all the products are not received
3604 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3605 if ($ret < 0) {
3606 return -1;
3607 }
3608 return 4;
3609 }
3610 } else {
3611 //Diff => received partially
3612 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3613 if ($ret < 0) {
3614 return -1;
3615 }
3616 return 4;
3617 }
3618 }
3619 return 1;
3620 }
3621 }
3622 return 0;
3623 }
3624
3632 public function loadReceptions($filtre_statut = -1)
3633 {
3634 $this->receptions = array();
3635
3636 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3637
3638 $sql = 'SELECT cd.rowid, cd.fk_product,';
3639 $sql .= ' sum(cfd.qty) as qty';
3640 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3641 if ($filtre_statut >= 0) {
3642 $sql .= ' '.MAIN_DB_PREFIX.'reception as e,';
3643 }
3644 $sql .= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3645 $sql .= ' WHERE';
3646 if ($filtre_statut >= 0) {
3647 $sql .= ' cfd.fk_reception = e.rowid AND';
3648 }
3649 $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3650 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3651 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3652 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3653 }
3654 if ($filtre_statut >= 0) {
3655 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3656 }
3657 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3658
3659 $resql = $this->db->query($sql);
3660 if ($resql) {
3661 $num = $this->db->num_rows($resql);
3662 $i = 0;
3663 while ($i < $num) {
3664 $obj = $this->db->fetch_object($resql);
3665 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3666 $i++;
3667 }
3668 $this->db->free($resql);
3669
3670 return $num;
3671 } else {
3672 $this->error = $this->db->lasterror();
3673 return -1;
3674 }
3675 }
3676
3684 public function getKanbanView($option = '', $arraydata = null)
3685 {
3686 global $langs;
3687
3688 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3689
3690 $return = '<div class="box-flex-item box-flex-grow-zero">';
3691 $return .= '<div class="info-box info-box-sm">';
3692 $return .= '<span class="info-box-icon bg-infobox-action">';
3693 $return .= img_picto('', $this->picto);
3694 $return .= '</span>';
3695 $return .= '<div class="info-box-content">';
3696 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3697 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3698 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3699 $return .='<br><span class="info-box-label amount">'.$this->socid.'</span>';
3700 }
3701 if (property_exists($this, 'billed')) {
3702 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3703 }
3704 if (method_exists($this, 'getLibStatut')) {
3705 $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3706 }
3707 $return .= '</div>';
3708 $return .= '</div>';
3709 $return .= '</div>';
3710 return $return;
3711 }
3712}
3713
3714
3715
3720{
3724 public $element = 'commande_fournisseurdet';
3725
3729 public $table_element = 'commande_fournisseurdet';
3730
3731 public $oldline;
3732
3737 public $fk_commande;
3738
3739 // From llx_commande_fournisseurdet
3743 public $fk_parent_line;
3744
3748 public $fk_facture;
3749
3750 public $rang = 0;
3751 public $special_code = 0;
3752
3757 public $pu_ht;
3758
3759 public $date_start;
3760 public $date_end;
3761
3762 // From llx_product_fournisseur_price
3763
3768 public $ref_supplier;
3769
3775 public $ref_fourn;
3776
3777 public $remise;
3778
3779
3785 public function __construct($db)
3786 {
3787 $this->db = $db;
3788 }
3789
3796 public function fetch($rowid)
3797 {
3798 global $conf;
3799
3800 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3801 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3802 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3803 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3804 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3805 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3806 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3807 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3808 $sql .= ' c.fk_soc as socid';
3809 $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3810 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3811 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3812
3813 $result = $this->db->query($sql);
3814 if ($result) {
3815 $objp = $this->db->fetch_object($result);
3816
3817 if (!empty($objp)) {
3818 $this->rowid = $objp->rowid;
3819 $this->id = $objp->rowid;
3820 $this->fk_commande = $objp->fk_commande;
3821 $this->desc = $objp->description;
3822 $this->qty = $objp->qty;
3823 $this->ref_fourn = $objp->ref_supplier;
3824 $this->ref_supplier = $objp->ref_supplier;
3825 $this->subprice = $objp->subprice;
3826 $this->tva_tx = $objp->tva_tx;
3827 $this->localtax1_tx = $objp->localtax1_tx;
3828 $this->localtax2_tx = $objp->localtax2_tx;
3829 $this->localtax1_type = $objp->localtax1_type;
3830 $this->localtax2_type = $objp->localtax2_type;
3831 $this->remise = $objp->remise;
3832 $this->remise_percent = $objp->remise_percent;
3833 $this->fk_product = $objp->fk_product;
3834 $this->info_bits = $objp->info_bits;
3835 $this->total_ht = $objp->total_ht;
3836 $this->total_tva = $objp->total_tva;
3837 $this->total_localtax1 = $objp->total_localtax1;
3838 $this->total_localtax2 = $objp->total_localtax2;
3839 $this->total_ttc = $objp->total_ttc;
3840 $this->product_type = $objp->product_type;
3841 $this->special_code = $objp->special_code;
3842
3843 $this->ref = $objp->product_ref;
3844
3845 $this->product_ref = $objp->product_ref;
3846 $this->product_label = $objp->product_label;
3847 $this->product_desc = $objp->product_desc;
3848
3849 if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
3850 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3851 // Move this into another method and call it when required.
3852
3853 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3854 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
3855 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3856 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3857 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3858 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3859 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3860 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3861 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3862 $sqlsearchpackage .= " LIMIT 1";
3863
3864 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3865 if ($resqlsearchpackage) {
3866 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3867 if ($objsearchpackage) {
3868 $this->fk_fournprice = $objsearchpackage->rowid;
3869 $this->packaging = $objsearchpackage->packaging;
3870 }
3871 } else {
3872 $this->error = $this->db->lasterror();
3873 return -1;
3874 }
3875 }
3876
3877 $this->date_start = $this->db->jdate($objp->date_start);
3878 $this->date_end = $this->db->jdate($objp->date_end);
3879 $this->fk_unit = $objp->fk_unit;
3880
3881 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3882 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3883 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3884 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3885
3886 $this->fetch_optionals();
3887
3888 $this->db->free($result);
3889 return 1;
3890 } else {
3891 $this->error = 'Supplier order line with id='.$rowid.' not found';
3892 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3893 return 0;
3894 }
3895 } else {
3896 dol_print_error($this->db);
3897 return -1;
3898 }
3899 }
3900
3907 public function insert($notrigger = 0)
3908 {
3909 global $conf, $user;
3910
3911 $error = 0;
3912
3913 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3914
3915 // Clean parameters
3916 if (empty($this->tva_tx)) {
3917 $this->tva_tx = 0;
3918 }
3919 if (empty($this->localtax1_tx)) {
3920 $this->localtax1_tx = 0;
3921 }
3922 if (empty($this->localtax2_tx)) {
3923 $this->localtax2_tx = 0;
3924 }
3925 if (empty($this->localtax1_type)) {
3926 $this->localtax1_type = '0';
3927 }
3928 if (empty($this->localtax2_type)) {
3929 $this->localtax2_type = '0';
3930 }
3931 if (empty($this->total_localtax1)) {
3932 $this->total_localtax1 = 0;
3933 }
3934 if (empty($this->total_localtax2)) {
3935 $this->total_localtax2 = 0;
3936 }
3937 if (empty($this->rang)) {
3938 $this->rang = 0;
3939 }
3940 if (empty($this->remise_percent)) {
3941 $this->remise_percent = 0;
3942 }
3943 if (empty($this->info_bits)) {
3944 $this->info_bits = 0;
3945 }
3946 if (empty($this->special_code)) {
3947 $this->special_code = 0;
3948 }
3949 if (empty($this->fk_parent_line)) {
3950 $this->fk_parent_line = 0;
3951 }
3952 if (empty($this->pa_ht)) {
3953 $this->pa_ht = 0;
3954 }
3955
3956 // Multicurrency
3957 if (!empty($this->multicurrency_code)) {
3958 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3959 }
3960 if (empty($this->fk_multicurrency)) {
3961 $this->multicurrency_code = $conf->currency;
3962 $this->fk_multicurrency = 0;
3963 $this->multicurrency_tx = 1;
3964 }
3965
3966 // Check parameters
3967 if ($this->product_type < 0) {
3968 return -1;
3969 }
3970
3971 $this->db->begin();
3972
3973 // Insertion dans base de la ligne
3974 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3975 $sql .= " (fk_commande, label, description, date_start, date_end,";
3976 $sql .= " fk_product, product_type, special_code, rang,";
3977 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3978 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3979 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3980 $sql .= ")";
3981 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3982 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3983 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3984 if ($this->fk_product) {
3985 $sql .= $this->fk_product.",";
3986 } else {
3987 $sql .= "null,";
3988 }
3989 $sql .= "'".$this->db->escape($this->product_type)."',";
3990 $sql .= "'".$this->db->escape($this->special_code)."',";
3991 $sql .= "'".$this->db->escape($this->rang)."',";
3992 $sql .= "'".$this->db->escape($this->qty)."', ";
3993 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3994 $sql .= " ".price2num($this->tva_tx).", ";
3995 $sql .= " ".price2num($this->localtax1_tx).",";
3996 $sql .= " ".price2num($this->localtax2_tx).",";
3997 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3998 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3999 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
4000 $sql .= " ".price2num($this->total_ht).",";
4001 $sql .= " ".price2num($this->total_tva).",";
4002 $sql .= " ".price2num($this->total_localtax1).",";
4003 $sql .= " ".price2num($this->total_localtax2).",";
4004 $sql .= " ".price2num($this->total_ttc).",";
4005 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
4006 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
4007 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4008 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
4009 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
4010 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
4011 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
4012 $sql .= ")";
4013
4014 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4015 $resql = $this->db->query($sql);
4016 if ($resql) {
4017 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4018 $this->rowid = $this->id;
4019
4020 if (!$error) {
4021 $result = $this->insertExtraFields();
4022 if ($result < 0) {
4023 $error++;
4024 }
4025 }
4026
4027 if (!$error && !$notrigger) {
4028 // Call trigger
4029 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
4030 if ($result < 0) {
4031 $error++;
4032 }
4033 // End call triggers
4034 }
4035
4036 if (!$error) {
4037 $this->db->commit();
4038 return 1;
4039 }
4040
4041 foreach ($this->errors as $errmsg) {
4042 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4043 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4044 }
4045 $this->db->rollback();
4046 return -1 * $error;
4047 } else {
4048 $this->errors[] = $this->db->error();
4049 $this->db->rollback();
4050 return -2;
4051 }
4052 }
4059 public function update($notrigger = 0)
4060 {
4061 global $conf, $user;
4062
4063 $error = 0;
4064
4065 $this->db->begin();
4066
4067 // Mise a jour ligne en base
4068 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
4069 $sql .= " description='".$this->db->escape($this->desc)."'";
4070 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4071 $sql .= ", subprice='".price2num($this->subprice)."'";
4072 //$sql.= ",remise='".price2num($remise)."'";
4073 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4074
4075 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4076 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4077 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4078 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4079 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4080 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4081 $sql .= ", qty='".price2num($this->qty)."'";
4082 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4083 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4084 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4085 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4086 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4087 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4088 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4089 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4090 $sql .= ", product_type=".$this->product_type;
4091 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4092 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4093
4094 // Multicurrency
4095 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4096 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4097 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4098 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4099
4100 $sql .= " WHERE rowid = ".((int) $this->id);
4101
4102 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4103 $resql = $this->db->query($sql);
4104 if ($resql) {
4105 if (!$error) {
4106 $result = $this->insertExtraFields();
4107 if ($result < 0) {
4108 $error++;
4109 }
4110 }
4111
4112 if (!$error && !$notrigger) {
4113 // Call trigger
4114 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4115 if ($result < 0) {
4116 $this->db->rollback();
4117 return -1;
4118 }
4119 // End call triggers
4120 }
4121
4122 if (!$error) {
4123 $this->db->commit();
4124 return 1;
4125 } else {
4126 $this->db->rollback();
4127 return -1;
4128 }
4129 } else {
4130 $this->error = $this->db->lasterror();
4131 $this->db->rollback();
4132 return -1;
4133 }
4134 }
4135
4142 public function delete($notrigger = 0)
4143 {
4144 global $user;
4145
4146 $error = 0;
4147
4148 $this->db->begin();
4149
4150 // extrafields
4151 $result = $this->deleteExtraFields();
4152 if ($result < 0) {
4153 $this->db->rollback();
4154 return -1;
4155 }
4156
4157 $sql1 = "UPDATE ".MAIN_DB_PREFIX."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4158 $resql = $this->db->query($sql1);
4159 if (!$resql) {
4160 $this->db->rollback();
4161 return -1;
4162 }
4163
4164 $sql2 = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4165
4166 dol_syslog(__METHOD__, LOG_DEBUG);
4167 $resql = $this->db->query($sql2);
4168 if ($resql) {
4169 if (!$notrigger) {
4170 // Call trigger
4171 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4172 if ($result < 0) {
4173 $error++;
4174 }
4175 // End call triggers
4176 }
4177
4178 if (!$error) {
4179 $this->db->commit();
4180 return 1;
4181 }
4182
4183 foreach ($this->errors as $errmsg) {
4184 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4185 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4186 }
4187 $this->db->rollback();
4188 return -1 * $error;
4189 } else {
4190 $this->error = $this->db->lasterror();
4191 return -1;
4192 }
4193 }
4194}
$object ref
Definition info.php:78
Class to manage table commandefournisseurdispatch.
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.
deleteline($idline, $notrigger=0)
Delete line.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
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: ...
$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 informations d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
getNextNumRef($soc)
Returns the following order reference not used depending on the numbering model activated defined wit...
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=false, $date_start=null, $date_end=null, $array_options=0, $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
calcAndSetStatusDispatch(User $user, $closeopenorder=1, $comment='')
Calc status regarding to dispatched stock.
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='', $date_end='', $array_options=0, $fk_unit=null, $pu_ht_devise=0, $ref_supplier='')
Update line.
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 (commande_fournisseur_dispatch) and add product...
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.
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)
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.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
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.
get_methodes_commande()
Get list of order methods.
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.
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.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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.
print $langs trans("Ref").' m m m statut
Definition index.php:152
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($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:62
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 '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
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.
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...
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e rowid
Definition invoice.php:1632
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:86