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