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