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