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