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