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