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