dolibarr 20.0.0
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-2024 Frédéric France <frederic.france@free.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 * Copyright (C) 2024 Solution Libre SAS <contact@solution-libre.fr>
17 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
42if (isModEnabled('productbatch')) {
43 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
44}
45
46
51{
55 public $element = 'order_supplier';
56
60 public $table_element = 'commande_fournisseur';
61
65 public $table_element_line = 'commande_fournisseurdet';
66
70 public $fk_element = 'fk_commande';
71
75 public $picto = 'supplier_order';
76
81 public $restrictiononfksoc = 1;
82
86 protected $table_ref_field = 'ref';
87
91 public $id;
92
96 public $ref;
97
101 public $ref_supplier;
102
108 public $ref_fourn;
109
113 public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
114 // -> 7=Canceled/Never received -> (reopen) 3=Process running
115 // -> 6=Canceled -> (reopen) 2=Approved
116 // -> 9=Refused -> (reopen) 1=Validated
117 // Note: billed or not is on another field "billed"
118
119 public $billed;
120
124 public $socid;
125
129 public $fourn_id;
130
134 public $date;
135
139 public $date_creation;
140
144 public $date_valid;
145
149 public $date_approve;
150
155 public $date_approve2;
156
160 public $date_commande;
161
162 //For backward compatibility
163 public $remise_percent;
164 public $methode_commande_id;
165 public $methode_commande;
166
170 public $delivery_date;
171
175 public $total_ht;
176
180 public $total_tva;
181
185 public $total_localtax1;
186
190 public $total_localtax2;
191
195 public $total_ttc;
196
197 public $source;
198
202 public $fk_project;
203
207 public $cond_reglement_id;
208
212 public $cond_reglement_code;
213
217 public $cond_reglement_label;
218
222 public $cond_reglement_doc;
223
227 public $fk_account;
228
232 public $mode_reglement_id;
233
237 public $mode_reglement_code;
238
242 public $mode_reglement;
243
247 public $user_author_id;
248
252 public $user_approve_id;
253
258 public $user_approve_id2;
259
260 public $refuse_note;
261
262 public $extraparams = array();
263
267 public $lines = array();
268
272 public $line;
273
274 // Add for supplier_proposal
275 public $origin;
276 public $origin_id;
277 public $linked_objects = array();
278
282 public $date_lim_reglement;
283 public $receptions = array();
284
285 // Multicurrency
289 public $fk_multicurrency;
290
294 public $multicurrency_code;
295
299 public $multicurrency_tx;
300
304 public $multicurrency_total_ht;
305
309 public $multicurrency_total_tva;
310
314 public $multicurrency_total_ttc;
315
343 public $fields = array(
344 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
345 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
346 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => 0, 'position' => 35),
347 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
348 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
349 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
350 'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
351 'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
352 'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
353 'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
354 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
355 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
356 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
357 'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
358 'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
359 'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
360 'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
361 'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
362 'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
363 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
364 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
365 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
366 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
367 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
368 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
369 'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
370 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
371 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
372 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
373 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
374 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
375 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
376 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
377 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
378 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
379 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
380 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
381 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
382 'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
383 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
384 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
385 'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
386 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
387 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
388 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
389 );
390
391
395 const STATUS_DRAFT = 0;
396
401
406
411
416
421
426
431
435 const STATUS_REFUSED = 9;
436
437
442
448 public function __construct($db)
449 {
450 $this->db = $db;
451
452 $this->ismultientitymanaged = 1;
453 }
454
455
463 public function fetch($id, $ref = '')
464 {
465 // Check parameters
466 if (empty($id) && empty($ref)) {
467 return -1;
468 }
469
470 $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,";
471 $sql .= " c.localtax1, c.localtax2, ";
472 $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
473 $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,";
474 $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,";
475 $sql .= " c.fk_account,";
476 $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
477 $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
478 $sql .= " cm.libelle as methode_commande,";
479 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
480 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
481 $sql .= ', c.fk_incoterms, c.location_incoterms';
482 $sql .= ', i.libelle as label_incoterms';
483 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
484 $sql .= " LEFT JOIN ".$this->db->prefix()."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
485 $sql .= " LEFT JOIN ".$this->db->prefix()."c_paiement as p ON c.fk_mode_reglement = p.id";
486 $sql .= " LEFT JOIN ".$this->db->prefix()."c_input_method as cm ON cm.rowid = c.fk_input_method";
487 $sql .= ' LEFT JOIN '.$this->db->prefix().'c_incoterms as i ON c.fk_incoterms = i.rowid';
488
489 if (empty($id)) {
490 $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
491 } else {
492 $sql .= " WHERE c.rowid=".((int) $id);
493 }
494
495 if ($ref) {
496 $sql .= " AND c.ref='".$this->db->escape($ref)."'";
497 }
498
499 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
500 $resql = $this->db->query($sql);
501 if ($resql) {
502 $obj = $this->db->fetch_object($resql);
503 if (!$obj) {
504 $this->error = 'Bill with id '.$id.' not found';
505 dol_syslog(get_class($this).'::fetch '.$this->error);
506 return 0;
507 }
508
509 $this->id = $obj->rowid;
510 $this->entity = $obj->entity;
511
512 $this->ref = $obj->ref;
513 $this->ref_supplier = $obj->ref_supplier;
514 $this->socid = $obj->fk_soc;
515 $this->fourn_id = $obj->fk_soc;
516 $this->statut = $obj->status; // deprecated
517 $this->status = $obj->status;
518 $this->billed = $obj->billed;
519 $this->user_author_id = $obj->user_author_id;
520 $this->user_validation_id = $obj->user_validation_id;
521 $this->user_approve_id = $obj->user_approve_id;
522 $this->user_approve_id2 = $obj->user_approve_id2;
523 $this->total_ht = $obj->total_ht;
524 $this->total_tva = $obj->total_tva;
525 $this->total_localtax1 = $obj->localtax1;
526 $this->total_localtax2 = $obj->localtax2;
527 $this->total_ttc = $obj->total_ttc;
528 $this->date_creation = $this->db->jdate($obj->date_creation);
529 $this->date_valid = $this->db->jdate($obj->date_valid);
530 $this->date_approve = $this->db->jdate($obj->date_approve);
531 $this->date_approve2 = $this->db->jdate($obj->date_approve2);
532 $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
533 if (isset($obj->date_commande)) {
534 $this->date = $this->date_commande;
535 } else {
536 $this->date = $this->date_creation;
537 }
538 $this->delivery_date = $this->db->jdate($obj->delivery_date);
539 $this->remise_percent = $obj->remise_percent;
540 $this->methode_commande_id = $obj->fk_input_method;
541 $this->methode_commande = $obj->methode_commande;
542
543 $this->source = $obj->source;
544 $this->fk_project = $obj->fk_project;
545 $this->cond_reglement_id = $obj->fk_cond_reglement;
546 $this->cond_reglement_code = $obj->cond_reglement_code;
547 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
548 $this->cond_reglement_label = $obj->cond_reglement_label;
549 $this->cond_reglement_doc = $obj->cond_reglement_doc;
550 $this->fk_account = $obj->fk_account;
551 $this->mode_reglement_id = $obj->fk_mode_reglement;
552 $this->mode_reglement_code = $obj->mode_reglement_code;
553 $this->mode_reglement = $obj->mode_reglement_libelle;
554 $this->note = $obj->note_private; // deprecated
555 $this->note_private = $obj->note_private;
556 $this->note_public = $obj->note_public;
557 $this->model_pdf = $obj->model_pdf;
558
559 //Incoterms
560 $this->fk_incoterms = $obj->fk_incoterms;
561 $this->location_incoterms = $obj->location_incoterms;
562 $this->label_incoterms = $obj->label_incoterms;
563
564 // Multicurrency
565 $this->fk_multicurrency = $obj->fk_multicurrency;
566 $this->multicurrency_code = $obj->multicurrency_code;
567 $this->multicurrency_tx = $obj->multicurrency_tx;
568 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
569 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
570 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
571
572 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
573
574 $this->db->free($resql);
575
576 // Retrieve all extrafield
577 // fetch optionals attributes and labels
578 $this->fetch_optionals();
579
580 // Lines
581 $result = $this->fetch_lines();
582
583 if ($result < 0) {
584 return -1;
585 } else {
586 return 1;
587 }
588 } else {
589 $this->error = $this->db->error()." sql=".$sql;
590 return -1;
591 }
592 }
593
594 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
601 public function fetch_lines($only_product = 0)
602 {
603 // phpcs:enable
604
605 $this->lines = array();
606
607 $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
608 $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
609 $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
610 $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.info_bits, l.special_code, l.fk_parent_line, l.rang,";
611 $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,";
612 $sql .= " l.fk_unit,";
613 $sql .= " l.date_start, l.date_end,";
614 $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
615 $sql .= " FROM ".$this->db->prefix()."commande_fournisseurdet as l";
616 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON l.fk_product = p.rowid';
617 $sql .= " WHERE l.fk_commande = ".((int) $this->id);
618 if ($only_product) {
619 $sql .= ' AND p.fk_product_type = 0';
620 }
621 $sql .= " ORDER BY l.rang, l.rowid";
622 //print $sql;
623
624 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
625
626 $result = $this->db->query($sql);
627 if ($result) {
628 $num = $this->db->num_rows($result);
629 $i = 0;
630
631 while ($i < $num) {
632 $objp = $this->db->fetch_object($result);
633
634 $line = new CommandeFournisseurLigne($this->db);
635
636 $line->id = $objp->rowid;
637 $line->fk_commande = $objp->fk_commande;
638 $line->desc = $objp->description;
639 $line->description = $objp->description;
640 $line->qty = $objp->qty;
641 $line->tva_tx = $objp->tva_tx;
642 $line->localtax1_tx = $objp->localtax1_tx;
643 $line->localtax2_tx = $objp->localtax2_tx;
644 $line->localtax1_type = $objp->localtax1_type;
645 $line->localtax2_type = $objp->localtax2_type;
646 $line->subprice = $objp->subprice;
647 $line->pu_ht = $objp->subprice;
648 $line->remise_percent = $objp->remise_percent;
649
650 $line->vat_src_code = $objp->vat_src_code;
651 $line->total_ht = $objp->total_ht;
652 $line->total_tva = $objp->total_tva;
653 $line->total_localtax1 = $objp->total_localtax1;
654 $line->total_localtax2 = $objp->total_localtax2;
655 $line->total_ttc = $objp->total_ttc;
656 $line->product_type = $objp->product_type;
657
658 $line->fk_product = $objp->fk_product;
659
660 $line->libelle = $objp->product_label; // deprecated
661 $line->product_label = $objp->product_label;
662 $line->product_desc = $objp->product_desc;
663 $line->product_tobatch = $objp->product_tobatch;
664 $line->product_barcode = $objp->product_barcode;
665
666 $line->ref = $objp->product_ref; // Ref of product
667 $line->product_ref = $objp->product_ref; // Ref of product
668 $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
669 $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
670
671 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
672 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
673 // Move this into another method and call it when required.
674
675 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
676 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
677 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
678 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
679 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
680 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
681 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
682 $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
683 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
684 $sqlsearchpackage .= " LIMIT 1";
685
686 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
687 if ($resqlsearchpackage) {
688 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
689 if ($objsearchpackage) {
690 $line->fk_fournprice = $objsearchpackage->rowid;
691 $line->packaging = $objsearchpackage->packaging;
692 }
693 } else {
694 $this->error = $this->db->lasterror();
695 return -1;
696 }
697 }
698
699 $line->date_start = $this->db->jdate($objp->date_start);
700 $line->date_end = $this->db->jdate($objp->date_end);
701 $line->fk_unit = $objp->fk_unit;
702
703 // Multicurrency
704 $line->fk_multicurrency = $objp->fk_multicurrency;
705 $line->multicurrency_code = $objp->multicurrency_code;
706 $line->multicurrency_subprice = $objp->multicurrency_subprice;
707 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
708 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
709 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
710
711 $line->info_bits = $objp->info_bits;
712 $line->special_code = $objp->special_code;
713 $line->fk_parent_line = $objp->fk_parent_line;
714
715 $line->rang = $objp->rang;
716
717 // Retrieve all extrafield
718 // fetch optionals attributes and labels
719 $line->fetch_optionals();
720
721 $this->lines[$i] = $line;
722
723 $i++;
724 }
725 $this->db->free($result);
726
727 return $num;
728 } else {
729 $this->error = $this->db->error()." sql=".$sql;
730 return -1;
731 }
732 }
733
742 public function valid($user, $idwarehouse = 0, $notrigger = 0)
743 {
744 global $conf;
745 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
746
747 $error = 0;
748
749 dol_syslog(get_class($this)."::valid");
750 $result = 0;
751 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
752 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
753 $this->db->begin();
754
755 // Definition of supplier order numbering model name
756 $soc = new Societe($this->db);
757 $soc->fetch($this->fourn_id);
758
759 // Check if object has a temporary ref
760 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
761 $num = $this->getNextNumRef($soc);
762 } else {
763 $num = $this->ref;
764 }
765 $this->newref = dol_sanitizeFileName($num);
766
767 $sql = 'UPDATE '.$this->db->prefix()."commande_fournisseur";
768 $sql .= " SET ref='".$this->db->escape($num)."',";
769 $sql .= " fk_statut = ".((int) self::STATUS_VALIDATED).",";
770 $sql .= " date_valid='".$this->db->idate(dol_now())."',";
771 $sql .= " fk_user_valid = ".((int) $user->id);
772 $sql .= " WHERE rowid = ".((int) $this->id);
773 $sql .= " AND fk_statut = ".((int) self::STATUS_DRAFT);
774
775 $resql = $this->db->query($sql);
776 if (!$resql) {
777 dol_print_error($this->db);
778 $error++;
779 }
780
781 if (!$error && !$notrigger) {
782 // Call trigger
783 $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
784 if ($result < 0) {
785 $error++;
786 }
787 // End call triggers
788 }
789
790 if (!$error) {
791 $this->oldref = $this->ref;
792
793 // Rename directory if dir was a temporary ref
794 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
795 // Now we rename also files into index
796 $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)."'";
797 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
798 $resql = $this->db->query($sql);
799 if (!$resql) {
800 $error++;
801 $this->error = $this->db->lasterror();
802 }
803 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
804 $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
805 $resql = $this->db->query($sql);
806 if (!$resql) {
807 $error++;
808 $this->error = $this->db->lasterror();
809 }
810
811 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
812 $oldref = dol_sanitizeFileName($this->ref);
813 $newref = dol_sanitizeFileName($num);
814 $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
815 $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
816 if (!$error && file_exists($dirsource)) {
817 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
818
819 if (@rename($dirsource, $dirdest)) {
820 dol_syslog("Rename ok");
821 // Rename docs starting with $oldref with $newref
822 $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
823 foreach ($listoffiles as $fileentry) {
824 $dirsource = $fileentry['name'];
825 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
826 $dirsource = $fileentry['path'].'/'.$dirsource;
827 $dirdest = $fileentry['path'].'/'.$dirdest;
828 @rename($dirsource, $dirdest);
829 }
830 }
831 }
832 }
833 }
834
835 if (!$error) {
836 $result = 1;
838 $this->statut = self::STATUS_VALIDATED; // deprecated
839 $this->ref = $num;
840 }
841
842 if (!$error) {
843 $this->db->commit();
844 return 1;
845 } else {
846 $this->db->rollback();
847 return -1;
848 }
849 } else {
850 $this->error = 'NotAuthorized';
851 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
852 return -1;
853 }
854 }
855
862 public function getLibStatut($mode = 0)
863 {
864 return $this->LibStatut($this->statut, $mode, $this->billed);
865 }
866
867 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
876 public function LibStatut($status, $mode = 0, $billed = 0)
877 {
878 // phpcs:enable
879 global $langs, $hookmanager;
880
881 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
882 $langs->load('orders');
883
884 $this->labelStatus[0] = 'StatusSupplierOrderDraft';
885 $this->labelStatus[1] = 'StatusSupplierOrderValidated';
886 $this->labelStatus[2] = 'StatusSupplierOrderApproved';
887 if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
888 $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
889 } else {
890 $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
891 }
892 $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
893 $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
894 $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
895 $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
896 $this->labelStatus[9] = 'StatusSupplierOrderRefused';
897
898 // List of language codes for status
899 $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
900 $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
901 $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
902 $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
903 $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
904 $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
905 $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
906 $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
907 $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
908 }
909
910 $statustrans = array(
911 0 => 'status0',
912 1 => 'status1b',
913 2 => 'status1',
914 3 => 'status4',
915 4 => 'status4b',
916 5 => 'status6',
917 6 => 'status9',
918 7 => 'status9',
919 9 => 'status9',
920 );
921
922 $statusClass = 'status0';
923 if (!empty($statustrans[$status])) {
924 $statusClass = $statustrans[$status];
925 }
926
927 $billedtext = '';
928 if ($billed) {
929 $billedtext = ' - '.$langs->trans("Billed");
930 }
931 if ($status == 5 && $billed) {
932 $statusClass = 'status6';
933 }
934
935 $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]).$billedtext;
936 $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
937
938 $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
939 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
940 if ($reshook > 0) {
941 return $hookmanager->resPrint;
942 }
943
944 return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
945 }
946
954 public function getTooltipContentArray($params)
955 {
956 global $conf, $langs, $user;
957
958 $langs->loadLangs(['bills', 'orders']);
959
960 $datas = [];
961 $nofetch = !empty($params['nofetch']);
962
963 if ($user->hasRight("fournisseur", "commande", "read")) {
964 $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
965 if (isset($this->statut)) {
966 $datas['picto'] .= ' '.$this->getLibStatut(5);
967 }
968 if (!empty($this->ref)) {
969 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
970 }
971 if (!empty($this->ref_supplier)) {
972 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
973 }
974 if (!$nofetch) {
975 $langs->load('companies');
976 if (empty($this->thirdparty)) {
977 $this->fetch_thirdparty();
978 }
979 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
980 }
981 if (!empty($this->total_ht)) {
982 $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
983 }
984 if (!empty($this->total_tva)) {
985 $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
986 }
987 if (!empty($this->total_ttc)) {
988 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
989 }
990 if (!empty($this->date)) {
991 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
992 }
993 if (!empty($this->delivery_date)) {
994 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
995 }
996 }
997 return $datas;
998 }
999
1010 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1011 {
1012 global $langs, $user, $hookmanager;
1013
1014 $result = '';
1015 $params = [
1016 'id' => $this->id,
1017 'objecttype' => $this->element,
1018 'option' => $option,
1019 'nofetch' => 1
1020 ];
1021 $classfortooltip = 'classfortooltip';
1022 $dataparams = '';
1023 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1024 $classfortooltip = 'classforajaxtooltip';
1025 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1026 $label = '';
1027 } else {
1028 $label = implode($this->getTooltipContentArray($params));
1029 }
1030
1031 $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
1032
1033 if ($option !== 'nolink') {
1034 // Add param to save lastsearch_values or not
1035 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1036 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1037 $add_save_lastsearch_values = 1;
1038 }
1039 if ($add_save_lastsearch_values) {
1040 $url .= '&save_lastsearch_values=1';
1041 }
1042 }
1043
1044 $linkclose = '';
1045 if (empty($notooltip)) {
1046 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1047 $label = $langs->trans("ShowOrder");
1048 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1049 }
1050 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1051 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1052 }
1053
1054 $linkstart = '<a href="'.$url.'"';
1055 $linkstart .= $linkclose.'>';
1056 $linkend = '</a>';
1057
1058 $result .= $linkstart;
1059 if ($withpicto) {
1060 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1061 }
1062 if ($withpicto != 2) {
1063 $result .= $this->ref;
1064 }
1065 $result .= $linkend;
1066
1067 if ($addlinktonotes) {
1068 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1069 if ($txttoshow) {
1070 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
1071 $result .= ' <span class="note inline-block">';
1072 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
1073 $result .= img_picto('', 'note');
1074 $result .= '</a>';
1075 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1076 //$result.='</a>';
1077 $result .= '</span>';
1078 }
1079 }
1080
1081 global $action;
1082 $hookmanager->initHooks(array($this->element . 'dao'));
1083 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1084 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1085 if ($reshook > 0) {
1086 $result = $hookmanager->resPrint;
1087 } else {
1088 $result .= $hookmanager->resPrint;
1089 }
1090 return $result;
1091 }
1092
1093
1101 public function getNextNumRef($soc)
1102 {
1103 global $langs, $conf;
1104 $langs->load("orders");
1105
1106 if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1107 $mybool = false;
1108
1109 $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1110 $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1111
1112 // Include file with class
1113 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1114
1115 foreach ($dirmodels as $reldir) {
1116 $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1117
1118 // Load file with numbering class (if found)
1119 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1120 }
1121
1122 if ($mybool === false) {
1123 dol_print_error(null, "Failed to include file ".$file);
1124 return '';
1125 }
1126
1127 $obj = new $classname();
1128 $numref = $obj->getNextValue($soc, $this);
1129
1130 if ($numref != "") {
1131 return $numref;
1132 } else {
1133 $this->error = $obj->error;
1134 return -1;
1135 }
1136 } else {
1137 $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1138 return -2;
1139 }
1140 }
1147 public function classifyBilled(User $user)
1148 {
1149 $error = 0;
1150
1151 if ($this->billed) {
1152 return 0;
1153 }
1154
1155 $this->db->begin();
1156
1157 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur SET billed = 1';
1158 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1159
1160 if ($this->db->query($sql)) {
1161 if (!$error) {
1162 // Call trigger
1163 $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1164 if ($result < 0) {
1165 $error++;
1166 }
1167 // End call triggers
1168 }
1169
1170 if (!$error) {
1171 $this->billed = 1;
1172
1173 $this->db->commit();
1174 return 1;
1175 } else {
1176 $this->db->rollback();
1177 return -1;
1178 }
1179 } else {
1180 dol_print_error($this->db);
1181
1182 $this->db->rollback();
1183 return -1;
1184 }
1185 }
1186
1195 public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1196 {
1197 global $langs, $conf;
1198 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1199
1200 $error = 0;
1201
1202 dol_syslog(get_class($this)."::approve");
1203
1204 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1205 $now = dol_now();
1206
1207 $this->db->begin();
1208
1209 // Definition of order numbering model name
1210 $soc = new Societe($this->db);
1211 $soc->fetch($this->fourn_id);
1212
1213 // Check if object has a temporary ref
1214 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1215 $num = $this->getNextNumRef($soc);
1216 } else {
1217 $num = $this->ref;
1218 }
1219 $this->newref = dol_sanitizeFileName($num);
1220
1221 // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1222 $movetoapprovestatus = true;
1223 $comment = '';
1224
1225 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1226 $sql .= " SET ref='".$this->db->escape($num)."',";
1227 if (empty($secondlevel)) { // standard or first level approval
1228 $sql .= " date_approve='".$this->db->idate($now)."',";
1229 $sql .= " fk_user_approve = ".$user->id;
1230 if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1231 if (empty($this->user_approve_id2)) {
1232 $movetoapprovestatus = false; // second level approval not done
1233 $comment = ' (first level)';
1234 }
1235 }
1236 } else { // request a second level approval
1237 $sql .= " date_approve2='".$this->db->idate($now)."',";
1238 $sql .= " fk_user_approve2 = ".((int) $user->id);
1239 if (empty($this->user_approve_id)) {
1240 $movetoapprovestatus = false; // first level approval not done
1241 }
1242 $comment = ' (second level)';
1243 }
1244 // If double approval is required and first approval, we keep status to 1 = validated
1245 if ($movetoapprovestatus) {
1246 $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1247 } else {
1248 $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1249 }
1250 $sql .= " WHERE rowid = ".((int) $this->id);
1251 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1252
1253 if ($this->db->query($sql)) {
1254 if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1255 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1256 if ($result < 0 && $result != -2) { // -2 means already exists
1257 $error++;
1258 }
1259 }
1260
1261 // If stock is incremented on validate order, we must increment it
1262 if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1263 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1264 $langs->load("agenda");
1265
1266 $cpt = count($this->lines);
1267 for ($i = 0; $i < $cpt; $i++) {
1268 // Product with reference
1269 if ($this->lines[$i]->fk_product > 0) {
1270 $this->line = $this->lines[$i];
1271 $mouvP = new MouvementStock($this->db);
1272 $mouvP->origin = &$this;
1273 $mouvP->setOrigin($this->element, $this->id);
1274 // We decrement stock of product (and sub-products)
1275 $up_ht_disc = $this->lines[$i]->subprice;
1276 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1277 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1278 }
1279 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1280 if ($result < 0) {
1281 $error++;
1282 }
1283 unset($this->line);
1284 }
1285 }
1286 }
1287
1288 if (!$error) {
1289 // Call trigger
1290 $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1291 if ($result < 0) {
1292 $error++;
1293 }
1294 // End call triggers
1295 }
1296
1297 if (!$error) {
1298 $this->ref = $this->newref;
1299
1300 if ($movetoapprovestatus) {
1302 } else {
1304 }
1305 if (empty($secondlevel)) { // standard or first level approval
1306 $this->date_approve = $now;
1307 $this->user_approve_id = $user->id;
1308 } else { // request a second level approval
1309 $this->date_approve2 = $now;
1310 $this->user_approve_id2 = $user->id;
1311 }
1312
1313 $this->db->commit();
1314 return 1;
1315 } else {
1316 $this->db->rollback();
1317 return -1;
1318 }
1319 } else {
1320 $this->db->rollback();
1321 $this->error = $this->db->lasterror();
1322 return -1;
1323 }
1324 } else {
1325 dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1326 }
1327 return -1;
1328 }
1329
1336 public function refuse($user)
1337 {
1338 global $conf, $langs;
1339
1340 $error = 0;
1341
1342 dol_syslog(get_class($this)."::refuse");
1343 $result = 0;
1344 if ($user->hasRight("fournisseur", "commande", "approuver")) {
1345 $this->db->begin();
1346
1347 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1348 $sql .= " WHERE rowid = ".((int) $this->id);
1349
1350 if ($this->db->query($sql)) {
1351 $result = 0;
1352
1353 if ($error == 0) {
1354 // Call trigger
1355 $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1356 if ($result < 0) {
1357 $error++;
1358 $this->db->rollback();
1359 } else {
1360 $this->db->commit();
1361 }
1362 // End call triggers
1363 }
1364 } else {
1365 $this->db->rollback();
1366 $this->error = $this->db->lasterror();
1367 dol_syslog(get_class($this)."::refuse Error -1");
1368 $result = -1;
1369 }
1370 } else {
1371 dol_syslog(get_class($this)."::refuse Not Authorized");
1372 }
1373 return $result;
1374 }
1375
1376 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1385 public function Cancel($user, $idwarehouse = -1)
1386 {
1387 // phpcs:enable
1388 global $langs, $conf;
1389
1390 $error = 0;
1391
1392 //dol_syslog("CommandeFournisseur::Cancel");
1393 $result = 0;
1394 if ($user->hasRight("fournisseur", "commande", "commander")) {
1395 $statut = self::STATUS_CANCELED;
1396
1397 $this->db->begin();
1398
1399 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur SET fk_statut = ".((int) $statut);
1400 $sql .= " WHERE rowid = ".((int) $this->id);
1401 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1402 if ($this->db->query($sql)) {
1403 $result = 0;
1404
1405 // Call trigger
1406 $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1407 if ($result < 0) {
1408 $error++;
1409 }
1410 // End call triggers
1411
1412 if ($error == 0) {
1413 $this->db->commit();
1414 return 1;
1415 } else {
1416 $this->db->rollback();
1417 return -1;
1418 }
1419 } else {
1420 $this->db->rollback();
1421 $this->error = $this->db->lasterror();
1422 dol_syslog(get_class($this)."::cancel ".$this->error);
1423 return -1;
1424 }
1425 } else {
1426 dol_syslog(get_class($this)."::cancel Not Authorized");
1427 return -1;
1428 }
1429 }
1430
1440 public function commande($user, $date, $methode, $comment = '')
1441 {
1442 global $langs;
1443 dol_syslog(get_class($this)."::commande");
1444 $error = 0;
1445 if ($user->hasRight("fournisseur", "commande", "commander")) {
1446 $this->db->begin();
1447
1448 $newnoteprivate = $this->note_private;
1449 if ($comment) {
1450 $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1451 }
1452
1453 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1454 $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1455 $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1456 $sql .= " WHERE rowid=".((int) $this->id);
1457
1458 dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1459 if ($this->db->query($sql)) {
1461 $this->methode_commande_id = $methode;
1462 $this->date_commande = $date;
1463 $this->context = array('comments' => $comment);
1464
1465 // Call trigger
1466 $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1467 if ($result < 0) {
1468 $error++;
1469 }
1470 // End call triggers
1471 } else {
1472 $error++;
1473 $this->error = $this->db->lasterror();
1474 $this->errors[] = $this->db->lasterror();
1475 }
1476
1477 if (!$error) {
1478 $this->db->commit();
1479 } else {
1480 $this->db->rollback();
1481 }
1482 } else {
1483 $error++;
1484 $this->error = $langs->trans('NotAuthorized');
1485 $this->errors[] = $langs->trans('NotAuthorized');
1486 dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1487 }
1488
1489 return ($error ? -1 : 1);
1490 }
1491
1499 public function create($user, $notrigger = 0)
1500 {
1501 global $langs, $conf, $hookmanager;
1502
1503 $this->db->begin();
1504
1505 $error = 0;
1506 $now = dol_now();
1507
1508 // set tmp vars
1509 $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1510 if (empty($date)) {
1511 $date = $now;
1512 }
1513 $delivery_date = $this->delivery_date;
1514
1515 // Clean parameters
1516 if (empty($this->source)) {
1517 $this->source = 0;
1518 }
1519
1520 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1521 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1522 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1523 } else {
1524 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1525 }
1526 if (empty($this->fk_multicurrency)) {
1527 $this->multicurrency_code = $conf->currency;
1528 $this->fk_multicurrency = 0;
1529 $this->multicurrency_tx = 1;
1530 }
1531
1532 $this->statut = self::STATUS_DRAFT; // deprecated
1533 $this->status = self::STATUS_DRAFT;
1534
1535 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseur (";
1536 $sql .= "ref";
1537 $sql .= ", ref_supplier";
1538 $sql .= ", note_private";
1539 $sql .= ", note_public";
1540 $sql .= ", entity";
1541 $sql .= ", fk_soc";
1542 $sql .= ", fk_projet";
1543 $sql .= ", date_creation";
1544 $sql .= ", date_livraison";
1545 $sql .= ", fk_user_author";
1546 $sql .= ", fk_statut";
1547 $sql .= ", source";
1548 $sql .= ", model_pdf";
1549 $sql .= ", fk_mode_reglement";
1550 $sql .= ", fk_cond_reglement";
1551 $sql .= ", fk_account";
1552 $sql .= ", fk_incoterms, location_incoterms";
1553 $sql .= ", fk_multicurrency";
1554 $sql .= ", multicurrency_code";
1555 $sql .= ", multicurrency_tx";
1556 $sql .= ") ";
1557 $sql .= " VALUES (";
1558 $sql .= "'(PROV)'";
1559 $sql .= ", ".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "NULL");
1560 $sql .= ", '".$this->db->escape($this->note_private)."'";
1561 $sql .= ", '".$this->db->escape($this->note_public)."'";
1562 $sql .= ", ".setEntity($this);
1563 $sql .= ", ".((int) $this->socid);
1564 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1565 $sql .= ", '".$this->db->idate($date)."'";
1566 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1567 $sql .= ", ".((int) $user->id);
1568 $sql .= ", ".self::STATUS_DRAFT;
1569 $sql .= ", ".((int) $this->source);
1570 $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1571 $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1572 $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1573 $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1574 $sql .= ", ".(int) $this->fk_incoterms;
1575 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1576 $sql .= ", ".(int) $this->fk_multicurrency;
1577 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1578 $sql .= ", ".(float) $this->multicurrency_tx;
1579 $sql .= ")";
1580
1581 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1582 if ($this->db->query($sql)) {
1583 $this->id = $this->db->last_insert_id($this->db->prefix()."commande_fournisseur");
1584
1585 if ($this->id) {
1586 $num = count($this->lines);
1587
1588 // insert products details into database
1589 for ($i = 0; $i < $num; $i++) {
1590 $line = $this->lines[$i];
1591 if (!is_object($line)) {
1592 $line = (object) $line;
1593 }
1594
1595 //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1596
1597 // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1598 $result = $this->addline(
1599 $line->desc,
1600 $line->subprice,
1601 $line->qty,
1602 $line->tva_tx,
1603 $line->localtax1_tx,
1604 $line->localtax2_tx,
1605 $line->fk_product,
1606 0,
1607 $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
1608 $line->remise_percent,
1609 'HT',
1610 0,
1611 $line->product_type,
1612 $line->info_bits,
1613 false,
1614 $line->date_start,
1615 $line->date_end,
1616 $line->array_options,
1617 $line->fk_unit,
1618 $line->multicurrency_subprice, // pu_ht_devise
1619 $line->origin, // origin
1620 $line->origin_id, // origin_id
1621 $line->rang, // rang
1622 $line->special_code
1623 );
1624 if ($result < 0) {
1625 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1626 $this->db->rollback();
1627 return -1;
1628 }
1629 }
1630
1631 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
1632 $sql .= " SET ref='(PROV".$this->id.")'";
1633 $sql .= " WHERE rowid=".((int) $this->id);
1634
1635 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1636 if ($this->db->query($sql)) {
1637 // Add link with price request and supplier order
1638 if ($this->id) {
1639 $this->ref = "(PROV".$this->id.")";
1640
1641 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1642 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1643 }
1644
1645 // Add object linked
1646 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1647 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1648 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, ...))
1649 foreach ($tmp_origin_id as $origin_id) {
1650 $ret = $this->add_object_linked($origin, $origin_id);
1651 if (!$ret) {
1652 dol_print_error($this->db);
1653 $error++;
1654 }
1655 }
1656 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1657 $origin_id = $tmp_origin_id;
1658 $ret = $this->add_object_linked($origin, $origin_id);
1659 if (!$ret) {
1660 dol_print_error($this->db);
1661 $error++;
1662 }
1663 }
1664 }
1665 }
1666 }
1667
1668 if (!$error) {
1669 $result = $this->insertExtraFields();
1670 if ($result < 0) {
1671 $error++;
1672 }
1673 }
1674
1675 if (!$error && !$notrigger) {
1676 // Call trigger
1677 $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1678 if ($result < 0) {
1679 $this->db->rollback();
1680
1681 return -1;
1682 }
1683 // End call triggers
1684 }
1685
1686 $this->db->commit();
1687 return $this->id;
1688 } else {
1689 $this->error = $this->db->lasterror();
1690 $this->db->rollback();
1691
1692 return -2;
1693 }
1694 } else {
1695 $this->error = 'Failed to get ID of inserted line';
1696
1697 return -1;
1698 }
1699 } else {
1700 $this->error = $this->db->lasterror();
1701 $this->db->rollback();
1702
1703 return -1;
1704 }
1705 }
1706
1714 public function update(User $user, $notrigger = 0)
1715 {
1716 global $conf;
1717
1718 $error = 0;
1719
1720 // Clean parameters
1721 if (isset($this->ref)) {
1722 $this->ref = trim($this->ref);
1723 }
1724 if (isset($this->ref_supplier)) {
1725 $this->ref_supplier = trim($this->ref_supplier);
1726 }
1727 if (isset($this->note_private)) {
1728 $this->note_private = trim($this->note_private);
1729 }
1730 if (isset($this->note_public)) {
1731 $this->note_public = trim($this->note_public);
1732 }
1733 if (isset($this->model_pdf)) {
1734 $this->model_pdf = trim($this->model_pdf);
1735 }
1736 if (isset($this->import_key)) {
1737 $this->import_key = trim($this->import_key);
1738 }
1739
1740 // Update request
1741 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
1742
1743 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1744 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1745 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1746 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1747 $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1748 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1749 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1750 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1751 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1752 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1753 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1754 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1755 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1756 $sql .= " fk_user_valid=".(isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null").",";
1757 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1758 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1759 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1760 $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1761 //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1762 $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1763 //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1764 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1765 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1766 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1767 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1768
1769 $sql .= " WHERE rowid=".((int) $this->id);
1770
1771 $this->db->begin();
1772
1773 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1774 $resql = $this->db->query($sql);
1775 if (!$resql) {
1776 $error++;
1777 $this->errors[] = "Error ".$this->db->lasterror();
1778 }
1779
1780 if (!$error) {
1781 $result = $this->insertExtraFields();
1782 if ($result < 0) {
1783 $error++;
1784 }
1785 }
1786
1787 if (!$error && !$notrigger) {
1788 // Call trigger
1789 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1790 if ($result < 0) {
1791 $error++;
1792 }
1793 // End call triggers
1794 }
1795
1796 // Commit or rollback
1797 if ($error) {
1798 foreach ($this->errors as $errmsg) {
1799 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1800 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1801 }
1802 $this->db->rollback();
1803 return -1 * $error;
1804 } else {
1805 $this->db->commit();
1806 return 1;
1807 }
1808 }
1809
1818 public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1819 {
1820 global $conf, $user, $hookmanager;
1821
1822 $error = 0;
1823
1824 $this->db->begin();
1825
1826 // get extrafields so they will be clone
1827 foreach ($this->lines as $line) {
1828 $line->fetch_optionals();
1829 }
1830
1831 // Load source object
1832 $objFrom = clone $this;
1833
1834 // Change socid if needed
1835 if (!empty($socid) && $socid != $this->socid) {
1836 $objsoc = new Societe($this->db);
1837
1838 if ($objsoc->fetch($socid) > 0) {
1839 $this->socid = $objsoc->id;
1840 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1841 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1842 $this->fk_project = 0;
1843 $this->fk_delivery_address = 0;
1844 }
1845
1846 // TODO Change product price if multi-prices
1847 }
1848
1849 $this->id = 0;
1850 $this->statut = self::STATUS_DRAFT;
1851
1852 // Clear fields
1853 $this->user_author_id = $user->id;
1854 $this->user_validation_id = 0;
1855
1856 $this->date = dol_now();
1857 $this->date_creation = 0;
1858 $this->date_validation = 0;
1859 $this->date_commande = 0;
1860 $this->ref_supplier = '';
1861 $this->user_approve_id = 0;
1862 $this->user_approve_id2 = 0;
1863 $this->date_approve = 0;
1864 $this->date_approve2 = 0;
1865
1866 // Create clone
1867 $this->context['createfromclone'] = 'createfromclone';
1868 $result = $this->create($user, $notrigger);
1869 if ($result < 0) {
1870 $error++;
1871 }
1872
1873 if (!$error) {
1874 // Hook of thirdparty module
1875 if (is_object($hookmanager)) {
1876 $parameters = array('objFrom' => $objFrom);
1877 $action = '';
1878 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1879 if ($reshook < 0) {
1880 $this->setErrorsFromObject($hookmanager);
1881 $error++;
1882 }
1883 }
1884 }
1885
1886 unset($this->context['createfromclone']);
1887
1888 // End
1889 if (!$error) {
1890 $this->db->commit();
1891 return $this->id;
1892 } else {
1893 $this->db->rollback();
1894 return -1;
1895 }
1896 }
1897
1927 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 = 0, $date_start = null, $date_end = null, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0, $rang = -1, $special_code = 0)
1928 {
1929 global $langs, $mysoc, $conf;
1930
1931 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");
1932 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1933
1934 if ($this->statut == self::STATUS_DRAFT) {
1935 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1936
1937 // Clean parameters
1938 if (empty($qty)) {
1939 $qty = 0;
1940 }
1941 if (!$info_bits) {
1942 $info_bits = 0;
1943 }
1944 if (empty($txtva)) {
1945 $txtva = 0;
1946 }
1947 if (empty($rang)) {
1948 $rang = 0;
1949 }
1950 if (empty($txlocaltax1)) {
1951 $txlocaltax1 = 0;
1952 }
1953 if (empty($txlocaltax2)) {
1954 $txlocaltax2 = 0;
1955 }
1956 if (empty($remise_percent)) {
1957 $remise_percent = 0;
1958 }
1959
1960 $remise_percent = price2num($remise_percent);
1961 $qty = price2num($qty);
1962 $pu_ht = price2num($pu_ht);
1963 $pu_ht_devise = price2num($pu_ht_devise);
1964 $pu_ttc = price2num($pu_ttc);
1965 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
1966 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1967 }
1968 $txlocaltax1 = price2num($txlocaltax1);
1969 $txlocaltax2 = price2num($txlocaltax2);
1970 if ($price_base_type == 'HT') {
1971 $pu = $pu_ht;
1972 } else {
1973 $pu = $pu_ttc;
1974 }
1975 $desc = trim($desc);
1976
1977 // Check parameters
1978 if ($qty < 0 && !$fk_product) {
1979 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1980 return -1;
1981 }
1982 if ($type < 0) {
1983 return -1;
1984 }
1985 if ($date_start && $date_end && $date_start > $date_end) {
1986 $langs->load("errors");
1987 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1988 return -1;
1989 }
1990
1991
1992 $this->db->begin();
1993
1994 $product_type = $type;
1995 $label = ''; // deprecated
1996
1997 if ($fk_product > 0) {
1998 if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
1999 // Check quantity is enough
2000 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);
2001 $prod = new ProductFournisseur($this->db);
2002 if ($prod->fetch($fk_product) > 0) {
2003 $product_type = $prod->type;
2004 $label = $prod->label;
2005
2006 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2007 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2008 $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
2009
2010 // If supplier order created from sales order, we take best supplier price
2011 // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2012 if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2013 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2014 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2015 // is remise percent not keyed but present for the product we add it
2016 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2017 $remise_percent = $prod->remise_percent;
2018 }
2019 }
2020 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2021 $langs->load("errors");
2022 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2023 $this->db->rollback();
2024 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2025 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2026 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2027 return -1;
2028 }
2029 if ($result == -1) {
2030 $langs->load("errors");
2031 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2032 $this->db->rollback();
2033 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2034 return -1;
2035 }
2036 if ($result < -1) {
2037 $this->error = $prod->error;
2038 $this->db->rollback();
2039 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2040 return -1;
2041 }
2042 } else {
2043 $this->error = $prod->error;
2044 $this->db->rollback();
2045 return -1;
2046 }
2047 }
2048
2049 // Predefine quantity according to packaging
2050 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2051 $prod = new Product($this->db);
2052 $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2053
2054 if ($qty < $prod->packaging) {
2055 $qty = $prod->packaging;
2056 } else {
2057 if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
2058 $coeff = intval($qty / $prod->packaging) + 1;
2059 $qty = $prod->packaging * $coeff;
2060 setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2061 }
2062 }
2063 }
2064 }
2065
2066 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2067 $pu = 0;
2068 }
2069
2070 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2071
2072 // Clean vat code
2073 $reg = array();
2074 $vat_src_code = '';
2075 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2076 $vat_src_code = $reg[1];
2077 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2078 }
2079
2080 // Calcul du total TTC et de la TVA pour la ligne a partir de
2081 // qty, pu, remise_percent et txtva
2082 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2083 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2084
2085 $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);
2086
2087 $total_ht = $tabprice[0];
2088 $total_tva = $tabprice[1];
2089 $total_ttc = $tabprice[2];
2090 $total_localtax1 = $tabprice[9];
2091 $total_localtax2 = $tabprice[10];
2092 $pu = $pu_ht = $tabprice[3];
2093
2094 // MultiCurrency
2095 $multicurrency_total_ht = $tabprice[16];
2096 $multicurrency_total_tva = $tabprice[17];
2097 $multicurrency_total_ttc = $tabprice[18];
2098 $pu_ht_devise = $tabprice[19];
2099
2100 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2101 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2102
2103 if ($rang < 0) {
2104 $rangmax = $this->line_max();
2105 $rang = $rangmax + 1;
2106 }
2107
2108 // Insert line
2109 $this->line = new CommandeFournisseurLigne($this->db);
2110
2111 $this->line->context = $this->context;
2112
2113 $this->line->fk_commande = $this->id;
2114 $this->line->label = $label;
2115 $this->line->ref_fourn = $ref_supplier;
2116 $this->line->ref_supplier = $ref_supplier;
2117 $this->line->desc = $desc;
2118 $this->line->qty = $qty;
2119 $this->line->tva_tx = $txtva;
2120 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2121 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2122 $this->line->localtax1_type = $localtax1_type;
2123 $this->line->localtax2_type = $localtax2_type;
2124 $this->line->fk_product = $fk_product;
2125 $this->line->product_type = $product_type;
2126 $this->line->remise_percent = $remise_percent;
2127 $this->line->subprice = $pu_ht;
2128 $this->line->rang = $rang;
2129 $this->line->info_bits = $info_bits;
2130
2131 $this->line->vat_src_code = $vat_src_code;
2132 $this->line->total_ht = $total_ht;
2133 $this->line->total_tva = $total_tva;
2134 $this->line->total_localtax1 = $total_localtax1;
2135 $this->line->total_localtax2 = $total_localtax2;
2136 $this->line->total_ttc = $total_ttc;
2137 $this->line->product_type = $type;
2138 $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2139 $this->line->origin = $origin;
2140 $this->line->origin_id = $origin_id;
2141 $this->line->fk_unit = $fk_unit;
2142
2143 $this->line->date_start = $date_start;
2144 $this->line->date_end = $date_end;
2145
2146 // Multicurrency
2147 $this->line->fk_multicurrency = $this->fk_multicurrency;
2148 $this->line->multicurrency_code = $this->multicurrency_code;
2149 $this->line->multicurrency_subprice = $pu_ht_devise;
2150 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2151 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2152 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2153
2154 $this->line->subprice = $pu_ht;
2155 $this->line->price = $this->line->subprice;
2156
2157 $this->line->remise_percent = $remise_percent;
2158
2159 if (is_array($array_options) && count($array_options) > 0) {
2160 $this->line->array_options = $array_options;
2161 }
2162
2163 $result = $this->line->insert($notrigger);
2164 if ($result > 0) {
2165 // Reorder if child line
2166 if (!empty($this->line->fk_parent_line)) {
2167 $this->line_order(true, 'DESC');
2168 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2169 $linecount = count($this->lines);
2170 for ($ii = $rang; $ii <= $linecount; $ii++) {
2171 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2172 }
2173 }
2174
2175 // Mise a jour information denormalisees au niveau de la commande meme
2176 $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.
2177 if ($result > 0) {
2178 $this->db->commit();
2179 return $this->line->id;
2180 } else {
2181 $this->db->rollback();
2182 return -1;
2183 }
2184 } else {
2185 $this->error = $this->line->error;
2186 $this->errors = $this->line->errors;
2187 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2188 $this->db->rollback();
2189 return -1;
2190 }
2191 }
2192 return -1;
2193 }
2194
2195
2213 public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2214 {
2215 global $conf, $langs;
2216
2217 $error = 0;
2218 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2219
2220 // Check parameters (if test are wrong here, there is bug into caller)
2221 if ($entrepot <= 0) {
2222 $this->error = 'ErrorBadValueForParameterWarehouse';
2223 return -1;
2224 }
2225 if ($qty == 0) {
2226 $this->error = 'ErrorBadValueForParameterQty';
2227 return -1;
2228 }
2229
2230 $dispatchstatus = 1;
2231 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2232 $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2233 }
2234
2235 $now = dol_now();
2236
2237 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2238
2239 if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2240 $this->db->begin();
2241
2242 $sql = "INSERT INTO ".$this->db->prefix()."receptiondet_batch";
2243 $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2244 $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2245 $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");
2246 $sql .= ")";
2247
2248 dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2249 $resql = $this->db->query($sql);
2250 if ($resql) {
2251 if (!$notrigger) {
2252 global $conf, $langs, $user;
2253 // Call trigger
2254 $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2255 if ($result < 0) {
2256 $error++;
2257 }
2258 // End call triggers
2259 }
2260 } else {
2261 $this->error = $this->db->lasterror();
2262 $error++;
2263 }
2264
2265 // If module stock is enabled and the stock increase is done on purchase order dispatching
2266 if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2267 $mouv = new MouvementStock($this->db);
2268 if ($product > 0) {
2269 // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2270 $mouv->origin = &$this;
2271 $mouv->setOrigin($this->element, $this->id);
2272
2273 // Method change if qty < 0
2274 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2275 $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2276 } else {
2277 $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2278 }
2279
2280 if ($result < 0) {
2281 $this->error = $mouv->error;
2282 $this->errors = $mouv->errors;
2283 dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".implode(',', $this->errors), LOG_ERR);
2284 $error++;
2285 }
2286 }
2287 }
2288
2289 if ($error == 0) {
2290 $this->db->commit();
2291 return 1;
2292 } else {
2293 $this->db->rollback();
2294 return -1;
2295 }
2296 } else {
2297 $this->error = 'BadStatusForObject';
2298 return -2;
2299 }
2300 }
2301
2309 public function deleteLine($idline, $notrigger = 0)
2310 {
2311 global $user;
2312
2313 if ($this->statut == 0) {
2314 $line = new CommandeFournisseurLigne($this->db);
2315
2316 if ($line->fetch($idline) <= 0) {
2317 return 0;
2318 }
2319
2320 // check if not yet received
2321 $dispatchedLines = $this->getDispachedLines();
2322 foreach ($dispatchedLines as $dispatchLine) {
2323 if ($dispatchLine['orderlineid'] == $idline) {
2324 $this->error = "LineAlreadyDispatched";
2325 $this->errors[] = $this->error;
2326 return -3;
2327 }
2328 }
2329
2330 if ($line->delete($user, $notrigger) > 0) {
2331 $this->update_price(1);
2332 return 1;
2333 } else {
2334 $this->setErrorsFromObject($line);
2335 return -1;
2336 }
2337 } else {
2338 return -2;
2339 }
2340 }
2341
2349 public function delete(User $user, $notrigger = 0)
2350 {
2351 global $langs, $conf;
2352 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2353
2354 $error = 0;
2355
2356 $this->db->begin();
2357
2358 if (empty($notrigger)) {
2359 // Call trigger
2360 $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2361 if ($result < 0) {
2362 $this->errors[] = 'ErrorWhenRunningTrigger';
2363 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2364 $this->db->rollback();
2365 return -1;
2366 }
2367 // End call triggers
2368 }
2369
2370 // Test we can delete
2371 $this->fetchObjectLinked(null, 'order_supplier');
2372 if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2373 foreach ($this->linkedObjects['reception'] as $element) {
2374 if ($element->statut >= 0) {
2375 $this->errors[] = $langs->trans('ReceptionExist');
2376 $error++;
2377 break;
2378 }
2379 }
2380 }
2381
2382 $main = $this->db->prefix().'commande_fournisseurdet';
2383 $ef = $main."_extrafields";
2384 $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2385 dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2386 if (!$this->db->query($sql)) {
2387 $this->error = $this->db->lasterror();
2388 $this->errors[] = $this->db->lasterror();
2389 $error++;
2390 }
2391
2392 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2393 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2394 if (!$this->db->query($sql)) {
2395 $this->error = $this->db->lasterror();
2396 $this->errors[] = $this->db->lasterror();
2397 $error++;
2398 }
2399
2400 $sql = "DELETE FROM ".$this->db->prefix()."commande_fournisseur WHERE rowid =".((int) $this->id);
2401 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2402 if ($resql = $this->db->query($sql)) {
2403 if ($this->db->affected_rows($resql) < 1) {
2404 $this->error = $this->db->lasterror();
2405 $this->errors[] = $this->db->lasterror();
2406 $error++;
2407 }
2408 } else {
2409 $this->error = $this->db->lasterror();
2410 $this->errors[] = $this->db->lasterror();
2411 $error++;
2412 }
2413
2414 // Remove extrafields
2415 if (!$error) {
2416 $result = $this->deleteExtraFields();
2417 if ($result < 0) {
2418 $this->error = 'FailToDeleteExtraFields';
2419 $this->errors[] = 'FailToDeleteExtraFields';
2420 $error++;
2421 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2422 }
2423 }
2424
2425 // Delete linked object
2426 $res = $this->deleteObjectLinked();
2427 if ($res < 0) {
2428 $this->error = 'FailToDeleteObjectLinked';
2429 $this->errors[] = 'FailToDeleteObjectLinked';
2430 $error++;
2431 }
2432
2433 if (!$error) {
2434 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2435 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2436 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2437
2438 // We remove directory
2439 $ref = dol_sanitizeFileName($this->ref);
2440 if ($conf->fournisseur->commande->dir_output) {
2441 $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2442 $file = $dir."/".$ref.".pdf";
2443 if (file_exists($file)) {
2444 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2445 $this->error = 'ErrorFailToDeleteFile';
2446 $this->errors[] = 'ErrorFailToDeleteFile';
2447 $error++;
2448 }
2449 }
2450 if (file_exists($dir)) {
2451 $res = @dol_delete_dir_recursive($dir);
2452 if (!$res) {
2453 $this->error = 'ErrorFailToDeleteDir';
2454 $this->errors[] = 'ErrorFailToDeleteDir';
2455 $error++;
2456 }
2457 }
2458 }
2459 }
2460
2461 if (!$error) {
2462 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2463 $this->db->commit();
2464 return 1;
2465 } else {
2466 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2467 $this->db->rollback();
2468 return -$error;
2469 }
2470 }
2471
2472
2481 public function getDispachedLines($status = -1)
2482 {
2483 $ret = array();
2484
2485 // List of already dispatched lines
2486 $sql = "SELECT p.ref, p.label,";
2487 $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2488 $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2489 $sql .= " FROM ".$this->db->prefix()."product as p,";
2490 $sql .= " ".$this->db->prefix()."receptiondet_batch as cfd";
2491 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e ON cfd.fk_entrepot = e.rowid";
2492 $sql .= " WHERE cfd.fk_element = ".((int) $this->id);
2493 $sql .= " AND cfd.fk_product = p.rowid";
2494 if ($status >= 0) {
2495 $sql .= " AND cfd.status = ".((int) $status);
2496 }
2497 $sql .= " ORDER BY cfd.rowid ASC";
2498
2499 $resql = $this->db->query($sql);
2500 if ($resql) {
2501 $num = $this->db->num_rows($resql);
2502 $i = 0;
2503
2504 while ($i < $num) {
2505 $objp = $this->db->fetch_object($resql);
2506 if ($objp) {
2507 $ret[] = array(
2508 'id' => $objp->dispatchedlineid,
2509 'productid' => $objp->fk_product,
2510 'warehouseid' => $objp->warehouse_id,
2511 'qty' => $objp->qty,
2512 'orderlineid' => $objp->fk_elementdet
2513 );
2514 }
2515
2516 $i++;
2517 }
2518 } else {
2519 dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2520 }
2521
2522 return $ret;
2523 }
2524
2525 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2535 public function Livraison($user, $date, $type, $comment)
2536 {
2537 // phpcs:enable
2538 global $conf, $langs;
2539
2540 $result = 0;
2541 $error = 0;
2542
2543 dol_syslog(get_class($this)."::Livraison");
2544
2545 $usercanreceive = 0;
2546 if (!isModEnabled('reception')) {
2547 $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2548 } else {
2549 $usercanreceive = $user->hasRight("reception", "creer");
2550 }
2551
2552 if ($usercanreceive) {
2553 // Define the new status
2554 if ($type == 'par') {
2556 } elseif ($type == 'tot') {
2558 } elseif ($type == 'nev') {
2560 } elseif ($type == 'can') {
2562 } else {
2563 $error++;
2564 dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2565 return -2;
2566 }
2567
2568 // Some checks to accept the record
2569 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2570 // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2571 if (!$error && ($type == 'tot')) {
2572 $dispatchedlinearray = $this->getDispachedLines(0);
2573 if (count($dispatchedlinearray) > 0) {
2574 $result = -1;
2575 $error++;
2576 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2577 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2578 }
2579 }
2580 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)
2581 $dispatcheddenied = $this->getDispachedLines(2);
2582 if (count($dispatchedlinearray) > 0) {
2583 $result = -1;
2584 $error++;
2585 $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2586 dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2587 }
2588 }
2589 }
2590
2591 // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2592
2593 if (empty($error)) {
2594 $this->db->begin();
2595
2596 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2597 $sql .= " SET fk_statut = ".((int) $statut);
2598 $sql .= " WHERE rowid = ".((int) $this->id);
2599 $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2600
2601 dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2602 $resql = $this->db->query($sql);
2603 if ($resql) {
2604 $result = 1;
2605 $old_statut = $this->statut;
2606 $this->statut = $statut;
2607 $this->context['actionmsg2'] = $comment;
2608
2609 // Call trigger
2610 $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2611 if ($result_trigger < 0) {
2612 $error++;
2613 }
2614 // End call triggers
2615
2616 if (empty($error)) {
2617 $this->db->commit();
2618 } else {
2619 $this->statut = $old_statut;
2620 $this->db->rollback();
2621 $this->error = $this->db->lasterror();
2622 $result = -1;
2623 }
2624 } else {
2625 $this->db->rollback();
2626 $this->error = $this->db->lasterror();
2627 $result = -1;
2628 }
2629 }
2630 } else {
2631 $this->error = $langs->trans('NotAuthorized');
2632 $this->errors[] = $langs->trans('NotAuthorized');
2633 dol_syslog(get_class($this)."::Livraison Not Authorized");
2634 $result = -3;
2635 }
2636 return $result;
2637 }
2638
2639 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2649 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2650 {
2651 // phpcs:enable
2652 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2653 }
2654
2663 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2664 {
2665 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2666 $error = 0;
2667
2668 $this->db->begin();
2669
2670 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2671 $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2672 $sql .= " WHERE rowid = ".((int) $this->id);
2673
2674 dol_syslog(__METHOD__, LOG_DEBUG);
2675 $resql = $this->db->query($sql);
2676 if (!$resql) {
2677 $this->errors[] = $this->db->error();
2678 $error++;
2679 }
2680
2681 if (!$error) {
2682 $this->oldcopy = clone $this;
2683 $this->delivery_date = $delivery_date;
2684 }
2685
2686 if (!$notrigger && empty($error)) {
2687 // Call trigger
2688 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2689 if ($result < 0) {
2690 $error++;
2691 }
2692 // End call triggers
2693 }
2694
2695 if (!$error) {
2696 $this->db->commit();
2697 return 1;
2698 } else {
2699 foreach ($this->errors as $errmsg) {
2700 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2701 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2702 }
2703 $this->db->rollback();
2704 return -1 * $error;
2705 }
2706 } else {
2707 return -2;
2708 }
2709 }
2710
2711 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2720 public function set_id_projet($user, $id_projet, $notrigger = 0)
2721 {
2722 // phpcs:enable
2723 if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2724 $error = 0;
2725
2726 $this->db->begin();
2727
2728 $sql = "UPDATE ".$this->db->prefix()."commande_fournisseur";
2729 $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2730 $sql .= " WHERE rowid = ".((int) $this->id);
2731
2732 dol_syslog(__METHOD__, LOG_DEBUG);
2733 $resql = $this->db->query($sql);
2734 if (!$resql) {
2735 $this->errors[] = $this->db->error();
2736 $error++;
2737 }
2738
2739 if (!$error) {
2740 $this->oldcopy = clone $this;
2741 $this->fk_projet = $id_projet;
2742 $this->fk_project = $id_projet;
2743 }
2744
2745 if (!$notrigger && empty($error)) {
2746 // Call trigger
2747 $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2748 if ($result < 0) {
2749 $error++;
2750 }
2751 // End call triggers
2752 }
2753
2754 if (!$error) {
2755 $this->db->commit();
2756 return 1;
2757 } else {
2758 foreach ($this->errors as $errmsg) {
2759 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2760 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2761 }
2762 $this->db->rollback();
2763 return -1 * $error;
2764 }
2765 } else {
2766 return -2;
2767 }
2768 }
2769
2778 public function updateFromCommandeClient($user, $idc, $comclientid)
2779 {
2780 $comclient = new Commande($this->db);
2781 $comclient->fetch($comclientid);
2782
2783 $this->id = $idc;
2784
2785 $this->lines = array();
2786
2787 $num = count($comclient->lines);
2788 for ($i = 0; $i < $num; $i++) {
2789 $prod = new Product($this->db);
2790 $label = '';
2791 $ref = '';
2792 if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2793 $label = $prod->label;
2794 $ref = $prod->ref;
2795 }
2796
2797 $sql = "INSERT INTO ".$this->db->prefix()."commande_fournisseurdet";
2798 $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2799 $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2800 $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2801 $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);
2802 $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2803 if ($this->db->query($sql)) {
2804 $this->update_price(1);
2805 }
2806 }
2807
2808 return 1;
2809 }
2810
2818 public function setStatus($user, $status)
2819 {
2820 global $conf, $langs;
2821 $error = 0;
2822
2823 $this->db->begin();
2824
2825 $sql = 'UPDATE '.$this->db->prefix().'commande_fournisseur';
2826 $sql .= " SET fk_statut = ".$status;
2827 $sql .= " WHERE rowid = ".((int) $this->id);
2828
2829 dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2830 $resql = $this->db->query($sql);
2831 if ($resql) {
2832 // Trigger names for each status
2833 $triggerName = array();
2834 $triggerName[0] = 'DRAFT';
2835 $triggerName[1] = 'VALIDATED';
2836 $triggerName[2] = 'APPROVED';
2837 $triggerName[3] = 'ORDERED'; // Ordered
2838 $triggerName[4] = 'RECEIVED_PARTIALLY';
2839 $triggerName[5] = 'RECEIVED_COMPLETELY';
2840 $triggerName[6] = 'CANCELED';
2841 $triggerName[7] = 'CANCELED';
2842 $triggerName[9] = 'REFUSED';
2843
2844 // Call trigger
2845 $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2846 if ($result < 0) {
2847 $error++;
2848 }
2849 // End call triggers
2850 } else {
2851 $error++;
2852 $this->error = $this->db->lasterror();
2853 dol_syslog(get_class($this)."::setStatus ".$this->error);
2854 }
2855
2856 if (!$error) {
2857 $this->statut = $status;
2858 $this->db->commit();
2859 return 1;
2860 } else {
2861 $this->db->rollback();
2862 return -1;
2863 }
2864 }
2865
2889 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 = 0, $date_end = 0, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
2890 {
2891 global $mysoc, $conf, $langs;
2892 dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2893 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2894
2895 $error = 0;
2896
2897 if ($this->statut == self::STATUS_DRAFT) {
2898 // Clean parameters
2899 if (empty($qty)) {
2900 $qty = 0;
2901 }
2902 if (empty($info_bits)) {
2903 $info_bits = 0;
2904 }
2905 if (empty($txtva)) {
2906 $txtva = 0;
2907 }
2908 if (empty($txlocaltax1)) {
2909 $txlocaltax1 = 0;
2910 }
2911 if (empty($txlocaltax2)) {
2912 $txlocaltax2 = 0;
2913 }
2914 if (empty($remise_percent)) {
2915 $remise_percent = 0;
2916 }
2917
2918 $remise_percent = (float) price2num($remise_percent);
2919 $qty = price2num($qty);
2920 if (!$qty) {
2921 $qty = 1;
2922 }
2923 $pu = price2num($pu);
2924 $pu_ht_devise = price2num($pu_ht_devise);
2925 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2926 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2927 }
2928 $txlocaltax1 = (float) price2num($txlocaltax1);
2929 $txlocaltax2 = (float) price2num($txlocaltax2);
2930
2931 // Check parameters
2932 if ($type < 0) {
2933 return -1;
2934 }
2935 if ($date_start && $date_end && $date_start > $date_end) {
2936 $langs->load("errors");
2937 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2938 return -1;
2939 }
2940
2941 $this->db->begin();
2942
2943 // Calcul du total TTC et de la TVA pour la ligne a partir de
2944 // qty, pu, remise_percent et txtva
2945 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2946 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2947
2948 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2949
2950 // Clean vat code
2951 $reg = array();
2952 $vat_src_code = '';
2953 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2954 $vat_src_code = $reg[1];
2955 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2956 }
2957
2958 $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);
2959 $total_ht = $tabprice[0];
2960 $total_tva = $tabprice[1];
2961 $total_ttc = $tabprice[2];
2962 $total_localtax1 = $tabprice[9];
2963 $total_localtax2 = $tabprice[10];
2964 $pu_ht = $tabprice[3];
2965 $pu_tva = $tabprice[4];
2966 $pu_ttc = $tabprice[5];
2967
2968 // MultiCurrency
2969 $multicurrency_total_ht = $tabprice[16];
2970 $multicurrency_total_tva = $tabprice[17];
2971 $multicurrency_total_ttc = $tabprice[18];
2972 $pu_ht_devise = $tabprice[19];
2973
2974 $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2975 $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2976
2977 //Fetch current line from the database and then clone the object and set it in $oldline property
2978 $this->line = new CommandeFournisseurLigne($this->db);
2979 $this->line->fetch($rowid);
2980
2981 $oldline = clone $this->line;
2982 $this->line->oldline = $oldline;
2983
2984 $this->line->context = $this->context;
2985
2986 $this->line->fk_commande = $this->id;
2987 //$this->line->label=$label;
2988 $this->line->desc = $desc;
2989
2990 // redefine quantity according to packaging
2991 if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2992 if ($qty < $this->line->packaging) {
2993 $qty = $this->line->packaging;
2994 } else {
2995 if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2996 $coeff = intval($qty / $this->line->packaging) + 1;
2997 $qty = $this->line->packaging * $coeff;
2998 setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2999 }
3000 }
3001 }
3002
3003 $this->line->qty = $qty;
3004 $this->line->ref_supplier = $ref_supplier;
3005
3006 $this->line->vat_src_code = $vat_src_code;
3007 $this->line->tva_tx = $txtva;
3008 $this->line->localtax1_tx = $txlocaltax1;
3009 $this->line->localtax2_tx = $txlocaltax2;
3010 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3011 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3012 $this->line->remise_percent = $remise_percent;
3013 $this->line->subprice = $pu_ht;
3014 $this->line->info_bits = $info_bits;
3015 $this->line->total_ht = $total_ht;
3016 $this->line->total_tva = $total_tva;
3017 $this->line->total_localtax1 = $total_localtax1;
3018 $this->line->total_localtax2 = $total_localtax2;
3019 $this->line->total_ttc = $total_ttc;
3020 $this->line->product_type = $type;
3021 $this->line->special_code = $oldline->special_code;
3022 $this->line->rang = $oldline->rang;
3023 $this->line->origin = $this->origin;
3024 $this->line->fk_unit = $fk_unit;
3025
3026 $this->line->date_start = $date_start;
3027 $this->line->date_end = $date_end;
3028
3029 // Multicurrency
3030 $this->line->fk_multicurrency = $this->fk_multicurrency;
3031 $this->line->multicurrency_code = $this->multicurrency_code;
3032 $this->line->multicurrency_subprice = $pu_ht_devise;
3033 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
3034 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
3035 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
3036
3037 $this->line->subprice = $pu_ht;
3038 $this->line->price = $this->line->subprice;
3039
3040 $this->line->remise_percent = $remise_percent;
3041
3042 if (is_array($array_options) && count($array_options) > 0) {
3043 // We replace values in this->line->array_options only for entries defined into $array_options
3044 foreach ($array_options as $key => $value) {
3045 $this->line->array_options[$key] = $array_options[$key];
3046 }
3047 }
3048
3049 $result = $this->line->update($notrigger);
3050
3051
3052 // Mise a jour info denormalisees au niveau facture
3053 if ($result >= 0) {
3054 $this->update_price('1', 'auto');
3055 $this->db->commit();
3056 return $result;
3057 } else {
3058 $this->error = $this->db->lasterror();
3059 $this->db->rollback();
3060 return -1;
3061 }
3062 } else {
3063 $this->error = "Order status makes operation forbidden";
3064 dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3065 return -2;
3066 }
3067 }
3068
3069
3077 public function initAsSpecimen()
3078 {
3079 global $user, $langs, $conf;
3080
3081 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3082
3083 dol_syslog(get_class($this)."::initAsSpecimen");
3084
3085 $now = dol_now();
3086
3087 // Find first product
3088 $prodid = 0;
3089 $product = new ProductFournisseur($this->db);
3090 $sql = "SELECT rowid";
3091 $sql .= " FROM ".$this->db->prefix()."product";
3092 $sql .= " WHERE entity IN (".getEntity('product').")";
3093 $sql .= $this->db->order("rowid", "ASC");
3094 $sql .= $this->db->plimit(1);
3095 $resql = $this->db->query($sql);
3096 if ($resql) {
3097 $obj = $this->db->fetch_object($resql);
3098 $prodid = $obj->rowid;
3099 }
3100
3101 // Initialise parameters
3102 $this->id = 0;
3103 $this->ref = 'SPECIMEN';
3104 $this->specimen = 1;
3105 $this->socid = 1;
3106 $this->date = $now;
3107 $this->date_commande = $now;
3108 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3109 $this->cond_reglement_code = 'RECEP';
3110 $this->mode_reglement_code = 'CHQ';
3111
3112 $this->note_public = 'This is a comment (public)';
3113 $this->note_private = 'This is a comment (private)';
3114
3115 $this->multicurrency_tx = 1;
3116 $this->multicurrency_code = $conf->currency;
3117
3118 $this->statut = 0;
3119
3120 // Lines
3121 $nbp = 5;
3122 $xnbp = 0;
3123 while ($xnbp < $nbp) {
3124 $line = new CommandeFournisseurLigne($this->db);
3125 $line->desc = $langs->trans("Description")." ".$xnbp;
3126 $line->qty = 1;
3127 $line->subprice = 100;
3128 $line->tva_tx = 19.6;
3129 $line->localtax1_tx = 0;
3130 $line->localtax2_tx = 0;
3131 if ($xnbp == 2) {
3132 $line->total_ht = 50;
3133 $line->total_ttc = 59.8;
3134 $line->total_tva = 9.8;
3135 $line->remise_percent = 50;
3136 } else {
3137 $line->total_ht = 100;
3138 $line->total_ttc = 119.6;
3139 $line->total_tva = 19.6;
3140 $line->remise_percent = 00;
3141 }
3142 $line->fk_product = $prodid;
3143
3144 $this->lines[$xnbp] = $line;
3145
3146 $this->total_ht += $line->total_ht;
3147 $this->total_tva += $line->total_tva;
3148 $this->total_ttc += $line->total_ttc;
3149
3150 $xnbp++;
3151 }
3152
3153 return 1;
3154 }
3155
3162 public function info($id)
3163 {
3164 $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3165 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3166 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c';
3167 $sql .= ' WHERE c.rowid = '.((int) $id);
3168
3169 $result = $this->db->query($sql);
3170 if ($result) {
3171 if ($this->db->num_rows($result)) {
3172 $obj = $this->db->fetch_object($result);
3173
3174 $this->id = $obj->rowid;
3175
3176 $this->user_creation_id = $obj->fk_user_author;
3177 $this->user_validation_id = $obj->fk_user_valid;
3178 $this->user_modification_id = $obj->fk_user_modif;
3179 $this->user_approve_id = $obj->fk_user_approve;
3180 $this->user_approve_id2 = $obj->fk_user_approve2;
3181
3182 $this->date_creation = $this->db->jdate($obj->datec);
3183 $this->date_modification = $this->db->jdate($obj->datem);
3184 $this->date_approve = $this->db->jdate($obj->datea);
3185 $this->date_approve2 = $this->db->jdate($obj->datea2);
3186 $this->date_validation = $this->db->jdate($obj->date_validation);
3187 }
3188 $this->db->free($result);
3189 } else {
3190 dol_print_error($this->db);
3191 }
3192 }
3193
3199 public function loadStateBoard()
3200 {
3201 global $conf, $user;
3202
3203 $this->nb = array();
3204 $clause = "WHERE";
3205
3206 $sql = "SELECT count(co.rowid) as nb";
3207 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as co";
3208 $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON co.fk_soc = s.rowid";
3209 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3210 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3211 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3212 $clause = "AND";
3213 }
3214 $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3215
3216 $resql = $this->db->query($sql);
3217 if ($resql) {
3218 while ($obj = $this->db->fetch_object($resql)) {
3219 $this->nb["supplier_orders"] = $obj->nb;
3220 }
3221 $this->db->free($resql);
3222 return 1;
3223 } else {
3224 dol_print_error($this->db);
3225 $this->error = $this->db->error();
3226 return -1;
3227 }
3228 }
3229
3230 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3238 public function load_board($user, $mode = 'opened')
3239 {
3240 // phpcs:enable
3241 global $conf, $langs;
3242
3243 $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3244 $sql .= " FROM ".$this->db->prefix()."commande_fournisseur as c";
3245 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3246 $sql .= " JOIN ".$this->db->prefix()."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3247 }
3248 $sql .= " WHERE c.entity = ".$conf->entity;
3249 if ($mode === 'awaiting') {
3250 $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3251 } else {
3252 $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3253 }
3254 if ($user->socid) {
3255 $sql .= " AND c.fk_soc = ".((int) $user->socid);
3256 }
3257
3258 $resql = $this->db->query($sql);
3259 if ($resql) {
3260 $commandestatic = new CommandeFournisseur($this->db);
3261
3262 $response = new WorkboardResponse();
3263 $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3264 $response->label = $langs->trans("SuppliersOrdersToProcess");
3265 $response->labelShort = $langs->trans("Opened");
3266 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3267 $response->img = img_object('', "order");
3268
3269 if ($mode === 'awaiting') {
3270 $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3271 $response->labelShort = $langs->trans("AwaitingReception");
3272 $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3273 }
3274
3275 while ($obj = $this->db->fetch_object($resql)) {
3276 $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3277 $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3278 $commandestatic->statut = $obj->fk_statut;
3279
3280 $response->nbtodo++;
3281 $response->total += $obj->total_ht;
3282
3283 if ($commandestatic->hasDelay()) {
3284 $response->nbtodolate++;
3285 }
3286 }
3287
3288 return $response;
3289 } else {
3290 $this->error = $this->db->error();
3291 return -1;
3292 }
3293 }
3294
3301 public function getInputMethod()
3302 {
3303 global $langs;
3304
3305 if ($this->methode_commande_id > 0) {
3306 $sql = "SELECT rowid, code, libelle as label";
3307 $sql .= " FROM ".$this->db->prefix().'c_input_method';
3308 $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3309
3310 $resql = $this->db->query($sql);
3311 if ($resql) {
3312 if ($this->db->num_rows($resql)) {
3313 $obj = $this->db->fetch_object($resql);
3314
3315 $string = $langs->trans($obj->code);
3316 if ($string == $obj->code) {
3317 $string = $obj->label != '-' ? $obj->label : '';
3318 }
3319 return $string;
3320 }
3321 } else {
3322 dol_print_error($this->db);
3323 }
3324 }
3325
3326 return '';
3327 }
3328
3340 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3341 {
3342 global $conf, $langs;
3343
3344 if (!dol_strlen($modele)) {
3345 $modele = ''; // No doc template/generation by default
3346
3347 if (!empty($this->model_pdf)) {
3348 $modele = $this->model_pdf;
3349 } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3350 $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3351 }
3352 }
3353
3354 if (empty($modele)) {
3355 return 0;
3356 } else {
3357 $langs->load("suppliers");
3358 $outputlangs->load("products");
3359
3360 $modelpath = "core/modules/supplier_order/doc/";
3361 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3362 return $result;
3363 }
3364 }
3365
3372 public function getMaxDeliveryTimeDay($langs)
3373 {
3374 if (empty($this->lines)) {
3375 return '';
3376 }
3377
3378 $tmpproductfourn = new ProductFournisseur($this->db);
3379
3380 $nb = 0;
3381 foreach ($this->lines as $line) {
3382 if ($line->fk_product > 0) {
3383 // Load delivery_time_days, return id into product_fournisseur_price
3384 $idp = $tmpproductfourn->find_min_price_product_fournisseur($line->fk_product, $line->qty, $this->thirdparty->id);
3385 if ($idp > 0) {
3386 //$tmpproductfourn->fetch_product_fournisseur_price($idp);
3387 if ($tmpproductfourn->delivery_time_days > $nb) {
3388 $nb = $tmpproductfourn->delivery_time_days;
3389 }
3390 }
3391 }
3392 }
3393
3394 if ($nb === 0) {
3395 return '';
3396 } else {
3397 return $nb.' '.$langs->trans('days');
3398 }
3399 }
3400
3405 public function getRights()
3406 {
3407 global $user;
3408
3409 return $user->hasRight("fournisseur", "commande");
3410 }
3411
3412
3421 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3422 {
3423 $tables = array(
3424 'commande_fournisseur'
3425 );
3426
3427 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3428 }
3429
3438 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3439 {
3440 $tables = array(
3441 'commande_fournisseurdet'
3442 );
3443
3444 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3445 }
3446
3454 public function hasDelay()
3455 {
3456 global $conf;
3457
3458 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3459 $now = dol_now();
3460 if (!empty($this->delivery_date)) {
3461 $date_to_test = $this->delivery_date;
3462 return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3463 } else {
3464 //$date_to_test = $this->date_commande;
3465 //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3466 return false;
3467 }
3468 } else {
3469 $now = dol_now();
3470 $date_to_test = $this->date_commande;
3471
3472 return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3473 }
3474 }
3475
3483 public function showDelay()
3484 {
3485 global $conf, $langs;
3486
3487 $langs->load('orders');
3488
3489 $text = '';
3490
3491 if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3492 if (!empty($this->delivery_date)) {
3493 $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3494 } else {
3495 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3496 }
3497 } else {
3498 $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3499 }
3500 if ($text) {
3501 $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3502 }
3503
3504 return $text;
3505 }
3506
3507
3516 public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3517 {
3518 if (isModEnabled("supplier_order")) {
3519 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3520
3521 $qtydelivered = array();
3522 $qtywished = array();
3523
3524 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3525
3526 $filter = array('t.fk_element' => $this->id);
3527 if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3528 $filter['t.status'] = 1; // Restrict to lines with status validated
3529 }
3530
3531 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3532 if ($ret < 0) {
3533 $this->error = $supplierorderdispatch->error;
3534 $this->errors = $supplierorderdispatch->errors;
3535 return $ret;
3536 } else {
3537 if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3538 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3539 $date_liv = dol_now();
3540
3541 // Build array with quantity deliverd by product
3542 foreach ($supplierorderdispatch->lines as $line) {
3543 $qtydelivered[$line->fk_product] += $line->qty;
3544 }
3545 foreach ($this->lines as $line) {
3546 // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3547 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3548 continue;
3549 }
3550 $qtywished[$line->fk_product] += $line->qty;
3551 }
3552
3553 //Compare array
3554 $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3555 $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3556 $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3557 //var_dump(array_keys($qtydelivered));
3558 //var_dump(array_keys($qtywished));
3559 //var_dump($diff_array);
3560 //var_dump($keysinwishednotindelivered);
3561 //var_dump($keysindeliverednotinwished);
3562 //exit;
3563
3564 if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3565 if ($closeopenorder) {
3566 //$ret=$this->setStatus($user,5);
3567 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3568 if ($ret < 0) {
3569 return -1;
3570 }
3571 return 5;
3572 } else {
3573 //Diff => received partially
3574 //$ret=$this->setStatus($user,4);
3575 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3576 if ($ret < 0) {
3577 return -1;
3578 }
3579 return 4;
3580 }
3581 } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3582 //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3583
3584 $close = 0;
3585
3586 if (count($diff_array) > 0) {
3587 //there are some difference between the two arrays
3588
3589 //scan the array of results
3590 foreach ($diff_array as $key => $value) {
3591 //if the quantity delivered is greater or equal to wish quantity
3592 if ($qtydelivered[$key] >= $qtywished[$key]) {
3593 $close++;
3594 }
3595 }
3596 }
3597
3598
3599 if ($close == count($diff_array)) {
3600 //all the products are received equal or more than the wished quantity
3601 if ($closeopenorder) {
3602 $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3603 if ($ret < 0) {
3604 return -1;
3605 }
3606 return 5;
3607 } else {
3608 //Diff => received partially
3609 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3610 if ($ret < 0) {
3611 return -1;
3612 }
3613 return 4;
3614 }
3615 } else {
3616 //all the products are not received
3617 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3618 if ($ret < 0) {
3619 return -1;
3620 }
3621 return 4;
3622 }
3623 } else {
3624 //Diff => received partially
3625 $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3626 if ($ret < 0) {
3627 return -1;
3628 }
3629 return 4;
3630 }
3631 }
3632 return 1;
3633 }
3634 }
3635 return 0;
3636 }
3637
3645 public function loadReceptions($filtre_statut = -1)
3646 {
3647 $this->receptions = array();
3648
3649 dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3650
3651 $sql = 'SELECT cd.rowid, cd.fk_product,';
3652 $sql .= ' sum(cfd.qty) as qty';
3653 $sql .= ' FROM '.$this->db->prefix().'receptiondet_batch as cfd,';
3654 if ($filtre_statut >= 0) {
3655 $sql .= ' '.$this->db->prefix().'reception as e,';
3656 }
3657 $sql .= ' '.$this->db->prefix().'commande_fournisseurdet as cd';
3658 $sql .= ' WHERE';
3659 if ($filtre_statut >= 0) {
3660 $sql .= ' cfd.fk_reception = e.rowid AND';
3661 }
3662 $sql .= ' cfd.fk_elementdet = cd.rowid';
3663 $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3664 if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3665 $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3666 }
3667 if ($filtre_statut >= 0) {
3668 $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3669 }
3670 $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3671
3672 $resql = $this->db->query($sql);
3673 if ($resql) {
3674 $num = $this->db->num_rows($resql);
3675 $i = 0;
3676 while ($i < $num) {
3677 $obj = $this->db->fetch_object($resql);
3678 empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3679 $i++;
3680 }
3681 $this->db->free($resql);
3682
3683 return $num;
3684 } else {
3685 $this->error = $this->db->lasterror();
3686 return -1;
3687 }
3688 }
3689
3697 public function getKanbanView($option = '', $arraydata = null)
3698 {
3699 global $langs;
3700
3701 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3702
3703 $return = '<div class="box-flex-item box-flex-grow-zero">';
3704 $return .= '<div class="info-box info-box-sm">';
3705 $return .= '<span class="info-box-icon bg-infobox-action">';
3706 $return .= img_picto('', $this->picto);
3707 $return .= '</span>';
3708 $return .= '<div class="info-box-content">';
3709 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3710 if ($selected >= 0) {
3711 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3712 }
3713 if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3714 $return .= '<br><span class="info-box-label amount">'.$this->socid.'</span>';
3715 }
3716 if (property_exists($this, 'billed')) {
3717 $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3718 }
3719 if (method_exists($this, 'getLibStatut')) {
3720 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3721 }
3722 $return .= '</div>';
3723 $return .= '</div>';
3724 $return .= '</div>';
3725 return $return;
3726 }
3727}
3728
3729
3730
3735{
3739 public $element = 'commande_fournisseurdet';
3740
3744 public $table_element = 'commande_fournisseurdet';
3745
3749 public $parent_element = 'commande_fournisseur';
3750
3754 public $fk_parent_attribute = 'fk_commande_fournisseur';
3755
3756 public $oldline;
3757
3762 public $fk_commande;
3763
3764 // From llx_commande_fournisseurdet
3768 public $fk_parent_line;
3769
3773 public $fk_facture;
3774
3775 public $rang = 0;
3776
3780 public $special_code = 0;
3781
3786 public $pu_ht;
3787
3788 public $date_start;
3789 public $date_end;
3790 public $fk_fournprice;
3791 public $packaging;
3792 public $pa_ht;
3793
3794 // From llx_product_fournisseur_price
3795
3800 public $ref_supplier;
3801
3807 public $ref_fourn;
3808
3809 public $remise;
3810
3811
3817 public function __construct($db)
3818 {
3819 $this->db = $db;
3820 }
3821
3828 public function fetch($rowid)
3829 {
3830 $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3831 $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3832 $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3833 $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3834 $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3835 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3836 $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3837 $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3838 $sql .= ' c.fk_soc as socid';
3839 $sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
3840 $sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';
3841 $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3842
3843 $result = $this->db->query($sql);
3844 if ($result) {
3845 $objp = $this->db->fetch_object($result);
3846
3847 if (!empty($objp)) {
3848 $this->rowid = $objp->rowid;
3849 $this->id = $objp->rowid;
3850 $this->fk_commande = $objp->fk_commande;
3851 $this->desc = $objp->description;
3852 $this->qty = $objp->qty;
3853 $this->ref_fourn = $objp->ref_supplier;
3854 $this->ref_supplier = $objp->ref_supplier;
3855 $this->subprice = $objp->subprice;
3856 $this->tva_tx = $objp->tva_tx;
3857 $this->localtax1_tx = $objp->localtax1_tx;
3858 $this->localtax2_tx = $objp->localtax2_tx;
3859 $this->localtax1_type = $objp->localtax1_type;
3860 $this->localtax2_type = $objp->localtax2_type;
3861 $this->remise = $objp->remise;
3862 $this->remise_percent = $objp->remise_percent;
3863 $this->fk_product = $objp->fk_product;
3864 $this->info_bits = $objp->info_bits;
3865 $this->total_ht = $objp->total_ht;
3866 $this->total_tva = $objp->total_tva;
3867 $this->total_localtax1 = $objp->total_localtax1;
3868 $this->total_localtax2 = $objp->total_localtax2;
3869 $this->total_ttc = $objp->total_ttc;
3870 $this->product_type = $objp->product_type;
3871 $this->special_code = $objp->special_code;
3872
3873 $this->ref = $objp->product_ref;
3874
3875 $this->product_ref = $objp->product_ref;
3876 $this->product_label = $objp->product_label;
3877 $this->product_desc = $objp->product_desc;
3878
3879 if (getDolGlobalInt('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3880 // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3881 // Move this into another method and call it when required.
3882
3883 // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3884 $sqlsearchpackage = 'SELECT rowid, packaging FROM '.$this->db->prefix()."product_fournisseur_price";
3885 $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3886 $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3887 $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3888 $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3889 $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3890 $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3891 $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3892 $sqlsearchpackage .= " LIMIT 1";
3893
3894 $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3895 if ($resqlsearchpackage) {
3896 $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3897 if ($objsearchpackage) {
3898 $this->fk_fournprice = $objsearchpackage->rowid;
3899 $this->packaging = $objsearchpackage->packaging;
3900 }
3901 } else {
3902 $this->error = $this->db->lasterror();
3903 return -1;
3904 }
3905 }
3906
3907 $this->date_start = $this->db->jdate($objp->date_start);
3908 $this->date_end = $this->db->jdate($objp->date_end);
3909 $this->fk_unit = $objp->fk_unit;
3910
3911 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3912 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3913 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3914 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3915
3916 $this->fetch_optionals();
3917
3918 $this->db->free($result);
3919 return 1;
3920 } else {
3921 $this->error = 'Supplier order line with id='.$rowid.' not found';
3922 dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3923 return 0;
3924 }
3925 } else {
3926 dol_print_error($this->db);
3927 return -1;
3928 }
3929 }
3930
3937 public function insert($notrigger = 0)
3938 {
3939 global $conf, $user;
3940
3941 $error = 0;
3942
3943 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3944
3945 // Clean parameters
3946 if (empty($this->tva_tx)) {
3947 $this->tva_tx = 0;
3948 }
3949 if (empty($this->localtax1_tx)) {
3950 $this->localtax1_tx = 0;
3951 }
3952 if (empty($this->localtax2_tx)) {
3953 $this->localtax2_tx = 0;
3954 }
3955 if (empty($this->localtax1_type)) {
3956 $this->localtax1_type = '0';
3957 }
3958 if (empty($this->localtax2_type)) {
3959 $this->localtax2_type = '0';
3960 }
3961 if (empty($this->total_localtax1)) {
3962 $this->total_localtax1 = 0;
3963 }
3964 if (empty($this->total_localtax2)) {
3965 $this->total_localtax2 = 0;
3966 }
3967 if (empty($this->rang)) {
3968 $this->rang = 0;
3969 }
3970 if (empty($this->remise_percent)) {
3971 $this->remise_percent = 0;
3972 }
3973 if (empty($this->info_bits)) {
3974 $this->info_bits = 0;
3975 }
3976 if (empty($this->special_code)) {
3977 $this->special_code = 0;
3978 }
3979 if (empty($this->fk_parent_line)) {
3980 $this->fk_parent_line = 0;
3981 }
3982 if (empty($this->pa_ht)) {
3983 $this->pa_ht = 0;
3984 }
3985
3986 // Multicurrency
3987 if (!empty($this->multicurrency_code)) {
3988 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3989 }
3990 if (empty($this->fk_multicurrency)) {
3991 $this->multicurrency_code = $conf->currency;
3992 $this->fk_multicurrency = 0;
3993 $this->multicurrency_tx = 1;
3994 }
3995
3996 // Check parameters
3997 if ($this->product_type < 0) {
3998 return -1;
3999 }
4000
4001 $this->db->begin();
4002
4003 // Insertion dans base de la ligne
4004 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element;
4005 $sql .= " (fk_commande, label, description, date_start, date_end,";
4006 $sql .= " fk_product, product_type, special_code, rang,";
4007 $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
4008 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
4009 $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,";
4010 $sql .= " fk_parent_line)";
4011 $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
4012 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4013 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4014 if ($this->fk_product) {
4015 $sql .= $this->fk_product.",";
4016 } else {
4017 $sql .= "null,";
4018 }
4019 $sql .= "'".$this->db->escape($this->product_type)."',";
4020 $sql .= (int) $this->special_code . ",";
4021 $sql .= "'".$this->db->escape($this->rang)."',";
4022 $sql .= "'".$this->db->escape($this->qty)."', ";
4023 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4024 $sql .= " ".price2num($this->tva_tx).", ";
4025 $sql .= " ".price2num($this->localtax1_tx).",";
4026 $sql .= " ".price2num($this->localtax2_tx).",";
4027 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4028 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4029 $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
4030 $sql .= " ".price2num($this->total_ht).",";
4031 $sql .= " ".price2num($this->total_tva).",";
4032 $sql .= " ".price2num($this->total_localtax1).",";
4033 $sql .= " ".price2num($this->total_localtax2).",";
4034 $sql .= " ".price2num($this->total_ttc).",";
4035 $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
4036 $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
4037 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4038 $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
4039 $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
4040 $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
4041 $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
4042 $sql .= ", ".((!empty($this->fk_parent_line) && $this->fk_parent_line > 0) ? $this->fk_parent_line : 'null');
4043 $sql .= ")";
4044
4045 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4046 $resql = $this->db->query($sql);
4047 if ($resql) {
4048 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
4049 $this->rowid = $this->id;
4050
4051 if (!$error) {
4052 $result = $this->insertExtraFields();
4053 if ($result < 0) {
4054 $error++;
4055 }
4056 }
4057
4058 if (!$error && !$notrigger) {
4059 // Call trigger
4060 $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
4061 if ($result < 0) {
4062 $error++;
4063 }
4064 // End call triggers
4065 }
4066
4067 if (!$error) {
4068 $this->db->commit();
4069 return 1;
4070 }
4071
4072 foreach ($this->errors as $errmsg) {
4073 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4074 $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4075 }
4076 $this->db->rollback();
4077 return -1 * $error;
4078 } else {
4079 $this->errors[] = $this->db->error();
4080 $this->db->rollback();
4081 return -2;
4082 }
4083 }
4090 public function update($notrigger = 0)
4091 {
4092 global $user;
4093
4094 $error = 0;
4095
4096 $this->db->begin();
4097
4098 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET";
4099 $sql .= " description='".$this->db->escape($this->desc)."'";
4100 $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4101 $sql .= ", subprice='".price2num($this->subprice)."'";
4102 //$sql.= ",remise='".price2num($remise)."'";
4103 $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4104
4105 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4106 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4107 $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4108 $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4109 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4110 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4111 $sql .= ", qty='".price2num($this->qty)."'";
4112 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4113 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4114 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4115 $sql .= ", total_ht='".price2num($this->total_ht)."'";
4116 $sql .= ", total_tva='".price2num($this->total_tva)."'";
4117 $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4118 $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4119 $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4120 $sql .= ", product_type=".$this->product_type;
4121 $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4122 $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4123
4124 // Multicurrency
4125 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4126 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4127 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4128 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4129
4130 $sql .= " WHERE rowid = ".((int) $this->id);
4131
4132 dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4133 $resql = $this->db->query($sql);
4134 if ($resql) {
4135 if (!$error) {
4136 $result = $this->insertExtraFields();
4137 if ($result < 0) {
4138 $error++;
4139 }
4140 }
4141
4142 if (!$error && !$notrigger) {
4143 // Call trigger
4144 $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4145 if ($result < 0) {
4146 $this->db->rollback();
4147 return -1;
4148 }
4149 // End call triggers
4150 }
4151
4152 if (!$error) {
4153 $this->db->commit();
4154 return 1;
4155 } else {
4156 $this->db->rollback();
4157 return -1;
4158 }
4159 } else {
4160 $this->error = $this->db->lasterror();
4161 $this->db->rollback();
4162 return -1;
4163 }
4164 }
4165
4173 public function delete($user, $notrigger = 0)
4174 {
4175 if (empty($user)) {
4176 global $user;
4177 }
4178
4179 $error = 0;
4180
4181 $this->db->begin();
4182
4183 // extrafields
4184 $result = $this->deleteExtraFields();
4185 if ($result < 0) {
4186 $this->db->rollback();
4187 return -1;
4188 }
4189
4190 $sql1 = 'UPDATE '.$this->db->prefix()."commandedet SET fk_commandefourndet = NULL WHERE fk_commandefourndet=".((int) $this->id);
4191 $resql = $this->db->query($sql1);
4192 if (!$resql) {
4193 $this->db->rollback();
4194 return -1;
4195 }
4196
4197 $sql2 = 'DELETE FROM '.$this->db->prefix()."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4198
4199 dol_syslog(__METHOD__, LOG_DEBUG);
4200 $resql = $this->db->query($sql2);
4201 if ($resql) {
4202 if (!$notrigger) {
4203 // Call trigger
4204 $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4205 if ($result < 0) {
4206 $error++;
4207 }
4208 // End call triggers
4209 }
4210
4211 if (!$error) {
4212 $this->db->commit();
4213 return 1;
4214 }
4215
4216 foreach ($this->errors as $errmsg) {
4217 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4218 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4219 }
4220 $this->db->rollback();
4221 return -1 * $error;
4222 } else {
4223 $this->error = $this->db->lasterror();
4224 return -1;
4225 }
4226 }
4227}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:637
$object ref
Definition info.php:79
Class to manage table ReceptionLineBatch.
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.
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: ...
static replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a product id with another one.
$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 information d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
getNextNumRef($soc)
Returns the next order reference not used, based on the numbering model defined within COMMANDE_SUPPL...
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=0, $date_start=null, $date_end=null, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
loadStateBoard()
Load the indicators this->nb for the state board.
calcAndSetStatusDispatch(User $user, $closeopenorder=1, $comment='')
Calc status regarding to dispatched stock.
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 (receptiondet_batch) and add product into stock...
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.
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=0, $date_end=0, $array_options=[], $fk_unit=null, $pu_ht_devise=0, $ref_supplier='')
Update line.
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)
deleteLine($idline, $notrigger=0)
Delete line.
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.
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.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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)
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($utf8_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:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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 '.
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_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
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:88
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1991
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:1991