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
2296 if (!$error) {
2297 $sql1 = "UPDATE ".$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet IN (SELECT rowid FROM ".$main." WHERE fk_commande = ".((int) $this->id).")";
2298 dol_syslog(__METHOD__." linked order lines", LOG_DEBUG);
2299 if (!$this->db->query($sql1)) {
2300 $error++;
2301 $this->error = $this->db->lasterror();
2302 $this->errors[] = $this->db->lasterror();
2303 }
2304 }
2305
2306 if (!$error) {
2307 $ef = $main."_extrafields";
2308 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2309 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2310 if (!$this->db->query($sql)) {
2311 $this->error = $this->db->lasterror();
2312 $this->errors[] = $this->db->lasterror();
2313 $error++;
2314 }
2315 }
2316
2317 if (!$error) {
2318 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2319 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2320 if (!$this->db->query($sql)) {
2321 $this->error = $this->db->lasterror();
2322 $this->errors[] = $this->db->lasterror();
2323 $error++;
2324 }
2325 }
2326
2327 if (!$error) {
2328 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2329 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2330 if ($resql = $this->db->query($sql)) {
2331 if ($this->db->affected_rows($resql) < 1) {
2332 $this->error = $this->db->lasterror();
2333 $this->errors[] = $this->db->lasterror();
2334 $error++;
2335 }
2336 } else {
2337 $this->error = $this->db->lasterror();
2338 $this->errors[] = $this->db->lasterror();
2339 $error++;
2340 }
2341 }
2342
2343 // Remove extrafields
2344 if (!$error) {
2345 $result = $this->deleteExtraFields();
2346 if ($result < 0) {
2347 $this->error = 'FailToDeleteExtraFields';
2348 $this->errors[] = 'FailToDeleteExtraFields';
2349 $error++;
2350 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2351 }
2352 }
2353
2354 // Delete linked object
2355 $res = $this->deleteObjectLinked();
2356 if ($res < 0) {
2357 $this->error = 'FailToDeleteObjectLinked';
2358 $this->errors[] = 'FailToDeleteObjectLinked';
2359 $error++;
2360 }
2361
2362 if (!$error) {
2363 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2364 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2365 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2366
2367 // We remove directory
2368 $ref = dol_sanitizeFileName($this->ref);
2369 if ($conf->fournisseur->commande->dir_output) {
2370 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2371 $file = $dir."/".$ref.".pdf";
2372 if (file_exists($file)) {
2373 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2374 $this->error = 'ErrorFailToDeleteFile';
2375 $this->errors[] = 'ErrorFailToDeleteFile';
2376 $error++;
2377 }
2378 }
2379 if (file_exists($dir)) {
2380 $res = @dol_delete_dir_recursive($dir);
2381 if (!$res) {
2382 $this->error = 'ErrorFailToDeleteDir';
2383 $this->errors[] = 'ErrorFailToDeleteDir';
2384 $error++;
2385 }
2386 }
2387 }
2388 }
2389
2390 if (!$error) {
2391 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2392 $this->db->commit();
2393 return 1;
2394 } else {
2395 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2396 $this->db->rollback();
2397 return -$error;
2398 }
2399 }
2400
2401
2410 public function getDispachedLines($status = -1)
2411 {
2412 $ret = array();
2413
2414 // List of already dispatched lines
2415 $sql = "SELECT p.ref, p.label,";
2416 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2417 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_commandefourndet";
2418 $sql .= " FROM ".$this->db->prefix()."product as p,";
2419 $sql .= " ".$this->db->prefix()."commande_fournisseur_dispatch as cfd";
2420 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2421 $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2422 $sql .= " AND cfd.fk_product = p.rowid";
2423 if ($status >= 0) {
2424 $sql .= " AND cfd.status = ".((int) $status);
2425 }
2426 $sql .= " ORDER BY cfd.rowid ASC";
2427
2428 $resql = $this->db->query($sql);
2429 if ($resql) {
2430 $num = $this->db->num_rows($resql);
2431 $i = 0;
2432
2433 while ($i < $num) {
2434 $objp = $this->db->fetch_object($resql);
2435 if ($objp) {
2436 $ret[] = array(
2437 'id' => $objp->dispatchedlineid,
2438 'productid' => $objp->fk_product,
2439 'warehouseid' => $objp->warehouse_id,
2440 'qty' => $objp->qty,
2441 'orderlineid' => $objp->fk_commandefourndet
2442 );
2443 }
2444
2445 $i++;
2446 }
2447 } else {
2448 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2449 }
2450
2451 return $ret;
2452 }
2453
2454 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2464 public function Livraison($user, $date, $type, $comment)
2465 {
2466 // phpcs:enable
2467 global $conf, $langs;
2468
2469 $result = 0;
2470 $error = 0;
2471
2472 dol_syslog(get_class($this)."::Livraison");
2473
2474 $usercanreceive = 0;
2475 if (!isModEnabled('reception')) {
2476 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2477 } else {
2478 $usercanreceive = $user->hasRight("reception", "creer");
2479 }
2480
2481 if ($usercanreceive) {
2482 // Define the new status
2483 if ($type == 'par') {
2485 } elseif ($type == 'tot') {
2487 } elseif ($type == 'nev') {
2489 } elseif ($type == 'can') {
2491 } else {
2492 $error++;
2493 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2494 return -2;
2495 }
2496
2497 // Some checks to accept the record
2498 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2499 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2500 if (!$error && ($type == 'tot')) {
2501 $dispatchedlinearray = $this->getDispachedLines(0);
2502 if (count($dispatchedlinearray) > 0) {
2503 $result = -1;
2504 $error++;
2505 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2506 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2507 }
2508 }
2509 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)
2510 $dispatcheddenied = $this->getDispachedLines(2);
2511 if (count($dispatchedlinearray) > 0) {
2512 $result = -1;
2513 $error++;
2514 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2515 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2516 }
2517 }
2518 }
2519
2520 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2521
2522 if (empty($error)) {
2523 $this->db->begin();
2524
2525 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2526 $sql .= " SET fk_statut = ".((int) $statut);
2527 $sql .= " WHERE rowid = ".((int) $this->id);
2528 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2529
2530 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2531 $resql = $this->db->query($sql);
2532 if ($resql) {
2533 $result = 1;
2534 $old_statut = $this->statut;
2535 $this->statut = $statut;
2536 $this->context['actionmsg2'] = $comment;
2537
2538 // Call trigger
2539 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2540 if ($result_trigger < 0) {
2541 $error++;
2542 }
2543 // End call triggers
2544
2545 if (empty($error)) {
2546 $this->db->commit();
2547 } else {
2548 $this->statut = $old_statut;
2549 $this->db->rollback();
2550 $this->error = $this->db->lasterror();
2551 $result = -1;
2552 }
2553 } else {
2554 $this->db->rollback();
2555 $this->error = $this->db->lasterror();
2556 $result = -1;
2557 }
2558 }
2559 } else {
2560 $this->error = $langs->trans('NotAuthorized');
2561 $this->errors[] = $langs->trans('NotAuthorized');
2562 dol_syslog(get_class($this)."::Livraison Not Authorized");
2563 $result = -3;
2564 }
2565 return $result;
2566 }
2567
2568 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2578 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2579 {
2580 // phpcs:enable
2581 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2582 }
2583
2592 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2593 {
2594 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2595 $error = 0;
2596
2597 $this->db->begin();
2598
2599 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2600 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2601 $sql .= " WHERE rowid = ".((int) $this->id);
2602
2603 dol_syslog(__METHOD__, LOG_DEBUG);
2604 $resql = $this->db->query($sql);
2605 if (!$resql) {
2606 $this->errors[] = $this->db->error();
2607 $error++;
2608 }
2609
2610 if (!$error) {
2611 $this->oldcopy = clone $this;
2612 $this->delivery_date = $delivery_date;
2613 }
2614
2615 if (!$notrigger && empty($error)) {
2616 // Call trigger
2617 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2618 if ($result < 0) {
2619 $error++;
2620 }
2621 // End call triggers
2622 }
2623
2624 if (!$error) {
2625 $this->db->commit();
2626 return 1;
2627 } else {
2628 foreach ($this->errors as $errmsg) {
2629 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2630 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2631 }
2632 $this->db->rollback();
2633 return -1 * $error;
2634 }
2635 } else {
2636 return -2;
2637 }
2638 }
2639
2640 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2649 public function set_id_projet($user, $id_projet, $notrigger = 0)
2650 {
2651 // phpcs:enable
2652 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2653 $error = 0;
2654
2655 $this->db->begin();
2656
2657 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2658 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2659 $sql .= " WHERE rowid = ".((int) $this->id);
2660
2661 dol_syslog(__METHOD__, LOG_DEBUG);
2662 $resql = $this->db->query($sql);
2663 if (!$resql) {
2664 $this->errors[] = $this->db->error();
2665 $error++;
2666 }
2667
2668 if (!$error) {
2669 $this->oldcopy = clone $this;
2670 $this->fk_projet = $id_projet;
2671 $this->fk_project = $id_projet;
2672 }
2673
2674 if (!$notrigger && empty($error)) {
2675 // Call trigger
2676 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2677 if ($result < 0) {
2678 $error++;
2679 }
2680 // End call triggers
2681 }
2682
2683 if (!$error) {
2684 $this->db->commit();
2685 return 1;
2686 } else {
2687 foreach ($this->errors as $errmsg) {
2688 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2689 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2690 }
2691 $this->db->rollback();
2692 return -1 * $error;
2693 }
2694 } else {
2695 return -2;
2696 }
2697 }
2698
2707 public function updateFromCommandeClient($user, $idc, $comclientid)
2708 {
2709 $comclient = new Commande($this->db);
2710 $comclient->fetch($comclientid);
2711
2712 $this->id = $idc;
2713
2714 $this->lines = array();
2715
2716 $num = count($comclient->lines);
2717 for ($i = 0; $i < $num; $i++) {
2718 $prod = new Product($this->db);
2719 $label = '';
2720 $ref = '';
2721 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2722 $label = $prod->label;
2723 $ref = $prod->ref;
2724 }
2725
2726 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2727 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2728 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2729 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2730 $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);
2731 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2732 if ($this->db->query($sql)) {
2733 $this->update_price(1);
2734 }
2735 }
2736
2737 return 1;
2738 }
2739
2747 public function setStatus($user, $status)
2748 {
2749 global $conf, $langs;
2750 $error = 0;
2751
2752 $this->db->begin();
2753
2754 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2755 $sql .= " SET fk_statut = ".$status;
2756 $sql .= " WHERE rowid = ".((int) $this->id);
2757
2758 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2759 $resql = $this->db->query($sql);
2760 if ($resql) {
2761 // Trigger names for each status
2762 $triggerName = array();
2763 $triggerName[0] = 'DRAFT';
2764 $triggerName[1] = 'VALIDATED';
2765 $triggerName[2] = 'APPROVED';
2766 $triggerName[3] = 'ORDERED'; // Ordered
2767 $triggerName[4] = 'RECEIVED_PARTIALLY';
2768 $triggerName[5] = 'RECEIVED_COMPLETELY';
2769 $triggerName[6] = 'CANCELED';
2770 $triggerName[7] = 'CANCELED';
2771 $triggerName[9] = 'REFUSED';
2772
2773 // Call trigger
2774 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2775 if ($result < 0) {
2776 $error++;
2777 }
2778 // End call triggers
2779 } else {
2780 $error++;
2781 $this->error = $this->db->lasterror();
2782 dol_syslog(get_class($this)."::setStatus ".$this->error);
2783 }
2784
2785 if (!$error) {
2786 $this->statut = $status;
2787 $this->db->commit();
2788 return 1;
2789 } else {
2790 $this->db->rollback();
2791 return -1;
2792 }
2793 }
2794
2818 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 = '')
2819 {
2820 global $mysoc, $conf, $langs;
2821 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2822 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2823
2824 $error = 0;
2825
2826 if ($this->statut == self::STATUS_DRAFT) {
2827 // Clean parameters
2828 if (empty($qty)) {
2829 $qty = 0;
2830 }
2831 if (empty($info_bits)) {
2832 $info_bits = 0;
2833 }
2834 if (empty($txtva)) {
2835 $txtva = 0;
2836 }
2837 if (empty($txlocaltax1)) {
2838 $txlocaltax1 = 0;
2839 }
2840 if (empty($txlocaltax2)) {
2841 $txlocaltax2 = 0;
2842 }
2843 if (empty($remise_percent)) {
2844 $remise_percent = 0;
2845 }
2846
2847 $remise_percent = price2num($remise_percent);
2848 $qty = price2num($qty);
2849 if (!$qty) {
2850 $qty = 1;
2851 }
2852 $pu = price2num($pu);
2853 $pu_ht_devise = price2num($pu_ht_devise);
2854 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2855 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2856 }
2857 $txlocaltax1 = price2num($txlocaltax1);
2858 $txlocaltax2 = price2num($txlocaltax2);
2859
2860 // Check parameters
2861 if ($type < 0) {
2862 return -1;
2863 }
2864 if ($date_start && $date_end && $date_start > $date_end) {
2865 $langs->load("errors");
2866 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2867 return -1;
2868 }
2869
2870 $this->db->begin();
2871
2872 // Calcul du total TTC et de la TVA pour la ligne a partir de
2873 // qty, pu, remise_percent et txtva
2874 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2875 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2876
2877 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2878
2879 // Clean vat code
2880 $reg = array();
2881 $vat_src_code = '';
2882 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2883 $vat_src_code = $reg[1];
2884 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2885 }
2886
2887 $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);
2888 $total_ht = $tabprice[0];
2889 $total_tva = $tabprice[1];
2890 $total_ttc = $tabprice[2];
2891 $total_localtax1 = $tabprice[9];
2892 $total_localtax2 = $tabprice[10];
2893 $pu_ht = $tabprice[3];
2894 $pu_tva = $tabprice[4];
2895 $pu_ttc = $tabprice[5];
2896
2897 // MultiCurrency
2898 $multicurrency_total_ht = $tabprice[16];
2899 $multicurrency_total_tva = $tabprice[17];
2900 $multicurrency_total_ttc = $tabprice[18];
2901 $pu_ht_devise = $tabprice[19];
2902
2903 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2904 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2905
2906 //Fetch current line from the database and then clone the object and set it in $oldline property
2907 $this->line = new CommandeFournisseurLigne($this->db);
2908 $this->line->fetch($rowid);
2909
2910 $oldline = clone $this->line;
2911 $this->line->oldline = $oldline;
2912
2913 $this->line->context = $this->context;
2914
2915 $this->line->fk_commande = $this->id;
2916 //$this->line->label=$label;
2917 $this->line->desc = $desc;
2918
2919 // redefine quantity according to packaging
2920 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2921 if ($qty < $this->line->packaging) {
2922 $qty = $this->line->packaging;
2923 } else {
2924 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2925 $coeff = intval($qty / $this->line->packaging) + 1;
2926 $qty = $this->line->packaging * $coeff;
2927 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2928 }
2929 }
2930 }
2931
2932 $this->line->qty = $qty;
2933 $this->line->ref_supplier = $ref_supplier;
2934
2935 $this->line->vat_src_code = $vat_src_code;
2936 $this->line->tva_tx = $txtva;
2937 $this->line->localtax1_tx = $txlocaltax1;
2938 $this->line->localtax2_tx = $txlocaltax2;
2939 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2940 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2941 $this->line->remise_percent = $remise_percent;
2942 $this->line->subprice = $pu_ht;
2943 $this->line->info_bits = $info_bits;
2944 $this->line->total_ht = $total_ht;
2945 $this->line->total_tva = $total_tva;
2946 $this->line->total_localtax1 = $total_localtax1;
2947 $this->line->total_localtax2 = $total_localtax2;
2948 $this->line->total_ttc = $total_ttc;
2949 $this->line->product_type = $type;
2950 $this->line->special_code = $oldline->special_code;
2951 $this->line->rang = $oldline->rang;
2952 $this->line->origin = $this->origin;
2953 $this->line->fk_unit = $fk_unit;
2954
2955 $this->line->date_start = $date_start;
2956 $this->line->date_end = $date_end;
2957
2958 // Multicurrency
2959 $this->line->fk_multicurrency = $this->fk_multicurrency;
2960 $this->line->multicurrency_code = $this->multicurrency_code;
2961 $this->line->multicurrency_subprice = $pu_ht_devise;
2962 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2963 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2964 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2965
2966 $this->line->subprice = $pu_ht;
2967 $this->line->price = $this->line->subprice;
2968
2969 $this->line->remise_percent = $remise_percent;
2970
2971 if (is_array($array_options) && count($array_options) > 0) {
2972 // We replace values in this->line->array_options only for entries defined into $array_options
2973 foreach ($array_options as $key => $value) {
2974 $this->line->array_options[$key] = $array_options[$key];
2975 }
2976 }
2977
2978 $result = $this->line->update($notrigger);
2979
2980
2981 // Mise a jour info denormalisees au niveau facture
2982 if ($result >= 0) {
2983 $this->update_price('1', 'auto');
2984 $this->db->commit();
2985 return $result;
2986 } else {
2987 $this->error = $this->db->lasterror();
2988 $this->db->rollback();
2989 return -1;
2990 }
2991 } else {
2992 $this->error = "Order status makes operation forbidden";
2993 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
2994 return -2;
2995 }
2996 }
2997
2998
3006 public function initAsSpecimen()
3007 {
3008 global $user, $langs, $conf;
3009
3010 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3011
3012 dol_syslog(get_class($this)."::initAsSpecimen");
3013
3014 $now = dol_now();
3015
3016 // Find first product
3017 $prodid = 0;
3018 $product = new ProductFournisseur($this->db);
3019 $sql = "SELECT rowid";
3020 $sql .= " FROM ".$this->db->prefix()."product";
3021 $sql .= " WHERE entity IN (".getEntity('product').")";
3022 $sql .= $this->db->order("rowid", "ASC");
3023 $sql .= $this->db->plimit(1);
3024 $resql = $this->db->query($sql);
3025 if ($resql && $this->db->num_rows($resql)) {
3026 $obj = $this->db->fetch_object($resql);
3027 $prodid = $obj->rowid;
3028 }
3029
3030 // Initialise parametres
3031 $this->id = 0;
3032 $this->ref = 'SPECIMEN';
3033 $this->specimen = 1;
3034 $this->socid = 1;
3035 $this->date = $now;
3036 $this->date_commande = $now;
3037 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3038 $this->cond_reglement_code = 'RECEP';
3039 $this->mode_reglement_code = 'CHQ';
3040
3041 $this->note_public = 'This is a comment (public)';
3042 $this->note_private = 'This is a comment (private)';
3043
3044 $this->multicurrency_tx = 1;
3045 $this->multicurrency_code = $conf->currency;
3046
3047 $this->statut = 0;
3048
3049 // Lines
3050 $nbp = 5;
3051 $xnbp = 0;
3052 while ($xnbp < $nbp) {
3053 $line = new CommandeFournisseurLigne($this->db);
3054 $line->desc = $langs->trans("Description")." ".$xnbp;
3055 $line->qty = 1;
3056 $line->subprice = 100;
3057 $line->tva_tx = 19.6;
3058 $line->localtax1_tx = 0;
3059 $line->localtax2_tx = 0;
3060 if ($xnbp == 2) {
3061 $line->total_ht = 50;
3062 $line->total_ttc = 59.8;
3063 $line->total_tva = 9.8;
3064 $line->remise_percent = 50;
3065 } else {
3066 $line->total_ht = 100;
3067 $line->total_ttc = 119.6;
3068 $line->total_tva = 19.6;
3069 $line->remise_percent = 00;
3070 }
3071 $line->fk_product = $prodid;
3072
3073 $this->lines[$xnbp] = $line;
3074
3075 $this->total_ht += $line->total_ht;
3076 $this->total_tva += $line->total_tva;
3077 $this->total_ttc += $line->total_ttc;
3078
3079 $xnbp++;
3080 }
3081 }
3082
3089 public function info($id)
3090 {
3091 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3092 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3093 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3094 $sql .= ' WHERE c.rowid = '.((int) $id);
3095
3096 $result = $this->db->query($sql);
3097 if ($result) {
3098 if ($this->db->num_rows($result)) {
3099 $obj = $this->db->fetch_object($result);
3100
3101 $this->id = $obj->rowid;
3102
3103 $this->user_creation_id = $obj->fk_user_author;
3104 $this->user_validation_id = $obj->fk_user_valid;
3105 $this->user_modification_id = $obj->fk_user_modif;
3106 $this->user_approve_id = $obj->fk_user_approve;
3107 $this->user_approve_id2 = $obj->fk_user_approve2;
3108
3109 $this->date_creation = $this->db->jdate($obj->datec);
3110 $this->date_modification = $this->db->jdate($obj->datem);
3111 $this->date_approve = $this->db->jdate($obj->datea);
3112 $this->date_approve2 = $this->db->jdate($obj->datea2);
3113 $this->date_validation = $this->db->jdate($obj->date_validation);
3114 }
3115 $this->db->free($result);
3116 } else {
3117 dol_print_error($this->db);
3118 }
3119 }
3120
3121 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3127 public function load_state_board()
3128 {
3129 // phpcs:enable
3130 global $conf, $user;
3131
3132 $this->nb = array();
3133 $clause = "WHERE";
3134
3135 $sql = "SELECT count(co.rowid) as nb";
3136 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3137 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3138 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3139 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3140 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3141 $clause = "AND";
3142 }
3143 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3144
3145 $resql = $this->db->query($sql);
3146 if ($resql) {
3147 while ($obj = $this->db->fetch_object($resql)) {
3148 $this->nb["supplier_orders"] = $obj->nb;
3149 }
3150 $this->db->free($resql);
3151 return 1;
3152 } else {
3153 dol_print_error($this->db);
3154 $this->error = $this->db->error();
3155 return -1;
3156 }
3157 }
3158
3159 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3167 public function load_board($user, $mode = 'opened')
3168 {
3169 // phpcs:enable
3170 global $conf, $langs;
3171
3172 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3173 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3174 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3175 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3176 }
3177 $sql .= " WHERE c.entity = ".$conf->entity;
3178 if ($mode === 'awaiting') {
3179 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3180 } else {
3181 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3182 }
3183 if ($user->socid) {
3184 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3185 }
3186
3187 $resql = $this->db->query($sql);
3188 if ($resql) {
3189 $commandestatic = new CommandeFournisseur($this->db);
3190
3191 $response = new WorkboardResponse();
3192 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3193 $response->label = $langs->trans("SuppliersOrdersToProcess");
3194 $response->labelShort = $langs->trans("Opened");
3195 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3196 $response->img = img_object('', "order");
3197
3198 if ($mode === 'awaiting') {
3199 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3200 $response->labelShort = $langs->trans("AwaitingReception");
3201 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3202 }
3203
3204 while ($obj = $this->db->fetch_object($resql)) {
3205 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3206 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3207 $commandestatic->statut = $obj->fk_statut;
3208
3209 $response->nbtodo++;
3210 $response->total += $obj->total_ht;
3211
3212 if ($commandestatic->hasDelay()) {
3213 $response->nbtodolate++;
3214 }
3215 }
3216
3217 return $response;
3218 } else {
3219 $this->error = $this->db->error();
3220 return -1;
3221 }
3222 }
3223
3230 public function getInputMethod()
3231 {
3232 global $db, $langs;
3233
3234 if ($this->methode_commande_id > 0) {
3235 $sql = "SELECT rowid, code, libelle as label";
3236 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3237 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3238
3239 $resql = $this->db->query($sql);
3240 if ($resql) {
3241 if ($this->db->num_rows($resql)) {
3242 $obj = $this->db->fetch_object($resql);
3243
3244 $string = $langs->trans($obj->code);
3245 if ($string == $obj->code) {
3246 $string = $obj->label != '-' ? $obj->label : '';
3247 }
3248 return $string;
3249 }
3250 } else {
3251 dol_print_error($this->db);
3252 }
3253 }
3254
3255 return '';
3256 }
3257
3269 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3270 {
3271 global $conf, $langs;
3272
3273 if (!dol_strlen($modele)) {
3274 $modele = ''; // No doc template/generation by default
3275
3276 if (!empty($this->model_pdf)) {
3277 $modele = $this->model_pdf;
3278 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3279 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3280 }
3281 }
3282
3283 if (empty($modele)) {
3284 return 0;
3285 } else {
3286 $langs->load("suppliers");
3287 $outputlangs->load("products");
3288
3289 $modelpath = "core/modules/supplier_order/doc/";
3290 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3291 return $result;
3292 }
3293 }
3294
3301 public function getMaxDeliveryTimeDay($langs)
3302 {
3303 if (empty($this->lines)) {
3304 return '';
3305 }
3306
3307 $obj = new ProductFournisseur($this->db);
3308
3309 $nb = 0;
3310 foreach ($this->lines as $line) {
3311 if ($line->fk_product > 0) {
3312 $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3313 if ($idp) {
3314 $obj->fetch($idp);
3315 if ($obj->delivery_time_days > $nb) {
3316 $nb = $obj->delivery_time_days;
3317 }
3318 }
3319 }
3320 }
3321
3322 if ($nb === 0) {
3323 return '';
3324 } else {
3325 return $nb.' '.$langs->trans('Days');
3326 }
3327 }
3328
3334 public function getRights()
3335 {
3336 global $user;
3337
3338 return $user->hasRight("fournisseur", "commande");
3339 }
3340
3341
3350 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3351 {
3352 $tables = array(
3353 'commande_fournisseur'
3354 );
3355
3356 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3357 }
3358
3367 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3368 {
3369 $tables = array(
3370 'commande_fournisseurdet'
3371 );
3372
3373 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3374 }
3375
3383 public function hasDelay()
3384 {
3385 global $conf;
3386
3387 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3388 $now = dol_now();
3389 if (!empty($this->delivery_date)) {
3390 $date_to_test = $this->delivery_date;
3391 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3392 } else {
3393 //$date_to_test = $this->date_commande;
3394 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3395 return false;
3396 }
3397 } else {
3398 $now = dol_now();
3399 $date_to_test = $this->date_commande;
3400
3401 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3402 }
3403 }
3404
3412 public function showDelay()
3413 {
3414 global $conf, $langs;
3415
3416 $text = '';
3417
3418 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3419 if (!empty($this->delivery_date)) {
3420 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3421 } else {
3422 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3423 }
3424 } else {
3425 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3426 }
3427 if ($text) {
3428 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3429 }
3430
3431 return $text;
3432 }
3433
3434
3443 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3444 {
3445 global $conf, $langs;
3446
3447 if (isModEnabled("supplier_order")) {
3448 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3449
3450 $qtydelivered = array();
3451 $qtywished = array();
3452
3453 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3454 $filter = array('t.fk_commande'=>$this->id);
3455 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3456 $filter['t.status'] = 1; // Restrict to lines with status validated
3457 }
3458
3459 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3460 if ($ret < 0) {
3461 $this->error = $supplierorderdispatch->error;
3462 $this->errors = $supplierorderdispatch->errors;
3463 return $ret;
3464 } else {
3465 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3466 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3467 $date_liv = dol_now();
3468
3469 // Build array with quantity deliverd by product
3470 foreach ($supplierorderdispatch->lines as $line) {
3471 $qtydelivered[$line->fk_product] += $line->qty;
3472 }
3473 foreach ($this->lines as $line) {
3474 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3475 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3476 continue;
3477 }
3478 $qtywished[$line->fk_product] += $line->qty;
3479 }
3480
3481 //Compare array
3482 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3483 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3484 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3485 //var_dump(array_keys($qtydelivered));
3486 //var_dump(array_keys($qtywished));
3487 //var_dump($diff_array);
3488 //var_dump($keysinwishednotindelivered);
3489 //var_dump($keysindeliverednotinwished);
3490 //exit;
3491
3492 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3493 if ($closeopenorder) {
3494 //$ret=$this->setStatus($user,5);
3495 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3496 if ($ret < 0) {
3497 return -1;
3498 }
3499 return 5;
3500 } else {
3501 //Diff => received partially
3502 //$ret=$this->setStatus($user,4);
3503 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3504 if ($ret < 0) {
3505 return -1;
3506 }
3507 return 4;
3508 }
3509 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3510 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3511
3512 $close = 0;
3513
3514 if (count($diff_array) > 0) {
3515 //there are some difference between the two arrays
3516
3517 //scan the array of results
3518 foreach ($diff_array as $key => $value) {
3519 //if the quantity delivered is greater or equal to wish quantity
3520 if ($qtydelivered[$key] >= $qtywished[$key]) {
3521 $close++;
3522 }
3523 }
3524 }
3525
3526
3527 if ($close == count($diff_array)) {
3528 //all the products are received equal or more than the wished quantity
3529 if ($closeopenorder) {
3530 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3531 if ($ret < 0) {
3532 return -1;
3533 }
3534 return 5;
3535 } else {
3536 //Diff => received partially
3537 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3538 if ($ret < 0) {
3539 return -1;
3540 }
3541 return 4;
3542 }
3543 } else {
3544 //all the products are not received
3545 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3546 if ($ret < 0) {
3547 return -1;
3548 }
3549 return 4;
3550 }
3551 } else {
3552 //Diff => received partially
3553 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3554 if ($ret < 0) {
3555 return -1;
3556 }
3557 return 4;
3558 }
3559 }
3560 return 1;
3561 }
3562 }
3563 return 0;
3564 }
3565
3573 public function loadReceptions($filtre_statut = -1)
3574 {
3575 $this->receptions = array();
3576
3577 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3578
3579 $sql = 'SELECT cd.rowid, cd.fk_product,';
3580 $sql .= ' sum(cfd.qty) as qty';
3581 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur_dispatch as cfd,';
3582 if ($filtre_statut >= 0) {
3583 $sql .= ' '.$this->db->prefix().'reception as e,';
3584 }
3585 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3586 $sql .= ' WHERE';
3587 if ($filtre_statut >= 0) {
3588 $sql .= ' cfd.fk_reception = e.rowid AND';
3589 }
3590 $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3591 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3592 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3593 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3594 }
3595 if ($filtre_statut >= 0) {
3596 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3597 }
3598 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3599
3600 $resql = $this->db->query($sql);
3601 if ($resql) {
3602 $num = $this->db->num_rows($resql);
3603 $i = 0;
3604 while ($i < $num) {
3605 $obj = $this->db->fetch_object($resql);
3606 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3607 $i++;
3608 }
3609 $this->db->free($resql);
3610
3611 return $num;
3612 } else {
3613 $this->error = $this->db->lasterror();
3614 return -1;
3615 }
3616 }
3617
3625 public function getKanbanView($option = '', $arraydata = null)
3626 {
3627 global $langs;
3628
3629 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3630
3631 $return = '<div class="box-flex-item box-flex-grow-zero">';
3632 $return .= '<div class="info-box info-box-sm">';
3633 $return .= '<span class="info-box-icon bg-infobox-action">';
3634 $return .= img_picto('', $this->picto);
3635 $return .= '</span>';
3636 $return .= '<div class="info-box-content">';
3637 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3638 if ($selected >= 0) {
3639 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3640 }
3641 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3642 $return .='<br><span class="info-box-label amount">'.$this->socid.'</span>';
3643 }
3644 if (property_exists($this, 'billed')) {
3645 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3646 }
3647 if (method_exists($this, 'getLibStatut')) {
3648 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3649 }
3650 $return .= '</div>';
3651 $return .= '</div>';
3652 $return .= '</div>';
3653 return $return;
3654 }
3655}
3656
3657
3658
3663{
3667 public $element = 'commande_fournisseurdet';
3668
3672 public $table_element = 'commande_fournisseurdet';
3673
3674 public $oldline;
3675
3680 public $fk_commande;
3681
3682 // From llx_commande_fournisseurdet
3686 public $fk_parent_line;
3687
3691 public $fk_facture;
3692
3693 public $rang = 0;
3694 public $special_code = 0;
3695
3700 public $pu_ht;
3701
3702 public $date_start;
3703 public $date_end;
3704 public $fk_fournprice;
3705 public $packaging;
3706 public $pa_ht;
3707
3708 // From llx_product_fournisseur_price
3709
3714 public $ref_supplier;
3715
3721 public $ref_fourn;
3722
3723 public $remise;
3724
3725
3731 public function __construct($db)
3732 {
3733 $this->db = $db;
3734 }
3735
3742 public function fetch($rowid)
3743 {
3744 global $conf;
3745
3746 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3747 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3748 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3749 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3750 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3751 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3752 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3753 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3754 $sql .= ' c.fk_soc as socid';
3755 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
3756 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';
3757 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3758
3759 $result = $this->db->query($sql);
3760 if ($result) {
3761 $objp = $this->db->fetch_object($result);
3762
3763 if (!empty($objp)) {
3764 $this->rowid = $objp->rowid;
3765 $this->id = $objp->rowid;
3766 $this->fk_commande = $objp->fk_commande;
3767 $this->desc = $objp->description;
3768 $this->qty = $objp->qty;
3769 $this->ref_fourn = $objp->ref_supplier;
3770 $this->ref_supplier = $objp->ref_supplier;
3771 $this->subprice = $objp->subprice;
3772 $this->tva_tx = $objp->tva_tx;
3773 $this->localtax1_tx = $objp->localtax1_tx;
3774 $this->localtax2_tx = $objp->localtax2_tx;
3775 $this->localtax1_type = $objp->localtax1_type;
3776 $this->localtax2_type = $objp->localtax2_type;
3777 $this->remise = $objp->remise;
3778 $this->remise_percent = $objp->remise_percent;
3779 $this->fk_product = $objp->fk_product;
3780 $this->info_bits = $objp->info_bits;
3781 $this->total_ht = $objp->total_ht;
3782 $this->total_tva = $objp->total_tva;
3783 $this->total_localtax1 = $objp->total_localtax1;
3784 $this->total_localtax2 = $objp->total_localtax2;
3785 $this->total_ttc = $objp->total_ttc;
3786 $this->product_type = $objp->product_type;
3787 $this->special_code = $objp->special_code;
3788
3789 $this->ref = $objp->product_ref;
3790
3791 $this->product_ref = $objp->product_ref;
3792 $this->product_label = $objp->product_label;
3793 $this->product_desc = $objp->product_desc;
3794
3795 if (getDolGlobalInt('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3796 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3797 // Move this into another method and call it when required.
3798
3799 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3800 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
3801 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3802 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3803 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3804 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3805 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3806 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3807 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3808 $sqlsearchpackage .= " LIMIT 1";
3809
3810 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3811 if ($resqlsearchpackage) {
3812 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3813 if ($objsearchpackage) {
3814 $this->fk_fournprice = $objsearchpackage->rowid;
3815 $this->packaging = $objsearchpackage->packaging;
3816 }
3817 } else {
3818 $this->error = $this->db->lasterror();
3819 return -1;
3820 }
3821 }
3822
3823 $this->date_start = $this->db->jdate($objp->date_start);
3824 $this->date_end = $this->db->jdate($objp->date_end);
3825 $this->fk_unit = $objp->fk_unit;
3826
3827 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3828 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3829 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3830 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3831
3832 $this->fetch_optionals();
3833
3834 $this->db->free($result);
3835 return 1;
3836 } else {
3837 $this->error = 'Supplier order line with id='.$rowid.' not found';
3838 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3839 return 0;
3840 }
3841 } else {
3842 dol_print_error($this->db);
3843 return -1;
3844 }
3845 }
3846
3853 public function insert($notrigger = 0)
3854 {
3855 global $conf, $user;
3856
3857 $error = 0;
3858
3859 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3860
3861 // Clean parameters
3862 if (empty($this->tva_tx)) {
3863 $this->tva_tx = 0;
3864 }
3865 if (empty($this->localtax1_tx)) {
3866 $this->localtax1_tx = 0;
3867 }
3868 if (empty($this->localtax2_tx)) {
3869 $this->localtax2_tx = 0;
3870 }
3871 if (empty($this->localtax1_type)) {
3872 $this->localtax1_type = '0';
3873 }
3874 if (empty($this->localtax2_type)) {
3875 $this->localtax2_type = '0';
3876 }
3877 if (empty($this->total_localtax1)) {
3878 $this->total_localtax1 = 0;
3879 }
3880 if (empty($this->total_localtax2)) {
3881 $this->total_localtax2 = 0;
3882 }
3883 if (empty($this->rang)) {
3884 $this->rang = 0;
3885 }
3886 if (empty($this->remise_percent)) {
3887 $this->remise_percent = 0;
3888 }
3889 if (empty($this->info_bits)) {
3890 $this->info_bits = 0;
3891 }
3892 if (empty($this->special_code)) {
3893 $this->special_code = 0;
3894 }
3895 if (empty($this->fk_parent_line)) {
3896 $this->fk_parent_line = 0;
3897 }
3898 if (empty($this->pa_ht)) {
3899 $this->pa_ht = 0;
3900 }
3901
3902 // Multicurrency
3903 if (!empty($this->multicurrency_code)) {
3904 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3905 }
3906 if (empty($this->fk_multicurrency)) {
3907 $this->multicurrency_code = $conf->currency;
3908 $this->fk_multicurrency = 0;
3909 $this->multicurrency_tx = 1;
3910 }
3911
3912 // Check parameters
3913 if ($this->product_type < 0) {
3914 return -1;
3915 }
3916
3917 $this->db->begin();
3918
3919 // Insertion dans base de la ligne
3920 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element;
3921 $sql .= " (fk_commande, label, description, date_start, date_end,";
3922 $sql .= " fk_product, product_type, special_code, rang,";
3923 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3924 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3925 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,";
3926 $sql .= " fk_parent_line)";
3927 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3928 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3929 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3930 if ($this->fk_product) {
3931 $sql .= $this->fk_product.",";
3932 } else {
3933 $sql .= "null,";
3934 }
3935 $sql .= "'".$this->db->escape($this->product_type)."',";
3936 $sql .= "'".$this->db->escape($this->special_code)."',";
3937 $sql .= "'".$this->db->escape($this->rang)."',";
3938 $sql .= "'".$this->db->escape($this->qty)."', ";
3939 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3940 $sql .= " ".price2num($this->tva_tx).", ";
3941 $sql .= " ".price2num($this->localtax1_tx).",";
3942 $sql .= " ".price2num($this->localtax2_tx).",";
3943 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3944 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3945 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3946 $sql .= " ".price2num($this->total_ht).",";
3947 $sql .= " ".price2num($this->total_tva).",";
3948 $sql .= " ".price2num($this->total_localtax1).",";
3949 $sql .= " ".price2num($this->total_localtax2).",";
3950 $sql .= " ".price2num($this->total_ttc).",";
3951 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3952 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3953 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3954 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3955 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3956 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3957 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3958 $sql .= ", ".((!empty($this->fk_parent_line) && $this->fk_parent_line > 0) ? $this->fk_parent_line : 'null');
3959 $sql .= ")";
3960
3961 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3962 $resql = $this->db->query($sql);
3963 if ($resql) {
3964 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
3965 $this->rowid = $this->id;
3966
3967 if (!$error) {
3968 $result = $this->insertExtraFields();
3969 if ($result < 0) {
3970 $error++;
3971 }
3972 }
3973
3974 if (!$error && !$notrigger) {
3975 // Call trigger
3976 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3977 if ($result < 0) {
3978 $error++;
3979 }
3980 // End call triggers
3981 }
3982
3983 if (!$error) {
3984 $this->db->commit();
3985 return 1;
3986 }
3987
3988 foreach ($this->errors as $errmsg) {
3989 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3990 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
3991 }
3992 $this->db->rollback();
3993 return -1 * $error;
3994 } else {
3995 $this->errors[] = $this->db->error();
3996 $this->db->rollback();
3997 return -2;
3998 }
3999 }
4000
4007 public function update($notrigger = 0)
4008 {
4009 global $user;
4010
4011 $error = 0;
4012
4013 $this->db->begin();
4014
4015 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
4016 $sql .= " description='".$this->db->escape($this->desc)."'";
4017 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4018 $sql .= ", subprice='".price2num($this->subprice)."'";
4019 //$sql.= ",remise='".price2num($remise)."'";
4020 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4021
4022 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4023 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4024 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4025 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4026 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4027 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4028 $sql .= ", qty='".price2num($this->qty)."'";
4029 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4030 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4031 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4032 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4033 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4034 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4035 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4036 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4037 $sql .= ", product_type=".$this->product_type;
4038 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4039 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4040
4041 // Multicurrency
4042 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4043 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4044 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4045 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4046
4047 $sql .= " WHERE rowid = ".((int) $this->id);
4048
4049 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4050 $resql = $this->db->query($sql);
4051 if ($resql) {
4052 if (!$error) {
4053 $result = $this->insertExtraFields();
4054 if ($result < 0) {
4055 $error++;
4056 }
4057 }
4058
4059 if (!$error && !$notrigger) {
4060 // Call trigger
4061 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4062 if ($result < 0) {
4063 $this->db->rollback();
4064 return -1;
4065 }
4066 // End call triggers
4067 }
4068
4069 if (!$error) {
4070 $this->db->commit();
4071 return 1;
4072 } else {
4073 $this->db->rollback();
4074 return -1;
4075 }
4076 } else {
4077 $this->error = $this->db->lasterror();
4078 $this->db->rollback();
4079 return -1;
4080 }
4081 }
4082
4089 public function delete($notrigger = 0)
4090 {
4091 global $user;
4092
4093 $error = 0;
4094
4095 $this->db->begin();
4096
4097 // extrafields
4098 $result = $this->deleteExtraFields();
4099 if ($result < 0) {
4100 $this->db->rollback();
4101 return -1;
4102 }
4103
4104 $sql1 = 'UPDATE '.$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4105 $resql = $this->db->query($sql1);
4106 if (!$resql) {
4107 $this->db->rollback();
4108 return -1;
4109 }
4110
4111 $sql2 = 'DELETE FROM '.$this->db->prefix()."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4112
4113 dol_syslog(__METHOD__, LOG_DEBUG);
4114 $resql = $this->db->query($sql2);
4115 if ($resql) {
4116 if (!$notrigger) {
4117 // Call trigger
4118 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4119 if ($result < 0) {
4120 $error++;
4121 }
4122 // End call triggers
4123 }
4124
4125 if (!$error) {
4126 $this->db->commit();
4127 return 1;
4128 }
4129
4130 foreach ($this->errors as $errmsg) {
4131 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4132 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4133 }
4134 $this->db->rollback();
4135 return -1 * $error;
4136 } else {
4137 $this->error = $this->db->lasterror();
4138 return -1;
4139 }
4140 }
4141}
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