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 $this->db->commit();
1601 return $this->id;
1602 } else {
1603 $this->error = $this->db->lasterror();
1604 $this->db->rollback();
1605
1606 return -2;
1607 }
1608 } else {
1609 $this->error = 'Failed to get ID of inserted line';
1610
1611 return -1;
1612 }
1613 } else {
1614 $this->error = $this->db->lasterror();
1615 $this->db->rollback();
1616
1617 return -1;
1618 }
1619 }
1620
1628 public function update(User $user, $notrigger = 0)
1629 {
1630 global $conf;
1631
1632 $error = 0;
1633
1634 // Clean parameters
1635 if (isset($this->ref)) {
1636 $this->ref = trim($this->ref);
1637 }
1638 if (isset($this->ref_supplier)) {
1639 $this->ref_supplier = trim($this->ref_supplier);
1640 }
1641 if (isset($this->note_private)) {
1642 $this->note_private = trim($this->note_private);
1643 }
1644 if (isset($this->note_public)) {
1645 $this->note_public = trim($this->note_public);
1646 }
1647 if (isset($this->model_pdf)) {
1648 $this->model_pdf = trim($this->model_pdf);
1649 }
1650 if (isset($this->import_key)) {
1651 $this->import_key = trim($this->import_key);
1652 }
1653
1654 // Update request
1655 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1656
1657 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1658 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1659 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1660 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1661 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1662 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1663 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1664 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1665 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1666 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1667 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1668 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1669 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1670 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1671 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1672 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1673 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1674 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1675 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1676 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1677 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1678 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1679 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1680 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1681 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1682
1683 $sql .= " WHERE rowid=".((int) $this->id);
1684
1685 $this->db->begin();
1686
1687 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1688 $resql = $this->db->query($sql);
1689 if (!$resql) {
1690 $error++;
1691 $this->errors[] = "Error ".$this->db->lasterror();
1692 }
1693
1694 if (!$error) {
1695 $result = $this->insertExtraFields();
1696 if ($result < 0) {
1697 $error++;
1698 }
1699 }
1700
1701 if (!$error && !$notrigger) {
1702 // Call trigger
1703 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1704 if ($result < 0) {
1705 $error++;
1706 }
1707 // End call triggers
1708 }
1709
1710 // Commit or rollback
1711 if ($error) {
1712 foreach ($this->errors as $errmsg) {
1713 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1714 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1715 }
1716 $this->db->rollback();
1717 return -1 * $error;
1718 } else {
1719 $this->db->commit();
1720 return 1;
1721 }
1722 }
1723
1732 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1733 {
1734 global $conf, $user, $hookmanager;
1735
1736 $error = 0;
1737
1738 $this->db->begin();
1739
1740 // get extrafields so they will be clone
1741 foreach ($this->lines as $line) {
1742 $line->fetch_optionals();
1743 }
1744
1745 // Load source object
1746 $objFrom = clone $this;
1747
1748 // Change socid if needed
1749 if (!empty($socid) && $socid != $this->socid) {
1750 $objsoc = new Societe($this->db);
1751
1752 if ($objsoc->fetch($socid) > 0) {
1753 $this->socid = $objsoc->id;
1754 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1755 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1756 $this->fk_project = 0;
1757 $this->fk_delivery_address = 0;
1758 }
1759
1760 // TODO Change product price if multi-prices
1761 }
1762
1763 $this->id = 0;
1764 $this->statut = self::STATUS_DRAFT;
1765
1766 // Clear fields
1767 $this->user_author_id = $user->id;
1768 $this->user_validation_id = 0;
1769 $this->date = '';
1770 $this->date_creation = '';
1771 $this->date_commande = '';
1772 $this->date_validation = '';
1773 $this->ref_supplier = null;
1774 $this->user_approve_id = '';
1775 $this->user_approve_id2 = '';
1776 $this->date_approve = '';
1777 $this->date_approve2 = '';
1778
1779 // Create clone
1780 $this->context['createfromclone'] = 'createfromclone';
1781 $result = $this->create($user, $notrigger);
1782 if ($result < 0) {
1783 $error++;
1784 }
1785
1786 if (!$error) {
1787 // Hook of thirdparty module
1788 if (is_object($hookmanager)) {
1789 $parameters = array('objFrom'=>$objFrom);
1790 $action = '';
1791 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1792 if ($reshook < 0) {
1793 $this->setErrorsFromObject($hookmanager);
1794 $error++;
1795 }
1796 }
1797 }
1798
1799 unset($this->context['createfromclone']);
1800
1801 // End
1802 if (!$error) {
1803 $this->db->commit();
1804 return $this->id;
1805 } else {
1806 $this->db->rollback();
1807 return -1;
1808 }
1809 }
1810
1840 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)
1841 {
1842 global $langs, $mysoc, $conf;
1843
1844 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");
1845 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1846
1847 if ($this->statut == self::STATUS_DRAFT) {
1848 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1849
1850 // Clean parameters
1851 if (empty($qty)) {
1852 $qty = 0;
1853 }
1854 if (!$info_bits) {
1855 $info_bits = 0;
1856 }
1857 if (empty($txtva)) {
1858 $txtva = 0;
1859 }
1860 if (empty($rang)) {
1861 $rang = 0;
1862 }
1863 if (empty($txlocaltax1)) {
1864 $txlocaltax1 = 0;
1865 }
1866 if (empty($txlocaltax2)) {
1867 $txlocaltax2 = 0;
1868 }
1869 if (empty($remise_percent)) {
1870 $remise_percent = 0;
1871 }
1872
1873 $remise_percent = price2num($remise_percent);
1874 $qty = price2num($qty);
1875 $pu_ht = price2num($pu_ht);
1876 $pu_ht_devise = price2num($pu_ht_devise);
1877 $pu_ttc = price2num($pu_ttc);
1878 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1879 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1880 }
1881 $txlocaltax1 = price2num($txlocaltax1);
1882 $txlocaltax2 = price2num($txlocaltax2);
1883 if ($price_base_type == 'HT') {
1884 $pu = $pu_ht;
1885 } else {
1886 $pu = $pu_ttc;
1887 }
1888 $desc = trim($desc);
1889
1890 // Check parameters
1891 if ($qty < 0 && !$fk_product) {
1892 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1893 return -1;
1894 }
1895 if ($type < 0) {
1896 return -1;
1897 }
1898 if ($date_start && $date_end && $date_start > $date_end) {
1899 $langs->load("errors");
1900 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1901 return -1;
1902 }
1903
1904
1905 $this->db->begin();
1906
1907 $product_type = $type;
1908 $label = ''; // deprecated
1909
1910 if ($fk_product > 0) {
1911 if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
1912 // Check quantity is enough
1913 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);
1914 $prod = new ProductFournisseur($this->db);
1915 if ($prod->fetch($fk_product) > 0) {
1916 $product_type = $prod->type;
1917 $label = $prod->label;
1918
1919 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1920 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1921 $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
1922
1923 // If supplier order created from sales order, we take best supplier price
1924 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1925 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1926 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1927 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1928 // is remise percent not keyed but present for the product we add it
1929 if ($remise_percent == 0 && $prod->remise_percent != 0) {
1930 $remise_percent = $prod->remise_percent;
1931 }
1932 }
1933 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1934 $langs->load("errors");
1935 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1936 $this->db->rollback();
1937 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1938 //$pu = $prod->fourn_pu; // We do not overwrite unit price
1939 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1940 return -1;
1941 }
1942 if ($result == -1) {
1943 $langs->load("errors");
1944 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1945 $this->db->rollback();
1946 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1947 return -1;
1948 }
1949 if ($result < -1) {
1950 $this->error = $prod->error;
1951 $this->db->rollback();
1952 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1953 return -1;
1954 }
1955 } else {
1956 $this->error = $prod->error;
1957 $this->db->rollback();
1958 return -1;
1959 }
1960 }
1961
1962 // Predefine quantity according to packaging
1963 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
1964 $prod = new Product($this->db);
1965 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
1966
1967 if ($qty < $prod->packaging) {
1968 $qty = $prod->packaging;
1969 } else {
1970 if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1971 $coeff = intval($qty / $prod->packaging) + 1;
1972 $qty = $prod->packaging * $coeff;
1973 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1974 }
1975 }
1976 }
1977 }
1978
1979 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1980 $pu = 0;
1981 }
1982
1983 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1984
1985 // Clean vat code
1986 $reg = array();
1987 $vat_src_code = '';
1988 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1989 $vat_src_code = $reg[1];
1990 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1991 }
1992
1993 // Calcul du total TTC et de la TVA pour la ligne a partir de
1994 // qty, pu, remise_percent et txtva
1995 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1996 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1997
1998 $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);
1999
2000 $total_ht = $tabprice[0];
2001 $total_tva = $tabprice[1];
2002 $total_ttc = $tabprice[2];
2003 $total_localtax1 = $tabprice[9];
2004 $total_localtax2 = $tabprice[10];
2005 $pu = $pu_ht = $tabprice[3];
2006
2007 // MultiCurrency
2008 $multicurrency_total_ht = $tabprice[16];
2009 $multicurrency_total_tva = $tabprice[17];
2010 $multicurrency_total_ttc = $tabprice[18];
2011 $pu_ht_devise = $tabprice[19];
2012
2013 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2014 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2015
2016 if ($rang < 0) {
2017 $rangmax = $this->line_max();
2018 $rang = $rangmax + 1;
2019 }
2020
2021 // Insert line
2022 $this->line = new CommandeFournisseurLigne($this->db);
2023
2024 $this->line->context = $this->context;
2025
2026 $this->line->fk_commande = $this->id;
2027 $this->line->label = $label;
2028 $this->line->ref_fourn = $ref_supplier;
2029 $this->line->ref_supplier = $ref_supplier;
2030 $this->line->desc = $desc;
2031 $this->line->qty = $qty;
2032 $this->line->tva_tx = $txtva;
2033 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2034 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2035 $this->line->localtax1_type = $localtax1_type;
2036 $this->line->localtax2_type = $localtax2_type;
2037 $this->line->fk_product = $fk_product;
2038 $this->line->product_type = $product_type;
2039 $this->line->remise_percent = $remise_percent;
2040 $this->line->subprice = $pu_ht;
2041 $this->line->rang = $rang;
2042 $this->line->info_bits = $info_bits;
2043
2044 $this->line->vat_src_code = $vat_src_code;
2045 $this->line->total_ht = $total_ht;
2046 $this->line->total_tva = $total_tva;
2047 $this->line->total_localtax1 = $total_localtax1;
2048 $this->line->total_localtax2 = $total_localtax2;
2049 $this->line->total_ttc = $total_ttc;
2050 $this->line->product_type = $type;
2051 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2052 $this->line->origin = $origin;
2053 $this->line->origin_id = $origin_id;
2054 $this->line->fk_unit = $fk_unit;
2055
2056 $this->line->date_start = $date_start;
2057 $this->line->date_end = $date_end;
2058
2059 // Multicurrency
2060 $this->line->fk_multicurrency = $this->fk_multicurrency;
2061 $this->line->multicurrency_code = $this->multicurrency_code;
2062 $this->line->multicurrency_subprice = $pu_ht_devise;
2063 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2064 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2065 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2066
2067 $this->line->subprice = $pu_ht;
2068 $this->line->price = $this->line->subprice;
2069
2070 $this->line->remise_percent = $remise_percent;
2071
2072 if (is_array($array_options) && count($array_options) > 0) {
2073 $this->line->array_options = $array_options;
2074 }
2075
2076 $result = $this->line->insert($notrigger);
2077 if ($result > 0) {
2078 // Reorder if child line
2079 if (!empty($this->line->fk_parent_line)) {
2080 $this->line_order(true, 'DESC');
2081 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2082 $linecount = count($this->lines);
2083 for ($ii = $rang; $ii <= $linecount; $ii++) {
2084 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2085 }
2086 }
2087
2088 // Mise a jour informations denormalisees au niveau de la commande meme
2089 $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.
2090 if ($result > 0) {
2091 $this->db->commit();
2092 return $this->line->id;
2093 } else {
2094 $this->db->rollback();
2095 return -1;
2096 }
2097 } else {
2098 $this->error = $this->line->error;
2099 $this->errors = $this->line->errors;
2100 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2101 $this->db->rollback();
2102 return -1;
2103 }
2104 }
2105 return -1;
2106 }
2107
2108
2126 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2127 {
2128 global $conf, $langs;
2129
2130 $error = 0;
2131 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2132
2133 // Check parameters (if test are wrong here, there is bug into caller)
2134 if ($entrepot <= 0) {
2135 $this->error = 'ErrorBadValueForParameterWarehouse';
2136 return -1;
2137 }
2138 if ($qty == 0) {
2139 $this->error = 'ErrorBadValueForParameterQty';
2140 return -1;
2141 }
2142
2143 $dispatchstatus = 1;
2144 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2145 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2146 }
2147
2148 $now = dol_now();
2149
2150 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2151
2152 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2153 $this->db->begin();
2154
2155 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur_dispatch";
2156 $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2157 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2158 $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");
2159 $sql .= ")";
2160
2161 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2162 $resql = $this->db->query($sql);
2163 if ($resql) {
2164 if (!$notrigger) {
2165 global $conf, $langs, $user;
2166 // Call trigger
2167 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2168 if ($result < 0) {
2169 $error++;
2170 }
2171 // End call triggers
2172 }
2173 } else {
2174 $this->error = $this->db->lasterror();
2175 $error++;
2176 }
2177
2178 // If module stock is enabled and the stock increase is done on purchase order dispatching
2179 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2180 $mouv = new MouvementStock($this->db);
2181 if ($product > 0) {
2182 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2183 $mouv->origin = &$this;
2184 $mouv->setOrigin($this->element, $this->id);
2185
2186 // Method change if qty < 0
2187 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2188 $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2189 } else {
2190 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2191 }
2192
2193 if ($result < 0) {
2194 $this->error = $mouv->error;
2195 $this->errors = $mouv->errors;
2196 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2197 $error++;
2198 }
2199 }
2200 }
2201
2202 if ($error == 0) {
2203 $this->db->commit();
2204 return 1;
2205 } else {
2206 $this->db->rollback();
2207 return -1;
2208 }
2209 } else {
2210 $this->error = 'BadStatusForObject';
2211 return -2;
2212 }
2213 }
2214
2222 public function deleteline($idline, $notrigger = 0)
2223 {
2224 if ($this->statut == 0) {
2225 $line = new CommandeFournisseurLigne($this->db);
2226
2227 if ($line->fetch($idline) <= 0) {
2228 return 0;
2229 }
2230
2231 // check if not yet received
2232 $dispatchedLines = $this->getDispachedLines();
2233 foreach ($dispatchedLines as $dispatchLine) {
2234 if ($dispatchLine['orderlineid'] == $idline) {
2235 $this->error = "LineAlreadyDispatched";
2236 $this->errors[] = $this->error;
2237 return -3;
2238 }
2239 }
2240
2241 if ($line->delete($notrigger) > 0) {
2242 $this->update_price(1);
2243 return 1;
2244 } else {
2245 $this->error = $line->error;
2246 $this->errors = $line->errors;
2247 return -1;
2248 }
2249 } else {
2250 return -2;
2251 }
2252 }
2253
2261 public function delete(User $user, $notrigger = 0)
2262 {
2263 global $langs, $conf;
2264 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2265
2266 $error = 0;
2267
2268 $this->db->begin();
2269
2270 if (empty($notrigger)) {
2271 // Call trigger
2272 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2273 if ($result < 0) {
2274 $this->errors[] = 'ErrorWhenRunningTrigger';
2275 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2276 $this->db->rollback();
2277 return -1;
2278 }
2279 // End call triggers
2280 }
2281
2282 // Test we can delete
2283 $this->fetchObjectLinked(null, 'order_supplier');
2284 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2285 foreach ($this->linkedObjects['reception'] as $element) {
2286 if ($element->statut >= 0) {
2287 $this->errors[] = $langs->trans('ReceptionExist');
2288 $error++;
2289 break;
2290 }
2291 }
2292 }
2293
2294 $main = $this->db->prefix().'commande_fournisseurdet';
2295 $ef = $main."_extrafields";
2296 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2297 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2298 if (!$this->db->query($sql)) {
2299 $this->error = $this->db->lasterror();
2300 $this->errors[] = $this->db->lasterror();
2301 $error++;
2302 }
2303
2304 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2305 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2306 if (!$this->db->query($sql)) {
2307 $this->error = $this->db->lasterror();
2308 $this->errors[] = $this->db->lasterror();
2309 $error++;
2310 }
2311
2312 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2313 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2314 if ($resql = $this->db->query($sql)) {
2315 if ($this->db->affected_rows($resql) < 1) {
2316 $this->error = $this->db->lasterror();
2317 $this->errors[] = $this->db->lasterror();
2318 $error++;
2319 }
2320 } else {
2321 $this->error = $this->db->lasterror();
2322 $this->errors[] = $this->db->lasterror();
2323 $error++;
2324 }
2325
2326 // Remove extrafields
2327 if (!$error) {
2328 $result = $this->deleteExtraFields();
2329 if ($result < 0) {
2330 $this->error = 'FailToDeleteExtraFields';
2331 $this->errors[] = 'FailToDeleteExtraFields';
2332 $error++;
2333 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2334 }
2335 }
2336
2337 // Delete linked object
2338 $res = $this->deleteObjectLinked();
2339 if ($res < 0) {
2340 $this->error = 'FailToDeleteObjectLinked';
2341 $this->errors[] = 'FailToDeleteObjectLinked';
2342 $error++;
2343 }
2344
2345 if (!$error) {
2346 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2347 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2348 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2349
2350 // We remove directory
2351 $ref = dol_sanitizeFileName($this->ref);
2352 if ($conf->fournisseur->commande->dir_output) {
2353 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2354 $file = $dir."/".$ref.".pdf";
2355 if (file_exists($file)) {
2356 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2357 $this->error = 'ErrorFailToDeleteFile';
2358 $this->errors[] = 'ErrorFailToDeleteFile';
2359 $error++;
2360 }
2361 }
2362 if (file_exists($dir)) {
2363 $res = @dol_delete_dir_recursive($dir);
2364 if (!$res) {
2365 $this->error = 'ErrorFailToDeleteDir';
2366 $this->errors[] = 'ErrorFailToDeleteDir';
2367 $error++;
2368 }
2369 }
2370 }
2371 }
2372
2373 if (!$error) {
2374 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2375 $this->db->commit();
2376 return 1;
2377 } else {
2378 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2379 $this->db->rollback();
2380 return -$error;
2381 }
2382 }
2383
2384
2393 public function getDispachedLines($status = -1)
2394 {
2395 $ret = array();
2396
2397 // List of already dispatched lines
2398 $sql = "SELECT p.ref, p.label,";
2399 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2400 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_commandefourndet";
2401 $sql .= " FROM ".$this->db->prefix()."product as p,";
2402 $sql .= " ".$this->db->prefix()."commande_fournisseur_dispatch as cfd";
2403 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2404 $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2405 $sql .= " AND cfd.fk_product = p.rowid";
2406 if ($status >= 0) {
2407 $sql .= " AND cfd.status = ".((int) $status);
2408 }
2409 $sql .= " ORDER BY cfd.rowid ASC";
2410
2411 $resql = $this->db->query($sql);
2412 if ($resql) {
2413 $num = $this->db->num_rows($resql);
2414 $i = 0;
2415
2416 while ($i < $num) {
2417 $objp = $this->db->fetch_object($resql);
2418 if ($objp) {
2419 $ret[] = array(
2420 'id' => $objp->dispatchedlineid,
2421 'productid' => $objp->fk_product,
2422 'warehouseid' => $objp->warehouse_id,
2423 'qty' => $objp->qty,
2424 'orderlineid' => $objp->fk_commandefourndet
2425 );
2426 }
2427
2428 $i++;
2429 }
2430 } else {
2431 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2432 }
2433
2434 return $ret;
2435 }
2436
2437 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2447 public function Livraison($user, $date, $type, $comment)
2448 {
2449 // phpcs:enable
2450 global $conf, $langs;
2451
2452 $result = 0;
2453 $error = 0;
2454
2455 dol_syslog(get_class($this)."::Livraison");
2456
2457 $usercanreceive = 0;
2458 if (!isModEnabled('reception')) {
2459 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2460 } else {
2461 $usercanreceive = $user->hasRight("reception", "creer");
2462 }
2463
2464 if ($usercanreceive) {
2465 // Define the new status
2466 if ($type == 'par') {
2468 } elseif ($type == 'tot') {
2470 } elseif ($type == 'nev') {
2472 } elseif ($type == 'can') {
2474 } else {
2475 $error++;
2476 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2477 return -2;
2478 }
2479
2480 // Some checks to accept the record
2481 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2482 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2483 if (!$error && ($type == 'tot')) {
2484 $dispatchedlinearray = $this->getDispachedLines(0);
2485 if (count($dispatchedlinearray) > 0) {
2486 $result = -1;
2487 $error++;
2488 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2489 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2490 }
2491 }
2492 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)
2493 $dispatcheddenied = $this->getDispachedLines(2);
2494 if (count($dispatchedlinearray) > 0) {
2495 $result = -1;
2496 $error++;
2497 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2498 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2499 }
2500 }
2501 }
2502
2503 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2504
2505 if (empty($error)) {
2506 $this->db->begin();
2507
2508 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2509 $sql .= " SET fk_statut = ".((int) $statut);
2510 $sql .= " WHERE rowid = ".((int) $this->id);
2511 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2512
2513 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2514 $resql = $this->db->query($sql);
2515 if ($resql) {
2516 $result = 1;
2517 $old_statut = $this->statut;
2518 $this->statut = $statut;
2519 $this->context['actionmsg2'] = $comment;
2520
2521 // Call trigger
2522 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2523 if ($result_trigger < 0) {
2524 $error++;
2525 }
2526 // End call triggers
2527
2528 if (empty($error)) {
2529 $this->db->commit();
2530 } else {
2531 $this->statut = $old_statut;
2532 $this->db->rollback();
2533 $this->error = $this->db->lasterror();
2534 $result = -1;
2535 }
2536 } else {
2537 $this->db->rollback();
2538 $this->error = $this->db->lasterror();
2539 $result = -1;
2540 }
2541 }
2542 } else {
2543 $this->error = $langs->trans('NotAuthorized');
2544 $this->errors[] = $langs->trans('NotAuthorized');
2545 dol_syslog(get_class($this)."::Livraison Not Authorized");
2546 $result = -3;
2547 }
2548 return $result;
2549 }
2550
2551 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2561 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2562 {
2563 // phpcs:enable
2564 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2565 }
2566
2575 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2576 {
2577 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2578 $error = 0;
2579
2580 $this->db->begin();
2581
2582 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2583 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2584 $sql .= " WHERE rowid = ".((int) $this->id);
2585
2586 dol_syslog(__METHOD__, LOG_DEBUG);
2587 $resql = $this->db->query($sql);
2588 if (!$resql) {
2589 $this->errors[] = $this->db->error();
2590 $error++;
2591 }
2592
2593 if (!$error) {
2594 $this->oldcopy = clone $this;
2595 $this->delivery_date = $delivery_date;
2596 }
2597
2598 if (!$notrigger && empty($error)) {
2599 // Call trigger
2600 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2601 if ($result < 0) {
2602 $error++;
2603 }
2604 // End call triggers
2605 }
2606
2607 if (!$error) {
2608 $this->db->commit();
2609 return 1;
2610 } else {
2611 foreach ($this->errors as $errmsg) {
2612 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2613 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2614 }
2615 $this->db->rollback();
2616 return -1 * $error;
2617 }
2618 } else {
2619 return -2;
2620 }
2621 }
2622
2623 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2632 public function set_id_projet($user, $id_projet, $notrigger = 0)
2633 {
2634 // phpcs:enable
2635 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2636 $error = 0;
2637
2638 $this->db->begin();
2639
2640 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2641 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2642 $sql .= " WHERE rowid = ".((int) $this->id);
2643
2644 dol_syslog(__METHOD__, LOG_DEBUG);
2645 $resql = $this->db->query($sql);
2646 if (!$resql) {
2647 $this->errors[] = $this->db->error();
2648 $error++;
2649 }
2650
2651 if (!$error) {
2652 $this->oldcopy = clone $this;
2653 $this->fk_projet = $id_projet;
2654 $this->fk_project = $id_projet;
2655 }
2656
2657 if (!$notrigger && empty($error)) {
2658 // Call trigger
2659 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2660 if ($result < 0) {
2661 $error++;
2662 }
2663 // End call triggers
2664 }
2665
2666 if (!$error) {
2667 $this->db->commit();
2668 return 1;
2669 } else {
2670 foreach ($this->errors as $errmsg) {
2671 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2672 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2673 }
2674 $this->db->rollback();
2675 return -1 * $error;
2676 }
2677 } else {
2678 return -2;
2679 }
2680 }
2681
2690 public function updateFromCommandeClient($user, $idc, $comclientid)
2691 {
2692 $comclient = new Commande($this->db);
2693 $comclient->fetch($comclientid);
2694
2695 $this->id = $idc;
2696
2697 $this->lines = array();
2698
2699 $num = count($comclient->lines);
2700 for ($i = 0; $i < $num; $i++) {
2701 $prod = new Product($this->db);
2702 $label = '';
2703 $ref = '';
2704 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2705 $label = $prod->label;
2706 $ref = $prod->ref;
2707 }
2708
2709 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2710 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2711 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2712 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2713 $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);
2714 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2715 if ($this->db->query($sql)) {
2716 $this->update_price(1);
2717 }
2718 }
2719
2720 return 1;
2721 }
2722
2730 public function setStatus($user, $status)
2731 {
2732 global $conf, $langs;
2733 $error = 0;
2734
2735 $this->db->begin();
2736
2737 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2738 $sql .= " SET fk_statut = ".$status;
2739 $sql .= " WHERE rowid = ".((int) $this->id);
2740
2741 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2742 $resql = $this->db->query($sql);
2743 if ($resql) {
2744 // Trigger names for each status
2745 $triggerName = array();
2746 $triggerName[0] = 'DRAFT';
2747 $triggerName[1] = 'VALIDATED';
2748 $triggerName[2] = 'APPROVED';
2749 $triggerName[3] = 'ORDERED'; // Ordered
2750 $triggerName[4] = 'RECEIVED_PARTIALLY';
2751 $triggerName[5] = 'RECEIVED_COMPLETELY';
2752 $triggerName[6] = 'CANCELED';
2753 $triggerName[7] = 'CANCELED';
2754 $triggerName[9] = 'REFUSED';
2755
2756 // Call trigger
2757 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2758 if ($result < 0) {
2759 $error++;
2760 }
2761 // End call triggers
2762 } else {
2763 $error++;
2764 $this->error = $this->db->lasterror();
2765 dol_syslog(get_class($this)."::setStatus ".$this->error);
2766 }
2767
2768 if (!$error) {
2769 $this->statut = $status;
2770 $this->db->commit();
2771 return 1;
2772 } else {
2773 $this->db->rollback();
2774 return -1;
2775 }
2776 }
2777
2801 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 = '')
2802 {
2803 global $mysoc, $conf, $langs;
2804 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2805 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2806
2807 $error = 0;
2808
2809 if ($this->statut == self::STATUS_DRAFT) {
2810 // Clean parameters
2811 if (empty($qty)) {
2812 $qty = 0;
2813 }
2814 if (empty($info_bits)) {
2815 $info_bits = 0;
2816 }
2817 if (empty($txtva)) {
2818 $txtva = 0;
2819 }
2820 if (empty($txlocaltax1)) {
2821 $txlocaltax1 = 0;
2822 }
2823 if (empty($txlocaltax2)) {
2824 $txlocaltax2 = 0;
2825 }
2826 if (empty($remise_percent)) {
2827 $remise_percent = 0;
2828 }
2829
2830 $remise_percent = price2num($remise_percent);
2831 $qty = price2num($qty);
2832 if (!$qty) {
2833 $qty = 1;
2834 }
2835 $pu = price2num($pu);
2836 $pu_ht_devise = price2num($pu_ht_devise);
2837 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2838 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2839 }
2840 $txlocaltax1 = price2num($txlocaltax1);
2841 $txlocaltax2 = price2num($txlocaltax2);
2842
2843 // Check parameters
2844 if ($type < 0) {
2845 return -1;
2846 }
2847 if ($date_start && $date_end && $date_start > $date_end) {
2848 $langs->load("errors");
2849 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2850 return -1;
2851 }
2852
2853 $this->db->begin();
2854
2855 // Calcul du total TTC et de la TVA pour la ligne a partir de
2856 // qty, pu, remise_percent et txtva
2857 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2858 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2859
2860 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2861
2862 // Clean vat code
2863 $reg = array();
2864 $vat_src_code = '';
2865 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2866 $vat_src_code = $reg[1];
2867 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2868 }
2869
2870 $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);
2871 $total_ht = $tabprice[0];
2872 $total_tva = $tabprice[1];
2873 $total_ttc = $tabprice[2];
2874 $total_localtax1 = $tabprice[9];
2875 $total_localtax2 = $tabprice[10];
2876 $pu_ht = $tabprice[3];
2877 $pu_tva = $tabprice[4];
2878 $pu_ttc = $tabprice[5];
2879
2880 // MultiCurrency
2881 $multicurrency_total_ht = $tabprice[16];
2882 $multicurrency_total_tva = $tabprice[17];
2883 $multicurrency_total_ttc = $tabprice[18];
2884 $pu_ht_devise = $tabprice[19];
2885
2886 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2887 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2888
2889 //Fetch current line from the database and then clone the object and set it in $oldline property
2890 $this->line = new CommandeFournisseurLigne($this->db);
2891 $this->line->fetch($rowid);
2892
2893 $oldline = clone $this->line;
2894 $this->line->oldline = $oldline;
2895
2896 $this->line->context = $this->context;
2897
2898 $this->line->fk_commande = $this->id;
2899 //$this->line->label=$label;
2900 $this->line->desc = $desc;
2901
2902 // redefine quantity according to packaging
2903 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2904 if ($qty < $this->line->packaging) {
2905 $qty = $this->line->packaging;
2906 } else {
2907 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2908 $coeff = intval($qty / $this->line->packaging) + 1;
2909 $qty = $this->line->packaging * $coeff;
2910 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2911 }
2912 }
2913 }
2914
2915 $this->line->qty = $qty;
2916 $this->line->ref_supplier = $ref_supplier;
2917
2918 $this->line->vat_src_code = $vat_src_code;
2919 $this->line->tva_tx = $txtva;
2920 $this->line->localtax1_tx = $txlocaltax1;
2921 $this->line->localtax2_tx = $txlocaltax2;
2922 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2923 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2924 $this->line->remise_percent = $remise_percent;
2925 $this->line->subprice = $pu_ht;
2926 $this->line->info_bits = $info_bits;
2927 $this->line->total_ht = $total_ht;
2928 $this->line->total_tva = $total_tva;
2929 $this->line->total_localtax1 = $total_localtax1;
2930 $this->line->total_localtax2 = $total_localtax2;
2931 $this->line->total_ttc = $total_ttc;
2932 $this->line->product_type = $type;
2933 $this->line->special_code = $oldline->special_code;
2934 $this->line->rang = $oldline->rang;
2935 $this->line->origin = $this->origin;
2936 $this->line->fk_unit = $fk_unit;
2937
2938 $this->line->date_start = $date_start;
2939 $this->line->date_end = $date_end;
2940
2941 // Multicurrency
2942 $this->line->fk_multicurrency = $this->fk_multicurrency;
2943 $this->line->multicurrency_code = $this->multicurrency_code;
2944 $this->line->multicurrency_subprice = $pu_ht_devise;
2945 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2946 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2947 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2948
2949 $this->line->subprice = $pu_ht;
2950 $this->line->price = $this->line->subprice;
2951
2952 $this->line->remise_percent = $remise_percent;
2953
2954 if (is_array($array_options) && count($array_options) > 0) {
2955 // We replace values in this->line->array_options only for entries defined into $array_options
2956 foreach ($array_options as $key => $value) {
2957 $this->line->array_options[$key] = $array_options[$key];
2958 }
2959 }
2960
2961 $result = $this->line->update($notrigger);
2962
2963
2964 // Mise a jour info denormalisees au niveau facture
2965 if ($result >= 0) {
2966 $this->update_price('1', 'auto');
2967 $this->db->commit();
2968 return $result;
2969 } else {
2970 $this->error = $this->db->lasterror();
2971 $this->db->rollback();
2972 return -1;
2973 }
2974 } else {
2975 $this->error = "Order status makes operation forbidden";
2976 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
2977 return -2;
2978 }
2979 }
2980
2981
2989 public function initAsSpecimen()
2990 {
2991 global $user, $langs, $conf;
2992
2993 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
2994
2995 dol_syslog(get_class($this)."::initAsSpecimen");
2996
2997 $now = dol_now();
2998
2999 // Find first product
3000 $prodid = 0;
3001 $product = new ProductFournisseur($this->db);
3002 $sql = "SELECT rowid";
3003 $sql .= " FROM ".$this->db->prefix()."product";
3004 $sql .= " WHERE entity IN (".getEntity('product').")";
3005 $sql .= $this->db->order("rowid", "ASC");
3006 $sql .= $this->db->plimit(1);
3007 $resql = $this->db->query($sql);
3008 if ($resql && $this->db->num_rows($resql)) {
3009 $obj = $this->db->fetch_object($resql);
3010 $prodid = $obj->rowid;
3011 }
3012
3013 // Initialise parametres
3014 $this->id = 0;
3015 $this->ref = 'SPECIMEN';
3016 $this->specimen = 1;
3017 $this->socid = 1;
3018 $this->date = $now;
3019 $this->date_commande = $now;
3020 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3021 $this->cond_reglement_code = 'RECEP';
3022 $this->mode_reglement_code = 'CHQ';
3023
3024 $this->note_public = 'This is a comment (public)';
3025 $this->note_private = 'This is a comment (private)';
3026
3027 $this->multicurrency_tx = 1;
3028 $this->multicurrency_code = $conf->currency;
3029
3030 $this->statut = 0;
3031
3032 // Lines
3033 $nbp = 5;
3034 $xnbp = 0;
3035 while ($xnbp < $nbp) {
3036 $line = new CommandeFournisseurLigne($this->db);
3037 $line->desc = $langs->trans("Description")." ".$xnbp;
3038 $line->qty = 1;
3039 $line->subprice = 100;
3040 $line->tva_tx = 19.6;
3041 $line->localtax1_tx = 0;
3042 $line->localtax2_tx = 0;
3043 if ($xnbp == 2) {
3044 $line->total_ht = 50;
3045 $line->total_ttc = 59.8;
3046 $line->total_tva = 9.8;
3047 $line->remise_percent = 50;
3048 } else {
3049 $line->total_ht = 100;
3050 $line->total_ttc = 119.6;
3051 $line->total_tva = 19.6;
3052 $line->remise_percent = 00;
3053 }
3054 $line->fk_product = $prodid;
3055
3056 $this->lines[$xnbp] = $line;
3057
3058 $this->total_ht += $line->total_ht;
3059 $this->total_tva += $line->total_tva;
3060 $this->total_ttc += $line->total_ttc;
3061
3062 $xnbp++;
3063 }
3064 }
3065
3072 public function info($id)
3073 {
3074 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3075 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3076 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3077 $sql .= ' WHERE c.rowid = '.((int) $id);
3078
3079 $result = $this->db->query($sql);
3080 if ($result) {
3081 if ($this->db->num_rows($result)) {
3082 $obj = $this->db->fetch_object($result);
3083
3084 $this->id = $obj->rowid;
3085
3086 $this->user_creation_id = $obj->fk_user_author;
3087 $this->user_validation_id = $obj->fk_user_valid;
3088 $this->user_modification_id = $obj->fk_user_modif;
3089 $this->user_approve_id = $obj->fk_user_approve;
3090 $this->user_approve_id2 = $obj->fk_user_approve2;
3091
3092 $this->date_creation = $this->db->jdate($obj->datec);
3093 $this->date_modification = $this->db->jdate($obj->datem);
3094 $this->date_approve = $this->db->jdate($obj->datea);
3095 $this->date_approve2 = $this->db->jdate($obj->datea2);
3096 $this->date_validation = $this->db->jdate($obj->date_validation);
3097 }
3098 $this->db->free($result);
3099 } else {
3100 dol_print_error($this->db);
3101 }
3102 }
3103
3104 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3110 public function load_state_board()
3111 {
3112 // phpcs:enable
3113 global $conf, $user;
3114
3115 $this->nb = array();
3116 $clause = "WHERE";
3117
3118 $sql = "SELECT count(co.rowid) as nb";
3119 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3120 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3121 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3122 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3123 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3124 $clause = "AND";
3125 }
3126 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3127
3128 $resql = $this->db->query($sql);
3129 if ($resql) {
3130 while ($obj = $this->db->fetch_object($resql)) {
3131 $this->nb["supplier_orders"] = $obj->nb;
3132 }
3133 $this->db->free($resql);
3134 return 1;
3135 } else {
3136 dol_print_error($this->db);
3137 $this->error = $this->db->error();
3138 return -1;
3139 }
3140 }
3141
3142 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3150 public function load_board($user, $mode = 'opened')
3151 {
3152 // phpcs:enable
3153 global $conf, $langs;
3154
3155 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3156 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3157 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3158 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3159 }
3160 $sql .= " WHERE c.entity = ".$conf->entity;
3161 if ($mode === 'awaiting') {
3162 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3163 } else {
3164 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3165 }
3166 if ($user->socid) {
3167 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3168 }
3169
3170 $resql = $this->db->query($sql);
3171 if ($resql) {
3172 $commandestatic = new CommandeFournisseur($this->db);
3173
3174 $response = new WorkboardResponse();
3175 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3176 $response->label = $langs->trans("SuppliersOrdersToProcess");
3177 $response->labelShort = $langs->trans("Opened");
3178 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3179 $response->img = img_object('', "order");
3180
3181 if ($mode === 'awaiting') {
3182 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3183 $response->labelShort = $langs->trans("AwaitingReception");
3184 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3185 }
3186
3187 while ($obj = $this->db->fetch_object($resql)) {
3188 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3189 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3190 $commandestatic->statut = $obj->fk_statut;
3191
3192 $response->nbtodo++;
3193 $response->total += $obj->total_ht;
3194
3195 if ($commandestatic->hasDelay()) {
3196 $response->nbtodolate++;
3197 }
3198 }
3199
3200 return $response;
3201 } else {
3202 $this->error = $this->db->error();
3203 return -1;
3204 }
3205 }
3206
3213 public function getInputMethod()
3214 {
3215 global $db, $langs;
3216
3217 if ($this->methode_commande_id > 0) {
3218 $sql = "SELECT rowid, code, libelle as label";
3219 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3220 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3221
3222 $resql = $this->db->query($sql);
3223 if ($resql) {
3224 if ($this->db->num_rows($resql)) {
3225 $obj = $this->db->fetch_object($resql);
3226
3227 $string = $langs->trans($obj->code);
3228 if ($string == $obj->code) {
3229 $string = $obj->label != '-' ? $obj->label : '';
3230 }
3231 return $string;
3232 }
3233 } else {
3234 dol_print_error($this->db);
3235 }
3236 }
3237
3238 return '';
3239 }
3240
3252 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3253 {
3254 global $conf, $langs;
3255
3256 if (!dol_strlen($modele)) {
3257 $modele = ''; // No doc template/generation by default
3258
3259 if (!empty($this->model_pdf)) {
3260 $modele = $this->model_pdf;
3261 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3262 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3263 }
3264 }
3265
3266 if (empty($modele)) {
3267 return 0;
3268 } else {
3269 $langs->load("suppliers");
3270 $outputlangs->load("products");
3271
3272 $modelpath = "core/modules/supplier_order/doc/";
3273 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3274 return $result;
3275 }
3276 }
3277
3284 public function getMaxDeliveryTimeDay($langs)
3285 {
3286 if (empty($this->lines)) {
3287 return '';
3288 }
3289
3290 $obj = new ProductFournisseur($this->db);
3291
3292 $nb = 0;
3293 foreach ($this->lines as $line) {
3294 if ($line->fk_product > 0) {
3295 $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3296 if ($idp) {
3297 $obj->fetch($idp);
3298 if ($obj->delivery_time_days > $nb) {
3299 $nb = $obj->delivery_time_days;
3300 }
3301 }
3302 }
3303 }
3304
3305 if ($nb === 0) {
3306 return '';
3307 } else {
3308 return $nb.' '.$langs->trans('Days');
3309 }
3310 }
3311
3317 public function getRights()
3318 {
3319 global $user;
3320
3321 return $user->hasRight("fournisseur", "commande");
3322 }
3323
3324
3333 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3334 {
3335 $tables = array(
3336 'commande_fournisseur'
3337 );
3338
3339 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3340 }
3341
3350 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3351 {
3352 $tables = array(
3353 'commande_fournisseurdet'
3354 );
3355
3356 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3357 }
3358
3366 public function hasDelay()
3367 {
3368 global $conf;
3369
3370 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3371 $now = dol_now();
3372 if (!empty($this->delivery_date)) {
3373 $date_to_test = $this->delivery_date;
3374 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3375 } else {
3376 //$date_to_test = $this->date_commande;
3377 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3378 return false;
3379 }
3380 } else {
3381 $now = dol_now();
3382 $date_to_test = $this->date_commande;
3383
3384 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3385 }
3386 }
3387
3395 public function showDelay()
3396 {
3397 global $conf, $langs;
3398
3399 $text = '';
3400
3401 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3402 if (!empty($this->delivery_date)) {
3403 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3404 } else {
3405 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3406 }
3407 } else {
3408 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3409 }
3410 if ($text) {
3411 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3412 }
3413
3414 return $text;
3415 }
3416
3417
3426 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3427 {
3428 global $conf, $langs;
3429
3430 if (isModEnabled("supplier_order")) {
3431 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3432
3433 $qtydelivered = array();
3434 $qtywished = array();
3435
3436 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3437 $filter = array('t.fk_commande'=>$this->id);
3438 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3439 $filter['t.status'] = 1; // Restrict to lines with status validated
3440 }
3441
3442 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3443 if ($ret < 0) {
3444 $this->error = $supplierorderdispatch->error;
3445 $this->errors = $supplierorderdispatch->errors;
3446 return $ret;
3447 } else {
3448 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3449 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3450 $date_liv = dol_now();
3451
3452 // Build array with quantity deliverd by product
3453 foreach ($supplierorderdispatch->lines as $line) {
3454 $qtydelivered[$line->fk_product] += $line->qty;
3455 }
3456 foreach ($this->lines as $line) {
3457 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3458 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3459 continue;
3460 }
3461 $qtywished[$line->fk_product] += $line->qty;
3462 }
3463
3464 //Compare array
3465 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3466 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3467 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3468 //var_dump(array_keys($qtydelivered));
3469 //var_dump(array_keys($qtywished));
3470 //var_dump($diff_array);
3471 //var_dump($keysinwishednotindelivered);
3472 //var_dump($keysindeliverednotinwished);
3473 //exit;
3474
3475 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3476 if ($closeopenorder) {
3477 //$ret=$this->setStatus($user,5);
3478 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3479 if ($ret < 0) {
3480 return -1;
3481 }
3482 return 5;
3483 } else {
3484 //Diff => received partially
3485 //$ret=$this->setStatus($user,4);
3486 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3487 if ($ret < 0) {
3488 return -1;
3489 }
3490 return 4;
3491 }
3492 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3493 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3494
3495 $close = 0;
3496
3497 if (count($diff_array) > 0) {
3498 //there are some difference between the two arrays
3499
3500 //scan the array of results
3501 foreach ($diff_array as $key => $value) {
3502 //if the quantity delivered is greater or equal to wish quantity
3503 if ($qtydelivered[$key] >= $qtywished[$key]) {
3504 $close++;
3505 }
3506 }
3507 }
3508
3509
3510 if ($close == count($diff_array)) {
3511 //all the products are received equal or more than the wished quantity
3512 if ($closeopenorder) {
3513 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3514 if ($ret < 0) {
3515 return -1;
3516 }
3517 return 5;
3518 } else {
3519 //Diff => received partially
3520 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3521 if ($ret < 0) {
3522 return -1;
3523 }
3524 return 4;
3525 }
3526 } else {
3527 //all the products are not received
3528 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3529 if ($ret < 0) {
3530 return -1;
3531 }
3532 return 4;
3533 }
3534 } else {
3535 //Diff => received partially
3536 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3537 if ($ret < 0) {
3538 return -1;
3539 }
3540 return 4;
3541 }
3542 }
3543 return 1;
3544 }
3545 }
3546 return 0;
3547 }
3548
3556 public function loadReceptions($filtre_statut = -1)
3557 {
3558 $this->receptions = array();
3559
3560 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3561
3562 $sql = 'SELECT cd.rowid, cd.fk_product,';
3563 $sql .= ' sum(cfd.qty) as qty';
3564 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur_dispatch as cfd,';
3565 if ($filtre_statut >= 0) {
3566 $sql .= ' '.$this->db->prefix().'reception as e,';
3567 }
3568 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3569 $sql .= ' WHERE';
3570 if ($filtre_statut >= 0) {
3571 $sql .= ' cfd.fk_reception = e.rowid AND';
3572 }
3573 $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3574 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3575 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3576 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3577 }
3578 if ($filtre_statut >= 0) {
3579 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3580 }
3581 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3582
3583 $resql = $this->db->query($sql);
3584 if ($resql) {
3585 $num = $this->db->num_rows($resql);
3586 $i = 0;
3587 while ($i < $num) {
3588 $obj = $this->db->fetch_object($resql);
3589 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3590 $i++;
3591 }
3592 $this->db->free($resql);
3593
3594 return $num;
3595 } else {
3596 $this->error = $this->db->lasterror();
3597 return -1;
3598 }
3599 }
3600
3608 public function getKanbanView($option = '', $arraydata = null)
3609 {
3610 global $langs;
3611
3612 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3613
3614 $return = '<div class="box-flex-item box-flex-grow-zero">';
3615 $return .= '<div class="info-box info-box-sm">';
3616 $return .= '<span class="info-box-icon bg-infobox-action">';
3617 $return .= img_picto('', $this->picto);
3618 $return .= '</span>';
3619 $return .= '<div class="info-box-content">';
3620 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3621 if ($selected >= 0) {
3622 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3623 }
3624 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3625 $return .='<br><span class="info-box-label amount">'.$this->socid.'</span>';
3626 }
3627 if (property_exists($this, 'billed')) {
3628 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3629 }
3630 if (method_exists($this, 'getLibStatut')) {
3631 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3632 }
3633 $return .= '</div>';
3634 $return .= '</div>';
3635 $return .= '</div>';
3636 return $return;
3637 }
3638}
3639
3640
3641
3646{
3650 public $element = 'commande_fournisseurdet';
3651
3655 public $table_element = 'commande_fournisseurdet';
3656
3657 public $oldline;
3658
3663 public $fk_commande;
3664
3665 // From llx_commande_fournisseurdet
3669 public $fk_parent_line;
3670
3674 public $fk_facture;
3675
3676 public $rang = 0;
3677 public $special_code = 0;
3678
3683 public $pu_ht;
3684
3685 public $date_start;
3686 public $date_end;
3687 public $fk_fournprice;
3688 public $packaging;
3689 public $pa_ht;
3690
3691 // From llx_product_fournisseur_price
3692
3697 public $ref_supplier;
3698
3704 public $ref_fourn;
3705
3706 public $remise;
3707
3708
3714 public function __construct($db)
3715 {
3716 $this->db = $db;
3717 }
3718
3725 public function fetch($rowid)
3726 {
3727 global $conf;
3728
3729 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3730 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3731 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3732 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3733 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3734 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3735 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3736 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3737 $sql .= ' c.fk_soc as socid';
3738 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
3739 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';
3740 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3741
3742 $result = $this->db->query($sql);
3743 if ($result) {
3744 $objp = $this->db->fetch_object($result);
3745
3746 if (!empty($objp)) {
3747 $this->rowid = $objp->rowid;
3748 $this->id = $objp->rowid;
3749 $this->fk_commande = $objp->fk_commande;
3750 $this->desc = $objp->description;
3751 $this->qty = $objp->qty;
3752 $this->ref_fourn = $objp->ref_supplier;
3753 $this->ref_supplier = $objp->ref_supplier;
3754 $this->subprice = $objp->subprice;
3755 $this->tva_tx = $objp->tva_tx;
3756 $this->localtax1_tx = $objp->localtax1_tx;
3757 $this->localtax2_tx = $objp->localtax2_tx;
3758 $this->localtax1_type = $objp->localtax1_type;
3759 $this->localtax2_type = $objp->localtax2_type;
3760 $this->remise = $objp->remise;
3761 $this->remise_percent = $objp->remise_percent;
3762 $this->fk_product = $objp->fk_product;
3763 $this->info_bits = $objp->info_bits;
3764 $this->total_ht = $objp->total_ht;
3765 $this->total_tva = $objp->total_tva;
3766 $this->total_localtax1 = $objp->total_localtax1;
3767 $this->total_localtax2 = $objp->total_localtax2;
3768 $this->total_ttc = $objp->total_ttc;
3769 $this->product_type = $objp->product_type;
3770 $this->special_code = $objp->special_code;
3771
3772 $this->ref = $objp->product_ref;
3773
3774 $this->product_ref = $objp->product_ref;
3775 $this->product_label = $objp->product_label;
3776 $this->product_desc = $objp->product_desc;
3777
3778 if (getDolGlobalInt('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3779 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3780 // Move this into another method and call it when required.
3781
3782 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3783 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
3784 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3785 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3786 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3787 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3788 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3789 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3790 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3791 $sqlsearchpackage .= " LIMIT 1";
3792
3793 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3794 if ($resqlsearchpackage) {
3795 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3796 if ($objsearchpackage) {
3797 $this->fk_fournprice = $objsearchpackage->rowid;
3798 $this->packaging = $objsearchpackage->packaging;
3799 }
3800 } else {
3801 $this->error = $this->db->lasterror();
3802 return -1;
3803 }
3804 }
3805
3806 $this->date_start = $this->db->jdate($objp->date_start);
3807 $this->date_end = $this->db->jdate($objp->date_end);
3808 $this->fk_unit = $objp->fk_unit;
3809
3810 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3811 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3812 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3813 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3814
3815 $this->fetch_optionals();
3816
3817 $this->db->free($result);
3818 return 1;
3819 } else {
3820 $this->error = 'Supplier order line with id='.$rowid.' not found';
3821 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3822 return 0;
3823 }
3824 } else {
3825 dol_print_error($this->db);
3826 return -1;
3827 }
3828 }
3829
3836 public function insert($notrigger = 0)
3837 {
3838 global $conf, $user;
3839
3840 $error = 0;
3841
3842 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3843
3844 // Clean parameters
3845 if (empty($this->tva_tx)) {
3846 $this->tva_tx = 0;
3847 }
3848 if (empty($this->localtax1_tx)) {
3849 $this->localtax1_tx = 0;
3850 }
3851 if (empty($this->localtax2_tx)) {
3852 $this->localtax2_tx = 0;
3853 }
3854 if (empty($this->localtax1_type)) {
3855 $this->localtax1_type = '0';
3856 }
3857 if (empty($this->localtax2_type)) {
3858 $this->localtax2_type = '0';
3859 }
3860 if (empty($this->total_localtax1)) {
3861 $this->total_localtax1 = 0;
3862 }
3863 if (empty($this->total_localtax2)) {
3864 $this->total_localtax2 = 0;
3865 }
3866 if (empty($this->rang)) {
3867 $this->rang = 0;
3868 }
3869 if (empty($this->remise_percent)) {
3870 $this->remise_percent = 0;
3871 }
3872 if (empty($this->info_bits)) {
3873 $this->info_bits = 0;
3874 }
3875 if (empty($this->special_code)) {
3876 $this->special_code = 0;
3877 }
3878 if (empty($this->fk_parent_line)) {
3879 $this->fk_parent_line = 0;
3880 }
3881 if (empty($this->pa_ht)) {
3882 $this->pa_ht = 0;
3883 }
3884
3885 // Multicurrency
3886 if (!empty($this->multicurrency_code)) {
3887 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3888 }
3889 if (empty($this->fk_multicurrency)) {
3890 $this->multicurrency_code = $conf->currency;
3891 $this->fk_multicurrency = 0;
3892 $this->multicurrency_tx = 1;
3893 }
3894
3895 // Check parameters
3896 if ($this->product_type < 0) {
3897 return -1;
3898 }
3899
3900 $this->db->begin();
3901
3902 // Insertion dans base de la ligne
3903 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element;
3904 $sql .= " (fk_commande, label, description, date_start, date_end,";
3905 $sql .= " fk_product, product_type, special_code, rang,";
3906 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3907 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3908 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,";
3909 $sql .= " fk_parent_line)";
3910 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3911 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3912 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3913 if ($this->fk_product) {
3914 $sql .= $this->fk_product.",";
3915 } else {
3916 $sql .= "null,";
3917 }
3918 $sql .= "'".$this->db->escape($this->product_type)."',";
3919 $sql .= "'".$this->db->escape($this->special_code)."',";
3920 $sql .= "'".$this->db->escape($this->rang)."',";
3921 $sql .= "'".$this->db->escape($this->qty)."', ";
3922 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3923 $sql .= " ".price2num($this->tva_tx).", ";
3924 $sql .= " ".price2num($this->localtax1_tx).",";
3925 $sql .= " ".price2num($this->localtax2_tx).",";
3926 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3927 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3928 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3929 $sql .= " ".price2num($this->total_ht).",";
3930 $sql .= " ".price2num($this->total_tva).",";
3931 $sql .= " ".price2num($this->total_localtax1).",";
3932 $sql .= " ".price2num($this->total_localtax2).",";
3933 $sql .= " ".price2num($this->total_ttc).",";
3934 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3935 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3936 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3937 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3938 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3939 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3940 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3941 $sql .= ", ".((!empty($this->fk_parent_line) && $this->fk_parent_line > 0) ? $this->fk_parent_line : 'null');
3942 $sql .= ")";
3943
3944 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3945 $resql = $this->db->query($sql);
3946 if ($resql) {
3947 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
3948 $this->rowid = $this->id;
3949
3950 if (!$error) {
3951 $result = $this->insertExtraFields();
3952 if ($result < 0) {
3953 $error++;
3954 }
3955 }
3956
3957 if (!$error && !$notrigger) {
3958 // Call trigger
3959 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3960 if ($result < 0) {
3961 $error++;
3962 }
3963 // End call triggers
3964 }
3965
3966 if (!$error) {
3967 $this->db->commit();
3968 return 1;
3969 }
3970
3971 foreach ($this->errors as $errmsg) {
3972 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3973 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
3974 }
3975 $this->db->rollback();
3976 return -1 * $error;
3977 } else {
3978 $this->errors[] = $this->db->error();
3979 $this->db->rollback();
3980 return -2;
3981 }
3982 }
3989 public function update($notrigger = 0)
3990 {
3991 global $user;
3992
3993 $error = 0;
3994
3995 $this->db->begin();
3996
3997 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
3998 $sql .= " description='".$this->db->escape($this->desc)."'";
3999 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4000 $sql .= ", subprice='".price2num($this->subprice)."'";
4001 //$sql.= ",remise='".price2num($remise)."'";
4002 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4003
4004 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4005 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4006 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4007 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4008 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4009 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4010 $sql .= ", qty='".price2num($this->qty)."'";
4011 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4012 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4013 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4014 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4015 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4016 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4017 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4018 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4019 $sql .= ", product_type=".$this->product_type;
4020 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4021 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4022
4023 // Multicurrency
4024 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4025 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4026 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4027 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4028
4029 $sql .= " WHERE rowid = ".((int) $this->id);
4030
4031 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4032 $resql = $this->db->query($sql);
4033 if ($resql) {
4034 if (!$error) {
4035 $result = $this->insertExtraFields();
4036 if ($result < 0) {
4037 $error++;
4038 }
4039 }
4040
4041 if (!$error && !$notrigger) {
4042 // Call trigger
4043 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4044 if ($result < 0) {
4045 $this->db->rollback();
4046 return -1;
4047 }
4048 // End call triggers
4049 }
4050
4051 if (!$error) {
4052 $this->db->commit();
4053 return 1;
4054 } else {
4055 $this->db->rollback();
4056 return -1;
4057 }
4058 } else {
4059 $this->error = $this->db->lasterror();
4060 $this->db->rollback();
4061 return -1;
4062 }
4063 }
4064
4071 public function delete($notrigger = 0)
4072 {
4073 global $user;
4074
4075 $error = 0;
4076
4077 $this->db->begin();
4078
4079 // extrafields
4080 $result = $this->deleteExtraFields();
4081 if ($result < 0) {
4082 $this->db->rollback();
4083 return -1;
4084 }
4085
4086 $sql1 = 'UPDATE '.$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4087 $resql = $this->db->query($sql1);
4088 if (!$resql) {
4089 $this->db->rollback();
4090 return -1;
4091 }
4092
4093 $sql2 = 'DELETE FROM '.$this->db->prefix()."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4094
4095 dol_syslog(__METHOD__, LOG_DEBUG);
4096 $resql = $this->db->query($sql2);
4097 if ($resql) {
4098 if (!$notrigger) {
4099 // Call trigger
4100 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4101 if ($result < 0) {
4102 $error++;
4103 }
4104 // End call triggers
4105 }
4106
4107 if (!$error) {
4108 $this->db->commit();
4109 return 1;
4110 }
4111
4112 foreach ($this->errors as $errmsg) {
4113 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4114 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4115 }
4116 $this->db->rollback();
4117 return -1 * $error;
4118 } else {
4119 $this->error = $this->db->lasterror();
4120 return -1;
4121 }
4122 }
4123}
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