dolibarr  17.0.4
fournisseur.commande.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6  * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
8  * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
9  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10  * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12  * Copyright (C) 2018-2022 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 
103  public $ref_supplier;
104  public $brouillon;
105  public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process runing -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
106  // -> 7=Canceled/Never received -> (reopen) 3=Process runing
107  // -> 6=Canceled -> (reopen) 2=Approved
108  // -> 9=Refused -> (reopen) 1=Validated
109  // Note: billed or not is on another field "billed"
110  public $statuts; // List of status
111 
112  public $billed;
113 
114  public $socid;
115  public $fourn_id;
116  public $date;
117  public $date_creation;
118  public $date_valid;
119  public $date_approve;
120  public $date_approve2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
121  public $date_commande;
122 
127  public $date_livraison;
128 
132  public $delivery_date;
133 
134  public $total_ht;
135  public $total_tva;
136  public $total_localtax1; // Total Local tax 1
137  public $total_localtax2; // Total Local tax 2
138  public $total_ttc;
139  public $source;
140 
144  public $fk_project;
145 
146  public $cond_reglement_id;
147  public $cond_reglement_code;
148  public $cond_reglement_label; // Label
149  public $cond_reglement_doc; // Label on documents
150 
154  public $fk_account;
155 
156  public $mode_reglement_id;
157  public $mode_reglement_code;
158  public $user_author_id;
159  public $user_valid_id;
160  public $user_approve_id;
161  public $user_approve_id2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
162 
163  public $refuse_note;
164 
165  public $extraparams = array();
166 
170  public $lines = array();
171 
172  //Add for supplier_proposal
173  public $origin;
174  public $origin_id;
175  public $linked_objects = array();
176 
177  // Multicurrency
181  public $fk_multicurrency;
182 
183  public $multicurrency_code;
184  public $multicurrency_tx;
185  public $multicurrency_total_ht;
186  public $multicurrency_total_tva;
187  public $multicurrency_total_ttc;
188 
189 
217  public $fields = array(
218  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>10),
219  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>25, 'searchall'=>1),
220  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>35),
221  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefOrderSupplierShort', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'searchall'=>1),
222  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>45),
223  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
224  'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
225  'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>64),
226  'date_commande' =>array('type'=>'date', 'label'=>'OrderDateShort', 'enabled'=>1, 'visible'=>1, 'position'=>70),
227  'date_livraison' =>array('type'=>'datetime', 'label'=>'DeliveryDate', 'enabled'=>'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible'=>1, 'position'=>74),
228  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>75),
229  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>3, 'notnull'=>-1, 'position'=>80),
230  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>85),
231  'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>90),
232  'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>95),
233  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>100),
234  'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>110),
235  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
236  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>3, 'position'=>135, 'isameasure'=>1),
237  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
238  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>1, 'position'=>145, 'isameasure'=>1),
239  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
240  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155, 'searchall'=>1),
241  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>160, 'searchall'=>1),
242  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>165),
243  'fk_input_method' =>array('type'=>'integer', 'label'=>'OrderMode', 'enabled'=>1, 'visible'=>3, 'position'=>170),
244  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>3, 'position'=>175),
245  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>3, 'position'=>180),
246  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>0, 'position'=>190),
247  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'$conf->banque->enabled', 'visible'=>3, 'position'=>200),
248  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>3, 'position'=>205),
249  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>3, 'position'=>210),
250  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>0, 'position'=>215),
251  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Currency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>220),
252  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'CurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>225),
253  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>230),
254  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>235),
255  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
256  'date_creation' =>array('type'=>'datetime', 'label'=>'Date creation', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
257  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>1, 'notnull'=>1, 'position'=>46),
258  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1000, 'index'=>1),
259  'tms'=>array('type'=>'datetime', 'label'=>"DateModificationShort", 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
260  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'position'=>700),
261  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>701),
262  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>0, 'position'=>900),
263  );
264 
265 
269  const STATUS_DRAFT = 0;
270 
274  const STATUS_VALIDATED = 1;
275 
279  const STATUS_ACCEPTED = 2;
280 
284  const STATUS_ORDERSENT = 3;
285 
290 
295 
299  const STATUS_CANCELED = 6;
300 
305 
309  const STATUS_REFUSED = 9;
310 
311 
316 
317 
318 
324  public function __construct($db)
325  {
326  $this->db = $db;
327  }
328 
329 
337  public function fetch($id, $ref = '')
338  {
339  global $conf;
340 
341  // Check parameters
342  if (empty($id) && empty($ref)) {
343  return -1;
344  }
345 
346  $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,";
347  $sql .= " c.localtax1, c.localtax2, ";
348  $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
349  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_approve, c.fk_user_approve2,";
350  $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,";
351  $sql .= " c.fk_account,";
352  $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
353  $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
354  $sql .= " cm.libelle as methode_commande,";
355  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
356  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
357  $sql .= ', c.fk_incoterms, c.location_incoterms';
358  $sql .= ', i.libelle as label_incoterms';
359  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
360  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
361  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON c.fk_mode_reglement = p.id";
362  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_input_method as cm ON cm.rowid = c.fk_input_method";
363  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
364 
365  if (empty($id)) {
366  $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
367  } else {
368  $sql .= " WHERE c.rowid=".((int) $id);
369  }
370 
371  if ($ref) {
372  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
373  }
374 
375  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
376  $resql = $this->db->query($sql);
377  if ($resql) {
378  $obj = $this->db->fetch_object($resql);
379  if (!$obj) {
380  $this->error = 'Bill with id '.$id.' not found';
381  dol_syslog(get_class($this).'::fetch '.$this->error);
382  return 0;
383  }
384 
385  $this->id = $obj->rowid;
386  $this->entity = $obj->entity;
387 
388  $this->ref = $obj->ref;
389  $this->ref_supplier = $obj->ref_supplier;
390  $this->socid = $obj->fk_soc;
391  $this->fourn_id = $obj->fk_soc;
392  $this->statut = $obj->fk_statut;
393  $this->status = $obj->fk_statut;
394  $this->billed = $obj->billed;
395  $this->user_author_id = $obj->fk_user_author;
396  $this->user_valid_id = $obj->fk_user_valid;
397  $this->user_approve_id = $obj->fk_user_approve;
398  $this->user_approve_id2 = $obj->fk_user_approve2;
399  $this->total_ht = $obj->total_ht;
400  $this->total_tva = $obj->total_tva;
401  $this->total_localtax1 = $obj->localtax1;
402  $this->total_localtax2 = $obj->localtax2;
403  $this->total_ttc = $obj->total_ttc;
404  $this->date_creation = $this->db->jdate($obj->date_creation);
405  $this->date_valid = $this->db->jdate($obj->date_valid);
406  $this->date_approve = $this->db->jdate($obj->date_approve);
407  $this->date_approve2 = $this->db->jdate($obj->date_approve2);
408  $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
409  if (isset($obj->date_commande)) {
410  $this->date = $this->date_commande;
411  } else {
412  $this->date = $this->date_creation;
413  }
414  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
415  $this->delivery_date = $this->db->jdate($obj->delivery_date);
416  $this->remise_percent = $obj->remise_percent;
417  $this->methode_commande_id = $obj->fk_input_method;
418  $this->methode_commande = $obj->methode_commande;
419 
420  $this->source = $obj->source;
421  $this->fk_project = $obj->fk_project;
422  $this->cond_reglement_id = $obj->fk_cond_reglement;
423  $this->cond_reglement_code = $obj->cond_reglement_code;
424  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
425  $this->cond_reglement_label = $obj->cond_reglement_label;
426  $this->cond_reglement_doc = $obj->cond_reglement_doc;
427  $this->fk_account = $obj->fk_account;
428  $this->mode_reglement_id = $obj->fk_mode_reglement;
429  $this->mode_reglement_code = $obj->mode_reglement_code;
430  $this->mode_reglement = $obj->mode_reglement_libelle;
431  $this->note = $obj->note_private; // deprecated
432  $this->note_private = $obj->note_private;
433  $this->note_public = $obj->note_public;
434  $this->model_pdf = $obj->model_pdf;
435  $this->modelpdf = $obj->model_pdf; // deprecated
436 
437  //Incoterms
438  $this->fk_incoterms = $obj->fk_incoterms;
439  $this->location_incoterms = $obj->location_incoterms;
440  $this->label_incoterms = $obj->label_incoterms;
441 
442  // Multicurrency
443  $this->fk_multicurrency = $obj->fk_multicurrency;
444  $this->multicurrency_code = $obj->multicurrency_code;
445  $this->multicurrency_tx = $obj->multicurrency_tx;
446  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
447  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
448  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
449 
450  $this->extraparams = (array) json_decode($obj->extraparams, true);
451 
452  $this->db->free($resql);
453 
454  // Retrieve all extrafield
455  // fetch optionals attributes and labels
456  $this->fetch_optionals();
457 
458  if ($this->statut == 0) {
459  $this->brouillon = 1;
460  }
461 
462  /*
463  * Lines
464  */
465  $result = $this->fetch_lines();
466 
467  if ($result < 0) {
468  return -1;
469  } else {
470  return 1;
471  }
472  } else {
473  $this->error = $this->db->error()." sql=".$sql;
474  return -1;
475  }
476  }
477 
478  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
485  public function fetch_lines($only_product = 0)
486  {
487  global $conf;
488  // phpcs:enable
489 
490  $this->lines = array();
491 
492  $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
493  $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
494  $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
495  $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.special_code, l.fk_parent_line, l.rang,";
496  $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,";
497  $sql .= " l.fk_unit,";
498  $sql .= " l.date_start, l.date_end,";
499  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
500  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
501  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
502  $sql .= " WHERE l.fk_commande = ".((int) $this->id);
503  if ($only_product) {
504  $sql .= ' AND p.fk_product_type = 0';
505  }
506  $sql .= " ORDER BY l.rang, l.rowid";
507  //print $sql;
508 
509  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
510 
511  $result = $this->db->query($sql);
512  if ($result) {
513  $num = $this->db->num_rows($result);
514  $i = 0;
515 
516  while ($i < $num) {
517  $objp = $this->db->fetch_object($result);
518 
519  $line = new CommandeFournisseurLigne($this->db);
520 
521  $line->id = $objp->rowid;
522  $line->fk_commande = $objp->fk_commande;
523  $line->desc = $objp->description;
524  $line->description = $objp->description;
525  $line->qty = $objp->qty;
526  $line->tva_tx = $objp->tva_tx;
527  $line->localtax1_tx = $objp->localtax1_tx;
528  $line->localtax2_tx = $objp->localtax2_tx;
529  $line->localtax1_type = $objp->localtax1_type;
530  $line->localtax2_type = $objp->localtax2_type;
531  $line->subprice = $objp->subprice;
532  $line->pu_ht = $objp->subprice;
533  $line->remise_percent = $objp->remise_percent;
534 
535  $line->vat_src_code = $objp->vat_src_code;
536  $line->total_ht = $objp->total_ht;
537  $line->total_tva = $objp->total_tva;
538  $line->total_localtax1 = $objp->total_localtax1;
539  $line->total_localtax2 = $objp->total_localtax2;
540  $line->total_ttc = $objp->total_ttc;
541  $line->product_type = $objp->product_type;
542 
543  $line->fk_product = $objp->fk_product;
544 
545  $line->libelle = $objp->product_label; // deprecated
546  $line->product_label = $objp->product_label;
547  $line->product_desc = $objp->product_desc;
548  $line->product_tobatch = $objp->product_tobatch;
549  $line->product_barcode = $objp->product_barcode;
550 
551  $line->ref = $objp->product_ref; // Ref of product
552  $line->product_ref = $objp->product_ref; // Ref of product
553  $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
554  $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
555 
556  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
557  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
558  // Move this into another method and call it when required.
559 
560  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
561  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
562  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
563  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
564  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
565  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
566  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
567  $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
568  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
569  $sqlsearchpackage .= " LIMIT 1";
570 
571  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
572  if ($resqlsearchpackage) {
573  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
574  if ($objsearchpackage) {
575  $line->fk_fournprice = $objsearchpackage->rowid;
576  $line->packaging = $objsearchpackage->packaging;
577  }
578  } else {
579  $this->error = $this->db->lasterror();
580  return -1;
581  }
582  }
583 
584  $line->date_start = $this->db->jdate($objp->date_start);
585  $line->date_end = $this->db->jdate($objp->date_end);
586  $line->fk_unit = $objp->fk_unit;
587 
588  // Multicurrency
589  $line->fk_multicurrency = $objp->fk_multicurrency;
590  $line->multicurrency_code = $objp->multicurrency_code;
591  $line->multicurrency_subprice = $objp->multicurrency_subprice;
592  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
593  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
594  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
595 
596  $line->special_code = $objp->special_code;
597  $line->fk_parent_line = $objp->fk_parent_line;
598 
599  $line->rang = $objp->rang;
600 
601  // Retrieve all extrafield
602  // fetch optionals attributes and labels
603  $line->fetch_optionals();
604 
605  $this->lines[$i] = $line;
606 
607  $i++;
608  }
609  $this->db->free($result);
610 
611  return $num;
612  } else {
613  $this->error = $this->db->error()." sql=".$sql;
614  return -1;
615  }
616  }
617 
626  public function valid($user, $idwarehouse = 0, $notrigger = 0)
627  {
628  global $langs, $conf;
629  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
630 
631  $error = 0;
632 
633  dol_syslog(get_class($this)."::valid");
634  $result = 0;
635  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && (!empty($user->rights->fournisseur->commande->creer) || !empty($user->rights->supplier_order->creer)))
636  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->supplier_order_advance->validate))) {
637  $this->db->begin();
638 
639  // Definition of supplier order numbering model name
640  $soc = new Societe($this->db);
641  $soc->fetch($this->fourn_id);
642 
643  // Check if object has a temporary ref
644  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
645  $num = $this->getNextNumRef($soc);
646  } else {
647  $num = $this->ref;
648  }
649  $this->newref = dol_sanitizeFileName($num);
650 
651  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
652  $sql .= " SET ref='".$this->db->escape($num)."',";
653  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
654  $sql .= " date_valid='".$this->db->idate(dol_now())."',";
655  $sql .= " fk_user_valid = ".((int) $user->id);
656  $sql .= " WHERE rowid = ".((int) $this->id);
657  $sql .= " AND fk_statut = ".self::STATUS_DRAFT;
658 
659  $resql = $this->db->query($sql);
660  if (!$resql) {
661  dol_print_error($this->db);
662  $error++;
663  }
664 
665  if (!$error && !$notrigger) {
666  // Call trigger
667  $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
668  if ($result < 0) {
669  $error++;
670  }
671  // End call triggers
672  }
673 
674  if (!$error) {
675  $this->oldref = $this->ref;
676 
677  // Rename directory if dir was a temporary ref
678  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
679  // Now we rename also files into index
680  $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)."'";
681  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
682  $resql = $this->db->query($sql);
683  if (!$resql) {
684  $error++; $this->error = $this->db->lasterror();
685  }
686 
687  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
688  $oldref = dol_sanitizeFileName($this->ref);
689  $newref = dol_sanitizeFileName($num);
690  $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
691  $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
692  if (!$error && file_exists($dirsource)) {
693  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
694 
695  if (@rename($dirsource, $dirdest)) {
696  dol_syslog("Rename ok");
697  // Rename docs starting with $oldref with $newref
698  $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
699  foreach ($listoffiles as $fileentry) {
700  $dirsource = $fileentry['name'];
701  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
702  $dirsource = $fileentry['path'].'/'.$dirsource;
703  $dirdest = $fileentry['path'].'/'.$dirdest;
704  @rename($dirsource, $dirdest);
705  }
706  }
707  }
708  }
709  }
710 
711  if (!$error) {
712  $result = 1;
713  $this->statut = self::STATUS_VALIDATED;
714  $this->ref = $num;
715  }
716 
717  if (!$error) {
718  $this->db->commit();
719  return 1;
720  } else {
721  $this->db->rollback();
722  return -1;
723  }
724  } else {
725  $this->error = 'NotAuthorized';
726  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
727  return -1;
728  }
729  }
730 
737  public function getLibStatut($mode = 0)
738  {
739  return $this->LibStatut($this->statut, $mode, $this->billed);
740  }
741 
742  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
751  public function LibStatut($status, $mode = 0, $billed = 0)
752  {
753  // phpcs:enable
754  global $conf, $langs, $hookmanager;
755 
756  if (empty($this->statuts) || empty($this->statuts_short)) {
757  $langs->load('orders');
758 
759  $this->statuts[0] = 'StatusSupplierOrderDraft';
760  $this->statuts[1] = 'StatusSupplierOrderValidated';
761  $this->statuts[2] = 'StatusSupplierOrderApproved';
762  if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
763  $this->statuts[3] = 'StatusSupplierOrderOnProcess';
764  } else {
765  $this->statuts[3] = 'StatusSupplierOrderOnProcessWithValidation';
766  }
767  $this->statuts[4] = 'StatusSupplierOrderReceivedPartially';
768  $this->statuts[5] = 'StatusSupplierOrderReceivedAll';
769  $this->statuts[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
770  $this->statuts[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
771  $this->statuts[9] = 'StatusSupplierOrderRefused';
772 
773  // List of language codes for status
774  $this->statuts_short[0] = 'StatusSupplierOrderDraftShort';
775  $this->statuts_short[1] = 'StatusSupplierOrderValidatedShort';
776  $this->statuts_short[2] = 'StatusSupplierOrderApprovedShort';
777  $this->statuts_short[3] = 'StatusSupplierOrderOnProcessShort';
778  $this->statuts_short[4] = 'StatusSupplierOrderReceivedPartiallyShort';
779  $this->statuts_short[5] = 'StatusSupplierOrderReceivedAllShort';
780  $this->statuts_short[6] = 'StatusSupplierOrderCanceledShort';
781  $this->statuts_short[7] = 'StatusSupplierOrderCanceledShort';
782  $this->statuts_short[9] = 'StatusSupplierOrderRefusedShort';
783  }
784 
785  $statustrans = array(
786  0 => 'status0',
787  1 => 'status1b',
788  2 => 'status1',
789  3 => 'status4',
790  4 => 'status4b',
791  5 => 'status6',
792  6 => 'status9',
793  7 => 'status9',
794  9 => 'status9',
795  );
796 
797  $statusClass = 'status0';
798  if (!empty($statustrans[$status])) {
799  $statusClass = $statustrans[$status];
800  }
801 
802  $billedtext = '';
803  if ($billed) {
804  $billedtext = ' - '.$langs->trans("Billed");
805  }
806  if ($status == 5 && $billed) {
807  $statusClass = 'status6';
808  }
809 
810  $statusLong = $langs->transnoentitiesnoconv($this->statuts[$status]).$billedtext;
811  $statusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
812 
813  $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
814  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
815  if ($reshook > 0) {
816  return $hookmanager->resPrint;
817  }
818 
819  return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
820  }
821 
822 
833  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
834  {
835  global $langs, $conf, $user, $hookmanager;
836 
837  $result = '';
838 
839  $label = '';
840 
841  if ($user->hasRight("fournisseur", "commande", "read")) {
842  $label = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
843  if (isset($this->statut)) {
844  $label .= ' '.$this->getLibStatut(5);
845  }
846  if (!empty($this->ref)) {
847  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
848  }
849  if (!empty($this->ref_supplier)) {
850  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
851  }
852  if (!empty($this->total_ht)) {
853  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
854  }
855  if (!empty($this->total_tva)) {
856  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
857  }
858  if (!empty($this->total_ttc)) {
859  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
860  }
861  if (!empty($this->date)) {
862  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
863  }
864  if (!empty($this->delivery_date)) {
865  $label .= '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
866  }
867  }
868 
869  $picto = 'order';
870  $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
871 
872  if ($option !== 'nolink') {
873  // Add param to save lastsearch_values or not
874  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
875  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
876  $add_save_lastsearch_values = 1;
877  }
878  if ($add_save_lastsearch_values) {
879  $url .= '&save_lastsearch_values=1';
880  }
881  }
882 
883  $linkclose = '';
884  if (empty($notooltip)) {
885  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
886  $label = $langs->trans("ShowOrder");
887  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
888  }
889  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
890  $linkclose .= ' class="classfortooltip"';
891  }
892 
893  $linkstart = '<a href="'.$url.'"';
894  $linkstart .= $linkclose.'>';
895  $linkend = '</a>';
896 
897  $result .= $linkstart;
898  if ($withpicto) {
899  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
900  }
901  if ($withpicto != 2) {
902  $result .= $this->ref;
903  }
904  $result .= $linkend;
905 
906  if ($addlinktonotes) {
907  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
908  if ($txttoshow) {
909  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
910  $result .= ' <span class="note inline-block">';
911  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
912  $result .= img_picto('', 'note');
913  $result .= '</a>';
914  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
915  //$result.='</a>';
916  $result .= '</span>';
917  }
918  }
919 
920  global $action;
921  $hookmanager->initHooks(array($this->element . 'dao'));
922  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
923  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
924  if ($reshook > 0) {
925  $result = $hookmanager->resPrint;
926  } else {
927  $result .= $hookmanager->resPrint;
928  }
929  return $result;
930  }
931 
932 
940  public function getNextNumRef($soc)
941  {
942  global $db, $langs, $conf;
943  $langs->load("orders");
944 
945  if (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER)) {
946  $mybool = false;
947 
948  $file = $conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER.'.php';
949  $classname = $conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER;
950 
951  // Include file with class
952  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
953 
954  foreach ($dirmodels as $reldir) {
955  $dir = dol_buildpath($reldir."core/modules/supplier_order/");
956 
957  // Load file with numbering class (if found)
958  $mybool |= @include_once $dir.$file;
959  }
960 
961  if ($mybool === false) {
962  dol_print_error('', "Failed to include file ".$file);
963  return '';
964  }
965 
966  $obj = new $classname();
967  $numref = $obj->getNextValue($soc, $this);
968 
969  if ($numref != "") {
970  return $numref;
971  } else {
972  $this->error = $obj->error;
973  return -1;
974  }
975  } else {
976  $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
977  return -2;
978  }
979  }
986  public function classifyBilled(User $user)
987  {
988  $error = 0;
989 
990  if ($this->billed) {
991  return 0;
992  }
993 
994  $this->db->begin();
995 
996  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
997  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
998 
999  if ($this->db->query($sql)) {
1000  if (!$error) {
1001  // Call trigger
1002  $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1003  if ($result < 0) {
1004  $error++;
1005  }
1006  // End call triggers
1007  }
1008 
1009  if (!$error) {
1010  $this->billed = 1;
1011 
1012  $this->db->commit();
1013  return 1;
1014  } else {
1015  $this->db->rollback();
1016  return -1;
1017  }
1018  } else {
1019  dol_print_error($this->db);
1020 
1021  $this->db->rollback();
1022  return -1;
1023  }
1024  }
1025 
1034  public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1035  {
1036  global $langs, $conf;
1037  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1038 
1039  $error = 0;
1040 
1041  dol_syslog(get_class($this)."::approve");
1042 
1043  if ($user->rights->fournisseur->commande->approuver) {
1044  $now = dol_now();
1045 
1046  $this->db->begin();
1047 
1048  // Definition of order numbering model name
1049  $soc = new Societe($this->db);
1050  $soc->fetch($this->fourn_id);
1051 
1052  // Check if object has a temporary ref
1053  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1054  $num = $this->getNextNumRef($soc);
1055  } else {
1056  $num = $this->ref;
1057  }
1058  $this->newref = dol_sanitizeFileName($num);
1059 
1060  // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1061  $movetoapprovestatus = true;
1062  $comment = '';
1063 
1064  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1065  $sql .= " SET ref='".$this->db->escape($num)."',";
1066  if (empty($secondlevel)) { // standard or first level approval
1067  $sql .= " date_approve='".$this->db->idate($now)."',";
1068  $sql .= " fk_user_approve = ".$user->id;
1069  if (!empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1070  if (empty($this->user_approve_id2)) {
1071  $movetoapprovestatus = false; // second level approval not done
1072  $comment = ' (first level)';
1073  }
1074  }
1075  } else // request a second level approval
1076  {
1077  $sql .= " date_approve2='".$this->db->idate($now)."',";
1078  $sql .= " fk_user_approve2 = ".((int) $user->id);
1079  if (empty($this->user_approve_id)) {
1080  $movetoapprovestatus = false; // first level approval not done
1081  }
1082  $comment = ' (second level)';
1083  }
1084  // If double approval is required and first approval, we keep status to 1 = validated
1085  if ($movetoapprovestatus) {
1086  $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1087  } else {
1088  $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1089  }
1090  $sql .= " WHERE rowid = ".((int) $this->id);
1091  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1092 
1093  if ($this->db->query($sql)) {
1094  if (!empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT)) {
1095  $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1096  if ($result < 0 && $result != -2) { // -2 means already exists
1097  $error++;
1098  }
1099  }
1100 
1101  // If stock is incremented on validate order, we must increment it
1102  if (!$error && $movetoapprovestatus && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) {
1103  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1104  $langs->load("agenda");
1105 
1106  $cpt = count($this->lines);
1107  for ($i = 0; $i < $cpt; $i++) {
1108  // Product with reference
1109  if ($this->lines[$i]->fk_product > 0) {
1110  $this->line = $this->lines[$i];
1111  $mouvP = new MouvementStock($this->db);
1112  $mouvP->origin = &$this;
1113  $mouvP->setOrigin($this->element, $this->id);
1114  // We decrement stock of product (and sub-products)
1115  $up_ht_disc = $this->lines[$i]->subprice;
1116  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1117  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1118  }
1119  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1120  if ($result < 0) {
1121  $error++;
1122  }
1123  unset($this->line);
1124  }
1125  }
1126  }
1127 
1128  if (!$error) {
1129  // Call trigger
1130  $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1131  if ($result < 0) {
1132  $error++;
1133  }
1134  // End call triggers
1135  }
1136 
1137  if (!$error) {
1138  $this->ref = $this->newref;
1139 
1140  if ($movetoapprovestatus) {
1141  $this->statut = self::STATUS_ACCEPTED;
1142  } else {
1143  $this->statut = self::STATUS_VALIDATED;
1144  }
1145  if (empty($secondlevel)) { // standard or first level approval
1146  $this->date_approve = $now;
1147  $this->user_approve_id = $user->id;
1148  } else // request a second level approval
1149  {
1150  $this->date_approve2 = $now;
1151  $this->user_approve_id2 = $user->id;
1152  }
1153 
1154  $this->db->commit();
1155  return 1;
1156  } else {
1157  $this->db->rollback();
1158  return -1;
1159  }
1160  } else {
1161  $this->db->rollback();
1162  $this->error = $this->db->lasterror();
1163  return -1;
1164  }
1165  } else {
1166  dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1167  }
1168  return -1;
1169  }
1170 
1177  public function refuse($user)
1178  {
1179  global $conf, $langs;
1180 
1181  $error = 0;
1182 
1183  dol_syslog(get_class($this)."::refuse");
1184  $result = 0;
1185  if ($user->rights->fournisseur->commande->approuver) {
1186  $this->db->begin();
1187 
1188  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1189  $sql .= " WHERE rowid = ".((int) $this->id);
1190 
1191  if ($this->db->query($sql)) {
1192  $result = 0;
1193 
1194  if ($error == 0) {
1195  // Call trigger
1196  $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1197  if ($result < 0) {
1198  $error++;
1199  $this->db->rollback();
1200  } else {
1201  $this->db->commit();
1202  }
1203  // End call triggers
1204  }
1205  } else {
1206  $this->db->rollback();
1207  $this->error = $this->db->lasterror();
1208  dol_syslog(get_class($this)."::refuse Error -1");
1209  $result = -1;
1210  }
1211  } else {
1212  dol_syslog(get_class($this)."::refuse Not Authorized");
1213  }
1214  return $result;
1215  }
1216 
1217  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1226  public function Cancel($user, $idwarehouse = -1)
1227  {
1228  // phpcs:enable
1229  global $langs, $conf;
1230 
1231  $error = 0;
1232 
1233  //dol_syslog("CommandeFournisseur::Cancel");
1234  $result = 0;
1235  if ($user->rights->fournisseur->commande->commander) {
1236  $statut = self::STATUS_CANCELED;
1237 
1238  $this->db->begin();
1239 
1240  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".((int) $statut);
1241  $sql .= " WHERE rowid = ".((int) $this->id);
1242  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1243  if ($this->db->query($sql)) {
1244  $result = 0;
1245 
1246  // Call trigger
1247  $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1248  if ($result < 0) {
1249  $error++;
1250  }
1251  // End call triggers
1252 
1253  if ($error == 0) {
1254  $this->db->commit();
1255  return 1;
1256  } else {
1257  $this->db->rollback();
1258  return -1;
1259  }
1260  } else {
1261  $this->db->rollback();
1262  $this->error = $this->db->lasterror();
1263  dol_syslog(get_class($this)."::cancel ".$this->error);
1264  return -1;
1265  }
1266  } else {
1267  dol_syslog(get_class($this)."::cancel Not Authorized");
1268  return -1;
1269  }
1270  }
1271 
1281  public function commande($user, $date, $methode, $comment = '')
1282  {
1283  global $langs;
1284  dol_syslog(get_class($this)."::commande");
1285  $error = 0;
1286  if ($user->rights->fournisseur->commande->commander) {
1287  $this->db->begin();
1288 
1289  $newnoteprivate = $this->note_private;
1290  if ($comment) {
1291  $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1292  }
1293 
1294  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1295  $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1296  $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1297  $sql .= " WHERE rowid=".((int) $this->id);
1298 
1299  dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1300  if ($this->db->query($sql)) {
1301  $this->statut = self::STATUS_ORDERSENT;
1302  $this->methode_commande_id = $methode;
1303  $this->date_commande = $date;
1304  $this->context = array('comments' => $comment);
1305 
1306  // Call trigger
1307  $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1308  if ($result < 0) {
1309  $error++;
1310  }
1311  // End call triggers
1312  } else {
1313  $error++;
1314  $this->error = $this->db->lasterror();
1315  $this->errors[] = $this->db->lasterror();
1316  }
1317 
1318  if (!$error) {
1319  $this->db->commit();
1320  } else {
1321  $this->db->rollback();
1322  }
1323  } else {
1324  $error++;
1325  $this->error = $langs->trans('NotAuthorized');
1326  $this->errors[] = $langs->trans('NotAuthorized');
1327  dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1328  }
1329 
1330  return ($error ? -1 : 1);
1331  }
1332 
1340  public function create($user, $notrigger = 0)
1341  {
1342  global $langs, $conf, $hookmanager;
1343 
1344  $this->db->begin();
1345 
1346  $error = 0;
1347  $now = dol_now();
1348 
1349  // set tmp vars
1350  $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1351  if (empty($date)) {
1352  $date = $now;
1353  }
1354  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1355 
1356  // Clean parameters
1357  if (empty($this->source)) {
1358  $this->source = 0;
1359  }
1360 
1361  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1362  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1363  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1364  } else {
1365  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1366  }
1367  if (empty($this->fk_multicurrency)) {
1368  $this->multicurrency_code = $conf->currency;
1369  $this->fk_multicurrency = 0;
1370  $this->multicurrency_tx = 1;
1371  }
1372 
1373  // We set order into draft status
1374  $this->brouillon = 1;
1375 
1376  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1377  $sql .= "ref";
1378  $sql .= ", ref_supplier";
1379  $sql .= ", note_private";
1380  $sql .= ", note_public";
1381  $sql .= ", entity";
1382  $sql .= ", fk_soc";
1383  $sql .= ", fk_projet";
1384  $sql .= ", date_creation";
1385  $sql .= ", date_livraison";
1386  $sql .= ", fk_user_author";
1387  $sql .= ", fk_statut";
1388  $sql .= ", source";
1389  $sql .= ", model_pdf";
1390  $sql .= ", fk_mode_reglement";
1391  $sql .= ", fk_cond_reglement";
1392  $sql .= ", fk_account";
1393  $sql .= ", fk_incoterms, location_incoterms";
1394  $sql .= ", fk_multicurrency";
1395  $sql .= ", multicurrency_code";
1396  $sql .= ", multicurrency_tx";
1397  $sql .= ") ";
1398  $sql .= " VALUES (";
1399  $sql .= "'(PROV)'";
1400  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
1401  $sql .= ", '".$this->db->escape($this->note_private)."'";
1402  $sql .= ", '".$this->db->escape($this->note_public)."'";
1403  $sql .= ", ".setEntity($this);
1404  $sql .= ", ".((int) $this->socid);
1405  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1406  $sql .= ", '".$this->db->idate($date)."'";
1407  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1408  $sql .= ", ".((int) $user->id);
1409  $sql .= ", ".self::STATUS_DRAFT;
1410  $sql .= ", ".((int) $this->source);
1411  $sql .= ", '".$this->db->escape($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)."'";
1412  $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1413  $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1414  $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1415  $sql .= ", ".(int) $this->fk_incoterms;
1416  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1417  $sql .= ", ".(int) $this->fk_multicurrency;
1418  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1419  $sql .= ", ".(double) $this->multicurrency_tx;
1420  $sql .= ")";
1421 
1422  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1423  if ($this->db->query($sql)) {
1424  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1425 
1426  if ($this->id) {
1427  $num = count($this->lines);
1428 
1429  // insert products details into database
1430  for ($i = 0; $i < $num; $i++) {
1431  $line = $this->lines[$i];
1432  if (!is_object($line)) {
1433  $line = (object) $line;
1434  }
1435 
1436 
1437  //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1438 
1439  // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1440  $result = $this->addline(
1441  $line->desc,
1442  $line->subprice,
1443  $line->qty,
1444  $line->tva_tx,
1445  $line->localtax1_tx,
1446  $line->localtax2_tx,
1447  $line->fk_product,
1448  0,
1449  $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
1450  $line->remise_percent,
1451  'HT',
1452  0,
1453  $line->product_type,
1454  $line->info_bits,
1455  false,
1456  $line->date_start,
1457  $line->date_end,
1458  $line->array_options,
1459  $line->fk_unit,
1460  $line->multicurrency_subprice, // pu_ht_devise
1461  $line->origin, // origin
1462  $line->origin_id, // origin_id
1463  $line->rang, // rang
1464  $line->special_code
1465  );
1466  if ($result < 0) {
1467  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functionnal error
1468  $this->db->rollback();
1469  return -1;
1470  }
1471  }
1472 
1473  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1474  $sql .= " SET ref='(PROV".$this->id.")'";
1475  $sql .= " WHERE rowid=".((int) $this->id);
1476  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1477  if ($this->db->query($sql)) {
1478  // Add link with price request and supplier order
1479  if ($this->id) {
1480  $this->ref = "(PROV".$this->id.")";
1481 
1482  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1483  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1484  }
1485 
1486  // Add object linked
1487  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1488  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1489  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, ...))
1490  foreach ($tmp_origin_id as $origin_id) {
1491  $ret = $this->add_object_linked($origin, $origin_id);
1492  if (!$ret) {
1493  dol_print_error($this->db);
1494  $error++;
1495  }
1496  }
1497  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1498  {
1499  $origin_id = $tmp_origin_id;
1500  $ret = $this->add_object_linked($origin, $origin_id);
1501  if (!$ret) {
1502  dol_print_error($this->db);
1503  $error++;
1504  }
1505  }
1506  }
1507  }
1508  }
1509 
1510  if (!$error) {
1511  $result = $this->insertExtraFields();
1512  if ($result < 0) {
1513  $error++;
1514  }
1515  }
1516 
1517  if (!$error && !$notrigger) {
1518  // Call trigger
1519  $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1520  if ($result < 0) {
1521  $this->db->rollback();
1522  return -1;
1523  }
1524  // End call triggers
1525  }
1526 
1527  $this->db->commit();
1528  return $this->id;
1529  } else {
1530  $this->error = $this->db->lasterror();
1531  $this->db->rollback();
1532  return -2;
1533  }
1534  }
1535  } else {
1536  $this->error = $this->db->lasterror();
1537  $this->db->rollback();
1538  return -1;
1539  }
1540  }
1541 
1549  public function update(User $user, $notrigger = 0)
1550  {
1551  global $conf;
1552 
1553  $error = 0;
1554 
1555  // Clean parameters
1556  if (isset($this->ref)) {
1557  $this->ref = trim($this->ref);
1558  }
1559  if (isset($this->ref_supplier)) {
1560  $this->ref_supplier = trim($this->ref_supplier);
1561  }
1562  if (isset($this->note_private)) {
1563  $this->note_private = trim($this->note_private);
1564  }
1565  if (isset($this->note_public)) {
1566  $this->note_public = trim($this->note_public);
1567  }
1568  if (isset($this->model_pdf)) {
1569  $this->model_pdf = trim($this->model_pdf);
1570  }
1571  if (isset($this->import_key)) {
1572  $this->import_key = trim($this->import_key);
1573  }
1574 
1575  // Update request
1576  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1577 
1578  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1579  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1580  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1581  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1582  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1583  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1584  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1585  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1586  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1587  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1588  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1589  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1590  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1591  $sql .= " fk_user_valid=".(isset($this->user_valid) && $this->user_valid > 0 ? $this->user_valid : "null").",";
1592  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1593  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1594  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1595  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1596  //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1597  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1598  //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1599  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1600  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1601  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1602  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1603 
1604  $sql .= " WHERE rowid=".((int) $this->id);
1605 
1606  $this->db->begin();
1607 
1608  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1609  $resql = $this->db->query($sql);
1610  if (!$resql) {
1611  $error++;
1612  $this->errors[] = "Error ".$this->db->lasterror();
1613  }
1614 
1615  if (!$error) {
1616  $result = $this->insertExtraFields();
1617  if ($result < 0) {
1618  $error++;
1619  }
1620  }
1621 
1622  if (!$error && !$notrigger) {
1623  // Call trigger
1624  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1625  if ($result < 0) {
1626  $error++;
1627  }
1628  // End call triggers
1629  }
1630 
1631  // Commit or rollback
1632  if ($error) {
1633  foreach ($this->errors as $errmsg) {
1634  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1635  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1636  }
1637  $this->db->rollback();
1638  return -1 * $error;
1639  } else {
1640  $this->db->commit();
1641  return 1;
1642  }
1643  }
1644 
1653  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1654  {
1655  global $conf, $user, $hookmanager;
1656 
1657  $error = 0;
1658 
1659  $this->db->begin();
1660 
1661  // get extrafields so they will be clone
1662  foreach ($this->lines as $line) {
1663  $line->fetch_optionals();
1664  }
1665 
1666  // Load source object
1667  $objFrom = clone $this;
1668 
1669  // Change socid if needed
1670  if (!empty($socid) && $socid != $this->socid) {
1671  $objsoc = new Societe($this->db);
1672 
1673  if ($objsoc->fetch($socid) > 0) {
1674  $this->socid = $objsoc->id;
1675  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1676  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1677  $this->fk_project = 0;
1678  $this->fk_delivery_address = 0;
1679  }
1680 
1681  // TODO Change product price if multi-prices
1682  }
1683 
1684  $this->id = 0;
1685  $this->statut = self::STATUS_DRAFT;
1686 
1687  // Clear fields
1688  $this->user_author_id = $user->id;
1689  $this->user_valid = 0;
1690  $this->date_creation = '';
1691  $this->date_validation = '';
1692  $this->ref_supplier = '';
1693  $this->user_approve_id = '';
1694  $this->user_approve_id2 = '';
1695  $this->date_approve = '';
1696  $this->date_approve2 = '';
1697 
1698  // Create clone
1699  $this->context['createfromclone'] = 'createfromclone';
1700  $result = $this->create($user, $notrigger);
1701  if ($result < 0) {
1702  $error++;
1703  }
1704 
1705  if (!$error) {
1706  // Hook of thirdparty module
1707  if (is_object($hookmanager)) {
1708  $parameters = array('objFrom'=>$objFrom);
1709  $action = '';
1710  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1711  if ($reshook < 0) {
1712  $this->errors += $hookmanager->errors;
1713  $this->error = $hookmanager->error;
1714  $error++;
1715  }
1716  }
1717  }
1718 
1719  unset($this->context['createfromclone']);
1720 
1721  // End
1722  if (!$error) {
1723  $this->db->commit();
1724  return $this->id;
1725  } else {
1726  $this->db->rollback();
1727  return -1;
1728  }
1729  }
1730 
1760  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)
1761  {
1762  global $langs, $mysoc, $conf;
1763 
1764  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");
1765  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1766 
1767  if ($this->statut == self::STATUS_DRAFT) {
1768  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1769 
1770  // Clean parameters
1771  if (empty($qty)) {
1772  $qty = 0;
1773  }
1774  if (!$info_bits) {
1775  $info_bits = 0;
1776  }
1777  if (empty($txtva)) {
1778  $txtva = 0;
1779  }
1780  if (empty($rang)) {
1781  $rang = 0;
1782  }
1783  if (empty($txlocaltax1)) {
1784  $txlocaltax1 = 0;
1785  }
1786  if (empty($txlocaltax2)) {
1787  $txlocaltax2 = 0;
1788  }
1789  if (empty($remise_percent)) {
1790  $remise_percent = 0;
1791  }
1792 
1793  $remise_percent = price2num($remise_percent);
1794  $qty = price2num($qty);
1795  $pu_ht = price2num($pu_ht);
1796  $pu_ht_devise = price2num($pu_ht_devise);
1797  $pu_ttc = price2num($pu_ttc);
1798  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1799  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1800  }
1801  $txlocaltax1 = price2num($txlocaltax1);
1802  $txlocaltax2 = price2num($txlocaltax2);
1803  if ($price_base_type == 'HT') {
1804  $pu = $pu_ht;
1805  } else {
1806  $pu = $pu_ttc;
1807  }
1808  $desc = trim($desc);
1809 
1810  // Check parameters
1811  if ($qty < 0 && !$fk_product) {
1812  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1813  return -1;
1814  }
1815  if ($type < 0) {
1816  return -1;
1817  }
1818  if ($date_start && $date_end && $date_start > $date_end) {
1819  $langs->load("errors");
1820  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1821  return -1;
1822  }
1823 
1824 
1825  $this->db->begin();
1826 
1827  $product_type = $type;
1828  $label = ''; // deprecated
1829 
1830  if ($fk_product > 0) {
1831  if (!empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY)) { // Not the common case
1832  // Check quantity is enough
1833  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);
1834  $prod = new ProductFournisseur($this->db);
1835  if ($prod->fetch($fk_product) > 0) {
1836  $product_type = $prod->type;
1837  $label = $prod->label;
1838 
1839  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1840  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1841  $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
1842 
1843  // If supplier order created from sales order, we take best supplier price
1844  // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1845  if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1846  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1847  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1848  // is remise percent not keyed but present for the product we add it
1849  if ($remise_percent == 0 && $prod->remise_percent != 0) {
1850  $remise_percent = $prod->remise_percent;
1851  }
1852  }
1853  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1854  $langs->load("errors");
1855  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1856  $this->db->rollback();
1857  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1858  //$pu = $prod->fourn_pu; // We do not overwrite unit price
1859  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1860  return -1;
1861  }
1862  if ($result == -1) {
1863  $langs->load("errors");
1864  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1865  $this->db->rollback();
1866  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1867  return -1;
1868  }
1869  if ($result < -1) {
1870  $this->error = $prod->error;
1871  $this->db->rollback();
1872  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1873  return -1;
1874  }
1875  } else {
1876  $this->error = $prod->error;
1877  $this->db->rollback();
1878  return -1;
1879  }
1880  }
1881 
1882  // Predefine quantity according to packaging
1883  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
1884  $prod = new Product($this->db);
1885  $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
1886 
1887  if ($qty < $prod->packaging) {
1888  $qty = $prod->packaging;
1889  } else {
1890  if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1891  $coeff = intval($qty / $prod->packaging) + 1;
1892  $qty = $prod->packaging * $coeff;
1893  setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1894  }
1895  }
1896  }
1897  }
1898 
1899  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1900  $pu = 0;
1901  }
1902 
1903  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1904 
1905  // Clean vat code
1906  $reg = array();
1907  $vat_src_code = '';
1908  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1909  $vat_src_code = $reg[1];
1910  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1911  }
1912 
1913  // Calcul du total TTC et de la TVA pour la ligne a partir de
1914  // qty, pu, remise_percent et txtva
1915  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1916  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1917 
1918  $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);
1919 
1920  $total_ht = $tabprice[0];
1921  $total_tva = $tabprice[1];
1922  $total_ttc = $tabprice[2];
1923  $total_localtax1 = $tabprice[9];
1924  $total_localtax2 = $tabprice[10];
1925  $pu = $pu_ht = $tabprice[3];
1926 
1927  // MultiCurrency
1928  $multicurrency_total_ht = $tabprice[16];
1929  $multicurrency_total_tva = $tabprice[17];
1930  $multicurrency_total_ttc = $tabprice[18];
1931  $pu_ht_devise = $tabprice[19];
1932 
1933  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1934  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1935 
1936  if ($rang < 0) {
1937  $rangmax = $this->line_max();
1938  $rang = $rangmax + 1;
1939  }
1940 
1941  // Insert line
1942  $this->line = new CommandeFournisseurLigne($this->db);
1943 
1944  $this->line->context = $this->context;
1945 
1946  $this->line->fk_commande = $this->id;
1947  $this->line->label = $label;
1948  $this->line->ref_fourn = $ref_supplier;
1949  $this->line->ref_supplier = $ref_supplier;
1950  $this->line->desc = $desc;
1951  $this->line->qty = $qty;
1952  $this->line->tva_tx = $txtva;
1953  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1954  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1955  $this->line->localtax1_type = $localtax1_type;
1956  $this->line->localtax2_type = $localtax2_type;
1957  $this->line->fk_product = $fk_product;
1958  $this->line->product_type = $product_type;
1959  $this->line->remise_percent = $remise_percent;
1960  $this->line->subprice = $pu_ht;
1961  $this->line->rang = $rang;
1962  $this->line->info_bits = $info_bits;
1963 
1964  $this->line->vat_src_code = $vat_src_code;
1965  $this->line->total_ht = $total_ht;
1966  $this->line->total_tva = $total_tva;
1967  $this->line->total_localtax1 = $total_localtax1;
1968  $this->line->total_localtax2 = $total_localtax2;
1969  $this->line->total_ttc = $total_ttc;
1970  $this->line->product_type = $type;
1971  $this->line->special_code = (!empty($special_code) ? $special_code : 0);
1972  $this->line->origin = $origin;
1973  $this->line->origin_id = $origin_id;
1974  $this->line->fk_unit = $fk_unit;
1975 
1976  $this->line->date_start = $date_start;
1977  $this->line->date_end = $date_end;
1978 
1979  // Multicurrency
1980  $this->line->fk_multicurrency = $this->fk_multicurrency;
1981  $this->line->multicurrency_code = $this->multicurrency_code;
1982  $this->line->multicurrency_subprice = $pu_ht_devise;
1983  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1984  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1985  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1986 
1987  $this->line->subprice = $pu_ht;
1988  $this->line->price = $this->line->subprice;
1989 
1990  $this->line->remise_percent = $remise_percent;
1991 
1992  if (is_array($array_options) && count($array_options) > 0) {
1993  $this->line->array_options = $array_options;
1994  }
1995 
1996  $result = $this->line->insert($notrigger);
1997  if ($result > 0) {
1998  // Reorder if child line
1999  if (!empty($fk_parent_line)) {
2000  $this->line_order(true, 'DESC');
2001  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2002  $linecount = count($this->lines);
2003  for ($ii = $rang; $ii <= $linecount; $ii++) {
2004  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2005  }
2006  }
2007 
2008  // Mise a jour informations denormalisees au niveau de la commande meme
2009  $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.
2010  if ($result > 0) {
2011  $this->db->commit();
2012  return $this->line->id;
2013  } else {
2014  $this->db->rollback();
2015  return -1;
2016  }
2017  } else {
2018  $this->error = $this->line->error;
2019  $this->errors = $this->line->errors;
2020  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2021  $this->db->rollback();
2022  return -1;
2023  }
2024  }
2025  }
2026 
2027 
2044  public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0)
2045  {
2046  global $conf, $langs;
2047 
2048  $error = 0;
2049  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2050 
2051  // Check parameters (if test are wrong here, there is bug into caller)
2052  if ($entrepot <= 0) {
2053  $this->error = 'ErrorBadValueForParameterWarehouse';
2054  return -1;
2055  }
2056  if ($qty == 0) {
2057  $this->error = 'ErrorBadValueForParameterQty';
2058  return -1;
2059  }
2060 
2061  $dispatchstatus = 1;
2062  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2063  $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2064  }
2065 
2066  $now = dol_now();
2067 
2068  $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2069 
2070  if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2071  $this->db->begin();
2072 
2073  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
2074  $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch) VALUES";
2075  $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2076  $sql .= ($eatby ? "'".$this->db->idate($eatby)."'" : "null").", ".($sellby ? "'".$this->db->idate($sellby)."'" : "null").", ".($batch ? "'".$this->db->escape($batch)."'" : "null");
2077  $sql .= ")";
2078 
2079  dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2080  $resql = $this->db->query($sql);
2081  if ($resql) {
2082  if (!$notrigger) {
2083  global $conf, $langs, $user;
2084  // Call trigger
2085  $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2086  if ($result < 0) {
2087  $error++;
2088  }
2089  // End call triggers
2090  }
2091  } else {
2092  $this->error = $this->db->lasterror();
2093  $error++;
2094  }
2095 
2096  // If module stock is enabled and the stock increase is done on purchase order dispatching
2097  if (!$error && $entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
2098  $mouv = new MouvementStock($this->db);
2099  if ($product > 0) {
2100  // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2101  $mouv->origin = &$this;
2102  $mouv->setOrigin($this->element, $this->id);
2103 
2104  // Method change if qty < 0
2105  if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qty < 0) {
2106  $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2107  } else {
2108  $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2109  }
2110 
2111  if ($result < 0) {
2112  $this->error = $mouv->error;
2113  $this->errors = $mouv->errors;
2114  dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2115  $error++;
2116  }
2117  }
2118  }
2119 
2120  if ($error == 0) {
2121  $this->db->commit();
2122  return 1;
2123  } else {
2124  $this->db->rollback();
2125  return -1;
2126  }
2127  } else {
2128  $this->error = 'BadStatusForObject';
2129  return -2;
2130  }
2131  }
2132 
2140  public function deleteline($idline, $notrigger = 0)
2141  {
2142  if ($this->statut == 0) {
2143  $line = new CommandeFournisseurLigne($this->db);
2144 
2145  if ($line->fetch($idline) <= 0) {
2146  return 0;
2147  }
2148 
2149  if ($line->delete($notrigger) > 0) {
2150  $this->update_price(1);
2151  return 1;
2152  } else {
2153  $this->error = $line->error;
2154  $this->errors = $line->errors;
2155  return -1;
2156  }
2157  } else {
2158  return -2;
2159  }
2160  }
2161 
2169  public function delete(User $user, $notrigger = 0)
2170  {
2171  global $langs, $conf;
2172  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2173 
2174  $error = 0;
2175 
2176  $this->db->begin();
2177 
2178  if (empty($notrigger)) {
2179  // Call trigger
2180  $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2181  if ($result < 0) {
2182  $this->errors[] = 'ErrorWhenRunningTrigger';
2183  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2184  $this->db->rollback();
2185  return -1;
2186  }
2187  // End call triggers
2188  }
2189 
2190  // Test we can delete
2191  $this->fetchObjectLinked(null, 'order_supplier');
2192  if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2193  foreach ($this->linkedObjects['reception'] as $element) {
2194  if ($element->statut >= 0) {
2195  $this->errors[] = $langs->trans('ReceptionExist');
2196  $error++;
2197  break;
2198  }
2199  }
2200  }
2201 
2202  $main = MAIN_DB_PREFIX.'commande_fournisseurdet';
2203  $ef = $main."_extrafields";
2204  $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2205  dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2206  if (!$this->db->query($sql)) {
2207  $this->error = $this->db->lasterror();
2208  $this->errors[] = $this->db->lasterror();
2209  $error++;
2210  }
2211 
2212  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2213  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2214  if (!$this->db->query($sql)) {
2215  $this->error = $this->db->lasterror();
2216  $this->errors[] = $this->db->lasterror();
2217  $error++;
2218  }
2219 
2220  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE rowid =".((int) $this->id);
2221  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2222  if ($resql = $this->db->query($sql)) {
2223  if ($this->db->affected_rows($resql) < 1) {
2224  $this->error = $this->db->lasterror();
2225  $this->errors[] = $this->db->lasterror();
2226  $error++;
2227  }
2228  } else {
2229  $this->error = $this->db->lasterror();
2230  $this->errors[] = $this->db->lasterror();
2231  $error++;
2232  }
2233 
2234  // Remove extrafields
2235  if (!$error) {
2236  $result = $this->deleteExtraFields();
2237  if ($result < 0) {
2238  $this->error = 'FailToDeleteExtraFields';
2239  $this->errors[] = 'FailToDeleteExtraFields';
2240  $error++;
2241  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2242  }
2243  }
2244 
2245  // Delete linked object
2246  $res = $this->deleteObjectLinked();
2247  if ($res < 0) {
2248  $this->error = 'FailToDeleteObjectLinked';
2249  $this->errors[] = 'FailToDeleteObjectLinked';
2250  $error++;
2251  }
2252 
2253  if (!$error) {
2254  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2255  $this->deleteEcmFiles();
2256 
2257  // We remove directory
2258  $ref = dol_sanitizeFileName($this->ref);
2259  if ($conf->fournisseur->commande->dir_output) {
2260  $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2261  $file = $dir."/".$ref.".pdf";
2262  if (file_exists($file)) {
2263  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2264  $this->error = 'ErrorFailToDeleteFile';
2265  $this->errors[] = 'ErrorFailToDeleteFile';
2266  $error++;
2267  }
2268  }
2269  if (file_exists($dir)) {
2270  $res = @dol_delete_dir_recursive($dir);
2271  if (!$res) {
2272  $this->error = 'ErrorFailToDeleteDir';
2273  $this->errors[] = 'ErrorFailToDeleteDir';
2274  $error++;
2275  }
2276  }
2277  }
2278  }
2279 
2280  if (!$error) {
2281  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2282  $this->db->commit();
2283  return 1;
2284  } else {
2285  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2286  $this->db->rollback();
2287  return -$error;
2288  }
2289  }
2290 
2291  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2297  public function get_methodes_commande()
2298  {
2299  // phpcs:enable
2300  $sql = "SELECT rowid, libelle";
2301  $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method";
2302  $sql .= " WHERE active = 1";
2303 
2304  $resql = $this->db->query($sql);
2305  if ($resql) {
2306  $i = 0;
2307  $num = $this->db->num_rows($resql);
2308  $this->methodes_commande = array();
2309  while ($i < $num) {
2310  $row = $this->db->fetch_row($resql);
2311 
2312  $this->methodes_commande[$row[0]] = $row[1];
2313 
2314  $i++;
2315  }
2316  return 0;
2317  } else {
2318  return -1;
2319  }
2320  }
2321 
2330  public function getDispachedLines($status = -1)
2331  {
2332  $ret = array();
2333 
2334  // List of already dispatched lines
2335  $sql = "SELECT p.ref, p.label,";
2336  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2337  $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status";
2338  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
2339  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2340  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2341  $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2342  $sql .= " AND cfd.fk_product = p.rowid";
2343  if ($status >= 0) {
2344  $sql .= " AND cfd.status = ".((int) $status);
2345  }
2346  $sql .= " ORDER BY cfd.rowid ASC";
2347 
2348  $resql = $this->db->query($sql);
2349  if ($resql) {
2350  $num = $this->db->num_rows($resql);
2351  $i = 0;
2352 
2353  while ($i < $num) {
2354  $objp = $this->db->fetch_object($resql);
2355  if ($objp) {
2356  $ret[] = array(
2357  'id' => $objp->dispatchedlineid,
2358  'productid' => $objp->fk_product,
2359  'warehouseid' => $objp->warehouse_id,
2360  'qty' => $objp->qty,
2361  );
2362  }
2363 
2364  $i++;
2365  }
2366  } else {
2367  dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2368  }
2369 
2370  return $ret;
2371  }
2372 
2373  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2383  public function Livraison($user, $date, $type, $comment)
2384  {
2385  // phpcs:enable
2386  global $conf, $langs;
2387 
2388  $result = 0;
2389  $error = 0;
2390 
2391  dol_syslog(get_class($this)."::Livraison");
2392 
2393  $usercanreceive = 0;
2394  if (!isModEnabled('reception')) {
2395  $usercanreceive = $user->rights->fournisseur->commande->receptionner;
2396  } else {
2397  $usercanreceive = $user->rights->reception->creer;
2398  }
2399 
2400  if ($usercanreceive) {
2401  // Define the new status
2402  if ($type == 'par') {
2404  } elseif ($type == 'tot') {
2406  } elseif ($type == 'nev') {
2408  } elseif ($type == 'can') {
2410  } else {
2411  $error++;
2412  dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2413  return -2;
2414  }
2415 
2416  // Some checks to accept the record
2417  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2418  // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2419  if (!$error && ($type == 'tot')) {
2420  $dispatchedlinearray = $this->getDispachedLines(0);
2421  if (count($dispatchedlinearray) > 0) {
2422  $result = -1;
2423  $error++;
2424  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2425  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2426  }
2427  }
2428  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)
2429  $dispatcheddenied = $this->getDispachedLines(2);
2430  if (count($dispatchedlinearray) > 0) {
2431  $result = -1;
2432  $error++;
2433  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2434  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2435  }
2436  }
2437  }
2438 
2439  // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2440 
2441  if (empty($error)) {
2442  $this->db->begin();
2443 
2444  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2445  $sql .= " SET fk_statut = ".((int) $statut);
2446  $sql .= " WHERE rowid = ".((int) $this->id);
2447  $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2448 
2449  dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2450  $resql = $this->db->query($sql);
2451  if ($resql) {
2452  $result = 1;
2453  $old_statut = $this->statut;
2454  $this->statut = $statut;
2455  $this->actionmsg2 = $comment;
2456 
2457  // Call trigger
2458  $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2459  if ($result_trigger < 0) {
2460  $error++;
2461  }
2462  // End call triggers
2463 
2464  if (empty($error)) {
2465  $this->db->commit();
2466  } else {
2467  $this->statut = $old_statut;
2468  $this->db->rollback();
2469  $this->error = $this->db->lasterror();
2470  $result = -1;
2471  }
2472  } else {
2473  $this->db->rollback();
2474  $this->error = $this->db->lasterror();
2475  $result = -1;
2476  }
2477  }
2478  } else {
2479  $this->error = $langs->trans('NotAuthorized');
2480  $this->errors[] = $langs->trans('NotAuthorized');
2481  dol_syslog(get_class($this)."::Livraison Not Authorized");
2482  $result = -3;
2483  }
2484  return $result;
2485  }
2486 
2487  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2497  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2498  {
2499  // phpcs:enable
2500  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2501  }
2502 
2511  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2512  {
2513  if ($user->rights->fournisseur->commande->creer || $user->rights->supplier_order->creer) {
2514  $error = 0;
2515 
2516  $this->db->begin();
2517 
2518  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2519  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2520  $sql .= " WHERE rowid = ".((int) $this->id);
2521 
2522  dol_syslog(__METHOD__, LOG_DEBUG);
2523  $resql = $this->db->query($sql);
2524  if (!$resql) {
2525  $this->errors[] = $this->db->error();
2526  $error++;
2527  }
2528 
2529  if (!$error) {
2530  $this->oldcopy = clone $this;
2531  $this->date_livraison = $delivery_date;
2532  $this->delivery_date = $delivery_date;
2533  }
2534 
2535  if (!$notrigger && empty($error)) {
2536  // Call trigger
2537  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2538  if ($result < 0) {
2539  $error++;
2540  }
2541  // End call triggers
2542  }
2543 
2544  if (!$error) {
2545  $this->db->commit();
2546  return 1;
2547  } else {
2548  foreach ($this->errors as $errmsg) {
2549  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2550  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2551  }
2552  $this->db->rollback();
2553  return -1 * $error;
2554  }
2555  } else {
2556  return -2;
2557  }
2558  }
2559 
2560  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2569  public function set_id_projet($user, $id_projet, $notrigger = 0)
2570  {
2571  // phpcs:enable
2572  if ($user->rights->fournisseur->commande->creer || $user->rights->supplier_order->creer) {
2573  $error = 0;
2574 
2575  $this->db->begin();
2576 
2577  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2578  $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2579  $sql .= " WHERE rowid = ".((int) $this->id);
2580 
2581  dol_syslog(__METHOD__, LOG_DEBUG);
2582  $resql = $this->db->query($sql);
2583  if (!$resql) {
2584  $this->errors[] = $this->db->error();
2585  $error++;
2586  }
2587 
2588  if (!$error) {
2589  $this->oldcopy = clone $this;
2590  $this->fk_projet = $id_projet;
2591  $this->fk_project = $id_projet;
2592  }
2593 
2594  if (!$notrigger && empty($error)) {
2595  // Call trigger
2596  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2597  if ($result < 0) {
2598  $error++;
2599  }
2600  // End call triggers
2601  }
2602 
2603  if (!$error) {
2604  $this->db->commit();
2605  return 1;
2606  } else {
2607  foreach ($this->errors as $errmsg) {
2608  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2609  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2610  }
2611  $this->db->rollback();
2612  return -1 * $error;
2613  }
2614  } else {
2615  return -2;
2616  }
2617  }
2618 
2627  public function updateFromCommandeClient($user, $idc, $comclientid)
2628  {
2629  $comclient = new Commande($this->db);
2630  $comclient->fetch($comclientid);
2631 
2632  $this->id = $idc;
2633 
2634  $this->lines = array();
2635 
2636  $num = count($comclient->lines);
2637  for ($i = 0; $i < $num; $i++) {
2638  $prod = new Product($this->db);
2639  $label = '';
2640  $ref = '';
2641  if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2642  $label = $prod->label;
2643  $ref = $prod->ref;
2644  }
2645 
2646  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2647  $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2648  $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2649  $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2650  $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);
2651  $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2652  if ($this->db->query($sql)) {
2653  $this->update_price(1);
2654  }
2655  }
2656 
2657  return 1;
2658  }
2659 
2667  public function setStatus($user, $status)
2668  {
2669  global $conf, $langs;
2670  $error = 0;
2671 
2672  $this->db->begin();
2673 
2674  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2675  $sql .= " SET fk_statut = ".$status;
2676  $sql .= " WHERE rowid = ".((int) $this->id);
2677 
2678  dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2679  $resql = $this->db->query($sql);
2680  if ($resql) {
2681  // Trigger names for each status
2682  $triggerName = array();
2683  $triggerName[0] = 'DRAFT';
2684  $triggerName[1] = 'VALIDATED';
2685  $triggerName[2] = 'APPROVED';
2686  $triggerName[3] = 'ORDERED'; // Ordered
2687  $triggerName[4] = 'RECEIVED_PARTIALLY';
2688  $triggerName[5] = 'RECEIVED_COMPLETELY';
2689  $triggerName[6] = 'CANCELED';
2690  $triggerName[7] = 'CANCELED';
2691  $triggerName[9] = 'REFUSED';
2692 
2693  // Call trigger
2694  $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2695  if ($result < 0) {
2696  $error++;
2697  }
2698  // End call triggers
2699  } else {
2700  $error++;
2701  $this->error = $this->db->lasterror();
2702  dol_syslog(get_class($this)."::setStatus ".$this->error);
2703  }
2704 
2705  if (!$error) {
2706  $this->statut = $status;
2707  $this->db->commit();
2708  return 1;
2709  } else {
2710  $this->db->rollback();
2711  return -1;
2712  }
2713  }
2714 
2738  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 = '')
2739  {
2740  global $mysoc, $conf, $langs;
2741  dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2742  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2743 
2744  $error = 0;
2745 
2746  if ($this->brouillon) {
2747  // Clean parameters
2748  if (empty($qty)) {
2749  $qty = 0;
2750  }
2751  if (empty($info_bits)) {
2752  $info_bits = 0;
2753  }
2754  if (empty($txtva)) {
2755  $txtva = 0;
2756  }
2757  if (empty($txlocaltax1)) {
2758  $txlocaltax1 = 0;
2759  }
2760  if (empty($txlocaltax2)) {
2761  $txlocaltax2 = 0;
2762  }
2763  if (empty($remise)) {
2764  $remise = 0;
2765  }
2766  if (empty($remise_percent)) {
2767  $remise_percent = 0;
2768  }
2769 
2770  $remise_percent = price2num($remise_percent);
2771  $qty = price2num($qty);
2772  if (!$qty) {
2773  $qty = 1;
2774  }
2775  $pu = price2num($pu);
2776  $pu_ht_devise = price2num($pu_ht_devise);
2777  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2778  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2779  }
2780  $txlocaltax1 = price2num($txlocaltax1);
2781  $txlocaltax2 = price2num($txlocaltax2);
2782 
2783  // Check parameters
2784  if ($type < 0) {
2785  return -1;
2786  }
2787  if ($date_start && $date_end && $date_start > $date_end) {
2788  $langs->load("errors");
2789  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2790  return -1;
2791  }
2792 
2793  $this->db->begin();
2794 
2795  // Calcul du total TTC et de la TVA pour la ligne a partir de
2796  // qty, pu, remise_percent et txtva
2797  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2798  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2799 
2800  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2801 
2802  // Clean vat code
2803  $reg = array();
2804  $vat_src_code = '';
2805  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2806  $vat_src_code = $reg[1];
2807  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2808  }
2809 
2810  $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);
2811  $total_ht = $tabprice[0];
2812  $total_tva = $tabprice[1];
2813  $total_ttc = $tabprice[2];
2814  $total_localtax1 = $tabprice[9];
2815  $total_localtax2 = $tabprice[10];
2816  $pu_ht = $tabprice[3];
2817  $pu_tva = $tabprice[4];
2818  $pu_ttc = $tabprice[5];
2819 
2820  // MultiCurrency
2821  $multicurrency_total_ht = $tabprice[16];
2822  $multicurrency_total_tva = $tabprice[17];
2823  $multicurrency_total_ttc = $tabprice[18];
2824  $pu_ht_devise = $tabprice[19];
2825 
2826  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2827  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2828 
2829  //Fetch current line from the database and then clone the object and set it in $oldline property
2830  $this->line = new CommandeFournisseurLigne($this->db);
2831  $this->line->fetch($rowid);
2832 
2833  $oldline = clone $this->line;
2834  $this->line->oldline = $oldline;
2835 
2836  $this->line->context = $this->context;
2837 
2838  $this->line->fk_commande = $this->id;
2839  //$this->line->label=$label;
2840  $this->line->desc = $desc;
2841 
2842  // redefine quantity according to packaging
2843  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
2844  if ($qty < $this->line->packaging) {
2845  $qty = $this->line->packaging;
2846  } else {
2847  if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2848  $coeff = intval($qty / $this->line->packaging) + 1;
2849  $qty = $this->line->packaging * $coeff;
2850  setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2851  }
2852  }
2853  }
2854 
2855  $this->line->qty = $qty;
2856  $this->line->ref_supplier = $ref_supplier;
2857 
2858  $this->line->vat_src_code = $vat_src_code;
2859  $this->line->tva_tx = $txtva;
2860  $this->line->localtax1_tx = $txlocaltax1;
2861  $this->line->localtax2_tx = $txlocaltax2;
2862  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2863  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2864  $this->line->remise_percent = $remise_percent;
2865  $this->line->subprice = $pu_ht;
2866  $this->line->info_bits = $info_bits;
2867  $this->line->total_ht = $total_ht;
2868  $this->line->total_tva = $total_tva;
2869  $this->line->total_localtax1 = $total_localtax1;
2870  $this->line->total_localtax2 = $total_localtax2;
2871  $this->line->total_ttc = $total_ttc;
2872  $this->line->product_type = $type;
2873  $this->line->special_code = $oldline->special_code;
2874  $this->line->rang = $oldline->rang;
2875  $this->line->origin = $this->origin;
2876  $this->line->fk_unit = $fk_unit;
2877 
2878  $this->line->date_start = $date_start;
2879  $this->line->date_end = $date_end;
2880 
2881  // Multicurrency
2882  $this->line->fk_multicurrency = $this->fk_multicurrency;
2883  $this->line->multicurrency_code = $this->multicurrency_code;
2884  $this->line->multicurrency_subprice = $pu_ht_devise;
2885  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2886  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2887  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2888 
2889  $this->line->subprice = $pu_ht;
2890  $this->line->price = $this->line->subprice;
2891 
2892  $this->line->remise_percent = $remise_percent;
2893 
2894  if (is_array($array_options) && count($array_options) > 0) {
2895  // We replace values in this->line->array_options only for entries defined into $array_options
2896  foreach ($array_options as $key => $value) {
2897  $this->line->array_options[$key] = $array_options[$key];
2898  }
2899  }
2900 
2901  $result = $this->line->update($notrigger);
2902 
2903 
2904  // Mise a jour info denormalisees au niveau facture
2905  if ($result >= 0) {
2906  $this->update_price('1', 'auto');
2907  $this->db->commit();
2908  return $result;
2909  } else {
2910  $this->error = $this->db->lasterror();
2911  $this->db->rollback();
2912  return -1;
2913  }
2914  } else {
2915  $this->error = "Order status makes operation forbidden";
2916  dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
2917  return -2;
2918  }
2919  }
2920 
2921 
2929  public function initAsSpecimen()
2930  {
2931  global $user, $langs, $conf;
2932 
2933  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
2934 
2935  dol_syslog(get_class($this)."::initAsSpecimen");
2936 
2937  $now = dol_now();
2938 
2939  // Find first product
2940  $prodid = 0;
2941  $product = new ProductFournisseur($this->db);
2942  $sql = "SELECT rowid";
2943  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2944  $sql .= " WHERE entity IN (".getEntity('product').")";
2945  $sql .= $this->db->order("rowid", "ASC");
2946  $sql .= $this->db->plimit(1);
2947  $resql = $this->db->query($sql);
2948  if ($resql) {
2949  $obj = $this->db->fetch_object($resql);
2950  $prodid = $obj->rowid;
2951  }
2952 
2953  // Initialise parametres
2954  $this->id = 0;
2955  $this->ref = 'SPECIMEN';
2956  $this->specimen = 1;
2957  $this->socid = 1;
2958  $this->date = $now;
2959  $this->date_commande = $now;
2960  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2961  $this->cond_reglement_code = 'RECEP';
2962  $this->mode_reglement_code = 'CHQ';
2963 
2964  $this->note_public = 'This is a comment (public)';
2965  $this->note_private = 'This is a comment (private)';
2966 
2967  $this->multicurrency_tx = 1;
2968  $this->multicurrency_code = $conf->currency;
2969 
2970  $this->statut = 0;
2971 
2972  // Lines
2973  $nbp = 5;
2974  $xnbp = 0;
2975  while ($xnbp < $nbp) {
2976  $line = new CommandeFournisseurLigne($this->db);
2977  $line->desc = $langs->trans("Description")." ".$xnbp;
2978  $line->qty = 1;
2979  $line->subprice = 100;
2980  $line->price = 100;
2981  $line->tva_tx = 19.6;
2982  $line->localtax1_tx = 0;
2983  $line->localtax2_tx = 0;
2984  if ($xnbp == 2) {
2985  $line->total_ht = 50;
2986  $line->total_ttc = 59.8;
2987  $line->total_tva = 9.8;
2988  $line->remise_percent = 50;
2989  } else {
2990  $line->total_ht = 100;
2991  $line->total_ttc = 119.6;
2992  $line->total_tva = 19.6;
2993  $line->remise_percent = 00;
2994  }
2995  $line->fk_product = $prodid;
2996 
2997  $this->lines[$xnbp] = $line;
2998 
2999  $this->total_ht += $line->total_ht;
3000  $this->total_tva += $line->total_tva;
3001  $this->total_ttc += $line->total_ttc;
3002 
3003  $xnbp++;
3004  }
3005  }
3006 
3013  public function info($id)
3014  {
3015  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3016  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3017  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
3018  $sql .= ' WHERE c.rowid = '.((int) $id);
3019 
3020  $result = $this->db->query($sql);
3021  if ($result) {
3022  if ($this->db->num_rows($result)) {
3023  $obj = $this->db->fetch_object($result);
3024  $this->id = $obj->rowid;
3025  if ($obj->fk_user_author) {
3026  $this->user_creation_id = $obj->fk_user_author;
3027  }
3028  if ($obj->fk_user_valid) {
3029  $this->user_validation_id = $obj->fk_user_valid;
3030  }
3031  if ($obj->fk_user_modif) {
3032  $this->user_modification_id = $obj->fk_user_modif;
3033  }
3034  if ($obj->fk_user_approve) {
3035  $this->user_approve_id = $obj->fk_user_approve;
3036  }
3037  if ($obj->fk_user_approve2) {
3038  $this->user_approve_id2 = $obj->fk_user_approve2;
3039  }
3040 
3041  $this->date_creation = $this->db->jdate($obj->datec);
3042  $this->date_modification = $this->db->jdate($obj->datem);
3043  $this->date_approve = $this->db->jdate($obj->datea);
3044  $this->date_approve2 = $this->db->jdate($obj->datea2);
3045  $this->date_validation = $this->db->jdate($obj->date_validation);
3046  }
3047  $this->db->free($result);
3048  } else {
3049  dol_print_error($this->db);
3050  }
3051  }
3052 
3053  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3059  public function load_state_board()
3060  {
3061  // phpcs:enable
3062  global $conf, $user;
3063 
3064  $this->nb = array();
3065  $clause = "WHERE";
3066 
3067  $sql = "SELECT count(co.rowid) as nb";
3068  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
3069  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3070  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3071  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3072  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3073  $clause = "AND";
3074  }
3075  $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3076 
3077  $resql = $this->db->query($sql);
3078  if ($resql) {
3079  while ($obj = $this->db->fetch_object($resql)) {
3080  $this->nb["supplier_orders"] = $obj->nb;
3081  }
3082  $this->db->free($resql);
3083  return 1;
3084  } else {
3085  dol_print_error($this->db);
3086  $this->error = $this->db->error();
3087  return -1;
3088  }
3089  }
3090 
3091  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3099  public function load_board($user, $mode = 'opened')
3100  {
3101  // phpcs:enable
3102  global $conf, $langs;
3103 
3104  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3105  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
3106  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3107  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3108  }
3109  $sql .= " WHERE c.entity = ".$conf->entity;
3110  if ($mode === 'awaiting') {
3111  $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3112  } else {
3113  $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3114  }
3115  if ($user->socid) {
3116  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3117  }
3118 
3119  $resql = $this->db->query($sql);
3120  if ($resql) {
3121  $commandestatic = new CommandeFournisseur($this->db);
3122 
3123  $response = new WorkboardResponse();
3124  $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3125  $response->label = $langs->trans("SuppliersOrdersToProcess");
3126  $response->labelShort = $langs->trans("Opened");
3127  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3128  $response->img = img_object('', "order");
3129 
3130  if ($mode === 'awaiting') {
3131  $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3132  $response->labelShort = $langs->trans("AwaitingReception");
3133  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3134  }
3135 
3136  while ($obj = $this->db->fetch_object($resql)) {
3137  $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3138  $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3139  $commandestatic->statut = $obj->fk_statut;
3140 
3141  $response->nbtodo++;
3142  $response->total += $obj->total_ht;
3143 
3144  if ($commandestatic->hasDelay()) {
3145  $response->nbtodolate++;
3146  }
3147  }
3148 
3149  return $response;
3150  } else {
3151  $this->error = $this->db->error();
3152  return -1;
3153  }
3154  }
3155 
3162  public function getInputMethod()
3163  {
3164  global $db, $langs;
3165 
3166  if ($this->methode_commande_id > 0) {
3167  $sql = "SELECT rowid, code, libelle as label";
3168  $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_method';
3169  $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3170 
3171  $resql = $this->db->query($sql);
3172  if ($resql) {
3173  if ($this->db->num_rows($resql)) {
3174  $obj = $this->db->fetch_object($resql);
3175 
3176  $string = $langs->trans($obj->code);
3177  if ($string == $obj->code) {
3178  $string = $obj->label != '-' ? $obj->label : '';
3179  }
3180  return $string;
3181  }
3182  } else {
3183  dol_print_error($this->db);
3184  }
3185  }
3186 
3187  return '';
3188  }
3189 
3201  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3202  {
3203  global $conf, $langs;
3204 
3205  if (!dol_strlen($modele)) {
3206  $modele = ''; // No doc template/generation by default
3207 
3208  if (!empty($this->model_pdf)) {
3209  $modele = $this->model_pdf;
3210  } elseif (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
3211  $modele = $conf->global->COMMANDE_SUPPLIER_ADDON_PDF;
3212  }
3213  }
3214 
3215  if (empty($modele)) {
3216  return 0;
3217  } else {
3218  $langs->load("suppliers");
3219  $outputlangs->load("products");
3220 
3221  $modelpath = "core/modules/supplier_order/doc/";
3222  $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3223  return $result;
3224  }
3225  }
3226 
3233  public function getMaxDeliveryTimeDay($langs)
3234  {
3235  if (empty($this->lines)) {
3236  return '';
3237  }
3238 
3239  $obj = new ProductFournisseur($this->db);
3240 
3241  $nb = 0;
3242  foreach ($this->lines as $line) {
3243  if ($line->fk_product > 0) {
3244  $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3245  if ($idp) {
3246  $obj->fetch($idp);
3247  if ($obj->delivery_time_days > $nb) {
3248  $nb = $obj->delivery_time_days;
3249  }
3250  }
3251  }
3252  }
3253 
3254  if ($nb === 0) {
3255  return '';
3256  } else {
3257  return $nb.' '.$langs->trans('Days');
3258  }
3259  }
3260 
3265  public function getRights()
3266  {
3267  global $user;
3268 
3269  return $user->rights->fournisseur->commande;
3270  }
3271 
3272 
3281  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3282  {
3283  $tables = array(
3284  'commande_fournisseur'
3285  );
3286 
3287  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3288  }
3289 
3298  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3299  {
3300  $tables = array(
3301  'commande_fournisseurdet'
3302  );
3303 
3304  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3305  }
3306 
3314  public function hasDelay()
3315  {
3316  global $conf;
3317 
3318  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3319  $this->delivery_date = $this->date_livraison; // For backward compatibility
3320  }
3321 
3322  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3323  $now = dol_now();
3324  if (!empty($this->delivery_date)) {
3325  $date_to_test = $this->delivery_date;
3326  return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3327  } else {
3328  //$date_to_test = $this->date_commande;
3329  //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3330  return false;
3331  }
3332  } else {
3333  $now = dol_now();
3334  $date_to_test = $this->date_commande;
3335 
3336  return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3337  }
3338  }
3339 
3347  public function showDelay()
3348  {
3349  global $conf, $langs;
3350 
3351  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3352  $this->delivery_date = $this->date_livraison; // For backward compatibility
3353  }
3354 
3355  $text = '';
3356 
3357  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3358  if (!empty($this->delivery_date)) {
3359  $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3360  } else {
3361  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3362  }
3363  } else {
3364  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3365  }
3366  if ($text) {
3367  $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3368  }
3369 
3370  return $text;
3371  }
3372 
3373 
3382  public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3383  {
3384  global $conf, $langs;
3385 
3386  if ((isModEnabled("fournisseur") && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || isModEnabled("supplier_order")) {
3387  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3388 
3389  $qtydelivered = array();
3390  $qtywished = array();
3391 
3392  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3393  $filter = array('t.fk_commande'=>$this->id);
3394  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3395  $filter['t.status'] = 1; // Restrict to lines with status validated
3396  }
3397 
3398  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3399  if ($ret < 0) {
3400  $this->error = $supplierorderdispatch->error; $this->errors = $supplierorderdispatch->errors;
3401  return $ret;
3402  } else {
3403  if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3404  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3405  $date_liv = dol_now();
3406 
3407  // Build array with quantity deliverd by product
3408  foreach ($supplierorderdispatch->lines as $line) {
3409  $qtydelivered[$line->fk_product] += $line->qty;
3410  }
3411  foreach ($this->lines as $line) {
3412  // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3413  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $line->product_type > 0) {
3414  continue;
3415  }
3416  $qtywished[$line->fk_product] += $line->qty;
3417  }
3418 
3419  //Compare array
3420  $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3421  $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3422  $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3423  //var_dump(array_keys($qtydelivered));
3424  //var_dump(array_keys($qtywished));
3425  //var_dump($diff_array);
3426  //var_dump($keysinwishednotindelivered);
3427  //var_dump($keysindeliverednotinwished);
3428  //exit;
3429 
3430  if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3431  if ($closeopenorder) {
3432  //$ret=$this->setStatus($user,5);
3433  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3434  if ($ret < 0) {
3435  return -1;
3436  }
3437  return 5;
3438  } else {
3439  //Diff => received partially
3440  //$ret=$this->setStatus($user,4);
3441  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3442  if ($ret < 0) {
3443  return -1;
3444  }
3445  return 4;
3446  }
3447  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
3448  //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3449 
3450  $close = 0;
3451 
3452  if (count($diff_array) > 0) {
3453  //there are some difference between the two arrays
3454 
3455  //scan the array of results
3456  foreach ($diff_array as $key => $value) {
3457  //if the quantity delivered is greater or equal to wish quantity
3458  if ($qtydelivered[$key] >= $qtywished[$key]) {
3459  $close++;
3460  }
3461  }
3462  }
3463 
3464 
3465  if ($close == count($diff_array)) {
3466  //all the products are received equal or more than the wished quantity
3467  if ($closeopenorder) {
3468  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3469  if ($ret < 0) {
3470  return -1;
3471  }
3472  return 5;
3473  } else {
3474  //Diff => received partially
3475  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3476  if ($ret < 0) {
3477  return -1;
3478  }
3479  return 4;
3480  }
3481  } else {
3482  //all the products are not received
3483  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3484  if ($ret < 0) {
3485  return -1;
3486  }
3487  return 4;
3488  }
3489  } else {
3490  //Diff => received partially
3491  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3492  if ($ret < 0) {
3493  return -1;
3494  }
3495  return 4;
3496  }
3497  }
3498  return 1;
3499  }
3500  }
3501  return 0;
3502  }
3503 
3511  public function loadReceptions($filtre_statut = -1)
3512  {
3513  $this->receptions = array();
3514 
3515  dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3516 
3517  $sql = 'SELECT cd.rowid, cd.fk_product,';
3518  $sql .= ' sum(cfd.qty) as qty';
3519  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3520  if ($filtre_statut >= 0) {
3521  $sql .= ' '.MAIN_DB_PREFIX.'reception as e,';
3522  }
3523  $sql .= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3524  $sql .= ' WHERE';
3525  if ($filtre_statut >= 0) {
3526  $sql .= ' cfd.fk_reception = e.rowid AND';
3527  }
3528  $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3529  $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3530  if ($this->fk_product > 0) {
3531  $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3532  }
3533  if ($filtre_statut >= 0) {
3534  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3535  }
3536  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3537 
3538  $resql = $this->db->query($sql);
3539  if ($resql) {
3540  $num = $this->db->num_rows($resql);
3541  $i = 0;
3542  while ($i < $num) {
3543  $obj = $this->db->fetch_object($resql);
3544  empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3545  $i++;
3546  }
3547  $this->db->free($resql);
3548 
3549  return $num;
3550  } else {
3551  $this->error = $this->db->lasterror();
3552  return -1;
3553  }
3554  }
3555 }
3556 
3557 
3558 
3563 {
3567  public $element = 'commande_fournisseurdet';
3568 
3572  public $table_element = 'commande_fournisseurdet';
3573 
3574  public $oldline;
3575 
3580  public $fk_commande;
3581 
3582  // From llx_commande_fournisseurdet
3586  public $fk_parent_line;
3587 
3591  public $fk_facture;
3592 
3593  public $rang = 0;
3594  public $special_code = 0;
3595 
3600  public $pu_ht;
3601 
3602  public $date_start;
3603  public $date_end;
3604 
3605  // From llx_product_fournisseur_price
3606 
3611  public $ref_supplier;
3612  public $remise;
3613 
3614 
3620  public function __construct($db)
3621  {
3622  $this->db = $db;
3623  }
3624 
3631  public function fetch($rowid)
3632  {
3633  global $conf;
3634 
3635  $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3636  $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3637  $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3638  $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3639  $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3640  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3641  $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3642  $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3643  $sql .= ' c.fk_soc as socid';
3644  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3645  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3646  $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3647 
3648  $result = $this->db->query($sql);
3649  if ($result) {
3650  $objp = $this->db->fetch_object($result);
3651 
3652  if (!empty($objp)) {
3653  $this->rowid = $objp->rowid;
3654  $this->id = $objp->rowid;
3655  $this->fk_commande = $objp->fk_commande;
3656  $this->desc = $objp->description;
3657  $this->qty = $objp->qty;
3658  $this->ref_fourn = $objp->ref_supplier;
3659  $this->ref_supplier = $objp->ref_supplier;
3660  $this->subprice = $objp->subprice;
3661  $this->tva_tx = $objp->tva_tx;
3662  $this->localtax1_tx = $objp->localtax1_tx;
3663  $this->localtax2_tx = $objp->localtax2_tx;
3664  $this->localtax1_type = $objp->localtax1_type;
3665  $this->localtax2_type = $objp->localtax2_type;
3666  $this->remise = $objp->remise;
3667  $this->remise_percent = $objp->remise_percent;
3668  $this->fk_product = $objp->fk_product;
3669  $this->info_bits = $objp->info_bits;
3670  $this->total_ht = $objp->total_ht;
3671  $this->total_tva = $objp->total_tva;
3672  $this->total_localtax1 = $objp->total_localtax1;
3673  $this->total_localtax2 = $objp->total_localtax2;
3674  $this->total_ttc = $objp->total_ttc;
3675  $this->product_type = $objp->product_type;
3676  $this->special_code = $objp->special_code;
3677 
3678  $this->ref = $objp->product_ref;
3679 
3680  $this->product_ref = $objp->product_ref;
3681  $this->product_label = $objp->product_label;
3682  $this->product_desc = $objp->product_desc;
3683 
3684  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
3685  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3686  // Move this into another method and call it when required.
3687 
3688  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3689  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
3690  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3691  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3692  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3693  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3694  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3695  $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3696  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3697  $sqlsearchpackage .= " LIMIT 1";
3698 
3699  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3700  if ($resqlsearchpackage) {
3701  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3702  if ($objsearchpackage) {
3703  $this->fk_fournprice = $objsearchpackage->rowid;
3704  $this->packaging = $objsearchpackage->packaging;
3705  }
3706  } else {
3707  $this->error = $this->db->lasterror();
3708  return -1;
3709  }
3710  }
3711 
3712  $this->date_start = $this->db->jdate($objp->date_start);
3713  $this->date_end = $this->db->jdate($objp->date_end);
3714  $this->fk_unit = $objp->fk_unit;
3715 
3716  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3717  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3718  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3719  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3720 
3721  $this->fetch_optionals();
3722 
3723  $this->db->free($result);
3724  return 1;
3725  } else {
3726  $this->error = 'Supplier order line with id='.$rowid.' not found';
3727  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3728  return 0;
3729  }
3730  } else {
3731  dol_print_error($this->db);
3732  return -1;
3733  }
3734  }
3735 
3742  public function insert($notrigger = 0)
3743  {
3744  global $conf, $user;
3745 
3746  $error = 0;
3747 
3748  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3749 
3750  // Clean parameters
3751  if (empty($this->tva_tx)) {
3752  $this->tva_tx = 0;
3753  }
3754  if (empty($this->localtax1_tx)) {
3755  $this->localtax1_tx = 0;
3756  }
3757  if (empty($this->localtax2_tx)) {
3758  $this->localtax2_tx = 0;
3759  }
3760  if (empty($this->localtax1_type)) {
3761  $this->localtax1_type = '0';
3762  }
3763  if (empty($this->localtax2_type)) {
3764  $this->localtax2_type = '0';
3765  }
3766  if (empty($this->total_localtax1)) {
3767  $this->total_localtax1 = 0;
3768  }
3769  if (empty($this->total_localtax2)) {
3770  $this->total_localtax2 = 0;
3771  }
3772  if (empty($this->rang)) {
3773  $this->rang = 0;
3774  }
3775  if (empty($this->remise_percent)) {
3776  $this->remise_percent = 0;
3777  }
3778  if (empty($this->info_bits)) {
3779  $this->info_bits = 0;
3780  }
3781  if (empty($this->special_code)) {
3782  $this->special_code = 0;
3783  }
3784  if (empty($this->fk_parent_line)) {
3785  $this->fk_parent_line = 0;
3786  }
3787  if (empty($this->pa_ht)) {
3788  $this->pa_ht = 0;
3789  }
3790 
3791  // Multicurrency
3792  if (!empty($this->multicurrency_code)) {
3793  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3794  }
3795  if (empty($this->fk_multicurrency)) {
3796  $this->multicurrency_code = $conf->currency;
3797  $this->fk_multicurrency = 0;
3798  $this->multicurrency_tx = 1;
3799  }
3800 
3801  // Check parameters
3802  if ($this->product_type < 0) {
3803  return -1;
3804  }
3805 
3806  $this->db->begin();
3807 
3808  // Insertion dans base de la ligne
3809  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3810  $sql .= " (fk_commande, label, description, date_start, date_end,";
3811  $sql .= " fk_product, product_type, special_code, rang,";
3812  $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3813  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3814  $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3815  $sql .= ")";
3816  $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3817  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3818  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3819  if ($this->fk_product) {
3820  $sql .= $this->fk_product.",";
3821  } else {
3822  $sql .= "null,";
3823  }
3824  $sql .= "'".$this->db->escape($this->product_type)."',";
3825  $sql .= "'".$this->db->escape($this->special_code)."',";
3826  $sql .= "'".$this->db->escape($this->rang)."',";
3827  $sql .= "'".$this->db->escape($this->qty)."', ";
3828  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3829  $sql .= " ".price2num($this->tva_tx).", ";
3830  $sql .= " ".price2num($this->localtax1_tx).",";
3831  $sql .= " ".price2num($this->localtax2_tx).",";
3832  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3833  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3834  $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3835  $sql .= " ".price2num($this->total_ht).",";
3836  $sql .= " ".price2num($this->total_tva).",";
3837  $sql .= " ".price2num($this->total_localtax1).",";
3838  $sql .= " ".price2num($this->total_localtax2).",";
3839  $sql .= " ".price2num($this->total_ttc).",";
3840  $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3841  $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3842  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3843  $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3844  $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3845  $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3846  $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3847  $sql .= ")";
3848 
3849  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3850  $resql = $this->db->query($sql);
3851  if ($resql) {
3852  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3853  $this->rowid = $this->id;
3854 
3855  if (!$error) {
3856  $result = $this->insertExtraFields();
3857  if ($result < 0) {
3858  $error++;
3859  }
3860  }
3861 
3862  if (!$error && !$notrigger) {
3863  // Call trigger
3864  $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3865  if ($result < 0) {
3866  $error++;
3867  }
3868  // End call triggers
3869  }
3870 
3871  if (!$error) {
3872  $this->db->commit();
3873  return 1;
3874  }
3875 
3876  foreach ($this->errors as $errmsg) {
3877  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3878  $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
3879  }
3880  $this->db->rollback();
3881  return -1 * $error;
3882  } else {
3883  $this->errors[] = $this->db->error();
3884  $this->db->rollback();
3885  return -2;
3886  }
3887  }
3894  public function update($notrigger = 0)
3895  {
3896  global $conf, $user;
3897 
3898  $error = 0;
3899 
3900  $this->db->begin();
3901 
3902  // Mise a jour ligne en base
3903  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3904  $sql .= " description='".$this->db->escape($this->desc)."'";
3905  $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
3906  $sql .= ", subprice='".price2num($this->subprice)."'";
3907  //$sql.= ",remise='".price2num($remise)."'";
3908  $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
3909 
3910  $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3911  $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
3912  $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
3913  $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
3914  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3915  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3916  $sql .= ", qty='".price2num($this->qty)."'";
3917  $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
3918  $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
3919  $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
3920  $sql .= ", total_ht='".price2num($this->total_ht)."'";
3921  $sql .= ", total_tva='".price2num($this->total_tva)."'";
3922  $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
3923  $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
3924  $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
3925  $sql .= ", product_type=".$this->product_type;
3926  $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
3927  $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
3928 
3929  // Multicurrency
3930  $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3931  $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3932  $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3933  $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3934 
3935  $sql .= " WHERE rowid = ".((int) $this->id);
3936 
3937  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
3938  $resql = $this->db->query($sql);
3939  if ($resql) {
3940  if (!$error) {
3941  $result = $this->insertExtraFields();
3942  if ($result < 0) {
3943  $error++;
3944  }
3945  }
3946 
3947  if (!$error && !$notrigger) {
3948  // Call trigger
3949  $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
3950  if ($result < 0) {
3951  $this->db->rollback();
3952  return -1;
3953  }
3954  // End call triggers
3955  }
3956 
3957  if (!$error) {
3958  $this->db->commit();
3959  return 1;
3960  } else {
3961  $this->db->rollback();
3962  return -1;
3963  }
3964  } else {
3965  $this->error = $this->db->lasterror();
3966  $this->db->rollback();
3967  return -1;
3968  }
3969  }
3970 
3977  public function delete($notrigger = 0)
3978  {
3979  global $user;
3980 
3981  $error = 0;
3982 
3983  $this->db->begin();
3984 
3985  // extrafields
3986  $result = $this->deleteExtraFields();
3987  if ($result < 0) {
3988  $this->db->rollback();
3989  return -1;
3990  }
3991 
3992  $sql1 = 'UPDATE '.MAIN_DB_PREFIX."commandedet SET fk_commandefourndet = NULL WHERE rowid=".((int) $this->id);
3993  $resql = $this->db->query($sql1);
3994  if (!$resql) {
3995  $this->db->rollback();
3996  return -1;
3997  }
3998 
3999  $sql2 = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4000 
4001  dol_syslog(__METHOD__, LOG_DEBUG);
4002  $resql = $this->db->query($sql2);
4003  if ($resql) {
4004  if (!$notrigger) {
4005  // Call trigger
4006  $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4007  if ($result < 0) {
4008  $error++;
4009  }
4010  // End call triggers
4011  }
4012 
4013  if (!$error) {
4014  $this->db->commit();
4015  return 1;
4016  }
4017 
4018  foreach ($this->errors as $errmsg) {
4019  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4020  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4021  }
4022  $this->db->rollback();
4023  return -1 * $error;
4024  } else {
4025  $this->error = $this->db->lasterror();
4026  return -1;
4027  }
4028  }
4029 }
$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.
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.
approve($user, $idwarehouse=0, $secondlevel=0)
Approve a supplier order.
Cancel($user, $idwarehouse=-1)
Cancel an approved order.
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.
dispatchProduct($user, $product, $qty, $entrepot, $price=0, $comment='', $eatby='', $sellby='', $batch='', $fk_commandefourndet=0, $notrigger=0)
Save a receiving into the tracking table of receiving (commande_fournisseur_dispatch) and add product...
createFromClone(User $user, $socid=0, $notrigger=0)
Load an object from its id and create a new one in database.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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.
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.
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 commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product 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 commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty 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:47
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
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:1402
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:1251
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:61
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
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)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages 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.
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.
setEventMessage($mesgs, $style='mesgs')
Set event message 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.
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.
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
$conf db
API class for accounts.
Definition: inc.php:41