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