dolibarr  x.y.z
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.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->desc = $objp->description;
523  $line->description = $objp->description;
524  $line->qty = $objp->qty;
525  $line->tva_tx = $objp->tva_tx;
526  $line->localtax1_tx = $objp->localtax1_tx;
527  $line->localtax2_tx = $objp->localtax2_tx;
528  $line->localtax1_type = $objp->localtax1_type;
529  $line->localtax2_type = $objp->localtax2_type;
530  $line->subprice = $objp->subprice;
531  $line->pu_ht = $objp->subprice;
532  $line->remise_percent = $objp->remise_percent;
533 
534  $line->vat_src_code = $objp->vat_src_code;
535  $line->total_ht = $objp->total_ht;
536  $line->total_tva = $objp->total_tva;
537  $line->total_localtax1 = $objp->total_localtax1;
538  $line->total_localtax2 = $objp->total_localtax2;
539  $line->total_ttc = $objp->total_ttc;
540  $line->product_type = $objp->product_type;
541 
542  $line->fk_product = $objp->fk_product;
543 
544  $line->libelle = $objp->product_label; // deprecated
545  $line->product_label = $objp->product_label;
546  $line->product_desc = $objp->product_desc;
547  $line->product_tobatch = $objp->product_tobatch;
548  $line->product_barcode = $objp->product_barcode;
549 
550  $line->ref = $objp->product_ref; // Ref of product
551  $line->product_ref = $objp->product_ref; // Ref of product
552  $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
553  $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
554 
555  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
556  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
557  // Move this into another method and call it when required.
558 
559  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
560  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
561  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
562  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
563  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
564  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
565  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
566  $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
567  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
568  $sqlsearchpackage .= " LIMIT 1";
569 
570  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
571  if ($resqlsearchpackage) {
572  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
573  if ($objsearchpackage) {
574  $line->fk_fournprice = $objsearchpackage->rowid;
575  $line->packaging = $objsearchpackage->packaging;
576  }
577  } else {
578  $this->error = $this->db->lasterror();
579  return -1;
580  }
581  }
582 
583  $line->date_start = $this->db->jdate($objp->date_start);
584  $line->date_end = $this->db->jdate($objp->date_end);
585  $line->fk_unit = $objp->fk_unit;
586 
587  // Multicurrency
588  $line->fk_multicurrency = $objp->fk_multicurrency;
589  $line->multicurrency_code = $objp->multicurrency_code;
590  $line->multicurrency_subprice = $objp->multicurrency_subprice;
591  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
592  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
593  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
594 
595  $line->special_code = $objp->special_code;
596  $line->fk_parent_line = $objp->fk_parent_line;
597 
598  $line->rang = $objp->rang;
599 
600  // Retrieve all extrafield
601  // fetch optionals attributes and labels
602  $line->fetch_optionals();
603 
604  $this->lines[$i] = $line;
605 
606  $i++;
607  }
608  $this->db->free($result);
609 
610  return $num;
611  } else {
612  $this->error = $this->db->error()." sql=".$sql;
613  return -1;
614  }
615  }
616 
625  public function valid($user, $idwarehouse = 0, $notrigger = 0)
626  {
627  global $langs, $conf;
628  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
629 
630  $error = 0;
631 
632  dol_syslog(get_class($this)."::valid");
633  $result = 0;
634  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && (!empty($user->rights->fournisseur->commande->creer) || !empty($user->rights->supplier_order->creer)))
635  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->supplier_order_advance->validate))) {
636  $this->db->begin();
637 
638  // Definition of supplier order numbering model name
639  $soc = new Societe($this->db);
640  $soc->fetch($this->fourn_id);
641 
642  // Check if object has a temporary ref
643  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
644  $num = $this->getNextNumRef($soc);
645  } else {
646  $num = $this->ref;
647  }
648  $this->newref = dol_sanitizeFileName($num);
649 
650  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
651  $sql .= " SET ref='".$this->db->escape($num)."',";
652  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
653  $sql .= " date_valid='".$this->db->idate(dol_now())."',";
654  $sql .= " fk_user_valid = ".((int) $user->id);
655  $sql .= " WHERE rowid = ".((int) $this->id);
656  $sql .= " AND fk_statut = ".self::STATUS_DRAFT;
657 
658  $resql = $this->db->query($sql);
659  if (!$resql) {
660  dol_print_error($this->db);
661  $error++;
662  }
663 
664  if (!$error && !$notrigger) {
665  // Call trigger
666  $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
667  if ($result < 0) {
668  $error++;
669  }
670  // End call triggers
671  }
672 
673  if (!$error) {
674  $this->oldref = $this->ref;
675 
676  // Rename directory if dir was a temporary ref
677  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
678  // Now we rename also files into index
679  $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)."'";
680  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
681  $resql = $this->db->query($sql);
682  if (!$resql) {
683  $error++; $this->error = $this->db->lasterror();
684  }
685 
686  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
687  $oldref = dol_sanitizeFileName($this->ref);
688  $newref = dol_sanitizeFileName($num);
689  $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
690  $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
691  if (!$error && file_exists($dirsource)) {
692  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
693 
694  if (@rename($dirsource, $dirdest)) {
695  dol_syslog("Rename ok");
696  // Rename docs starting with $oldref with $newref
697  $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
698  foreach ($listoffiles as $fileentry) {
699  $dirsource = $fileentry['name'];
700  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
701  $dirsource = $fileentry['path'].'/'.$dirsource;
702  $dirdest = $fileentry['path'].'/'.$dirdest;
703  @rename($dirsource, $dirdest);
704  }
705  }
706  }
707  }
708  }
709 
710  if (!$error) {
711  $result = 1;
712  $this->statut = self::STATUS_VALIDATED;
713  $this->ref = $num;
714  }
715 
716  if (!$error) {
717  $this->db->commit();
718  return 1;
719  } else {
720  $this->db->rollback();
721  return -1;
722  }
723  } else {
724  $this->error = 'NotAuthorized';
725  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
726  return -1;
727  }
728  }
729 
736  public function getLibStatut($mode = 0)
737  {
738  return $this->LibStatut($this->statut, $mode, $this->billed);
739  }
740 
741  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
750  public function LibStatut($status, $mode = 0, $billed = 0)
751  {
752  // phpcs:enable
753  global $conf, $langs, $hookmanager;
754 
755  if (empty($this->statuts) || empty($this->statuts_short)) {
756  $langs->load('orders');
757 
758  $this->statuts[0] = 'StatusSupplierOrderDraft';
759  $this->statuts[1] = 'StatusSupplierOrderValidated';
760  $this->statuts[2] = 'StatusSupplierOrderApproved';
761  if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
762  $this->statuts[3] = 'StatusSupplierOrderOnProcess';
763  } else {
764  $this->statuts[3] = 'StatusSupplierOrderOnProcessWithValidation';
765  }
766  $this->statuts[4] = 'StatusSupplierOrderReceivedPartially';
767  $this->statuts[5] = 'StatusSupplierOrderReceivedAll';
768  $this->statuts[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
769  $this->statuts[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
770  $this->statuts[9] = 'StatusSupplierOrderRefused';
771 
772  // List of language codes for status
773  $this->statuts_short[0] = 'StatusSupplierOrderDraftShort';
774  $this->statuts_short[1] = 'StatusSupplierOrderValidatedShort';
775  $this->statuts_short[2] = 'StatusSupplierOrderApprovedShort';
776  $this->statuts_short[3] = 'StatusSupplierOrderOnProcessShort';
777  $this->statuts_short[4] = 'StatusSupplierOrderReceivedPartiallyShort';
778  $this->statuts_short[5] = 'StatusSupplierOrderReceivedAllShort';
779  $this->statuts_short[6] = 'StatusSupplierOrderCanceledShort';
780  $this->statuts_short[7] = 'StatusSupplierOrderCanceledShort';
781  $this->statuts_short[9] = 'StatusSupplierOrderRefusedShort';
782  }
783 
784  $statustrans = array(
785  0 => 'status0',
786  1 => 'status1b',
787  2 => 'status1',
788  3 => 'status4',
789  4 => 'status4b',
790  5 => 'status6',
791  6 => 'status9',
792  7 => 'status9',
793  9 => 'status9',
794  );
795 
796  $statusClass = 'status0';
797  if (!empty($statustrans[$status])) {
798  $statusClass = $statustrans[$status];
799  }
800 
801  $billedtext = '';
802  if ($billed) {
803  $billedtext = ' - '.$langs->trans("Billed");
804  }
805  if ($status == 5 && $billed) {
806  $statusClass = 'status6';
807  }
808 
809  $statusLong = $langs->transnoentitiesnoconv($this->statuts[$status]).$billedtext;
810  $statusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
811 
812  $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
813  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
814  if ($reshook > 0) {
815  return $hookmanager->resPrint;
816  }
817 
818  return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
819  }
820 
821 
832  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
833  {
834  global $langs, $conf, $user, $hookmanager;
835 
836  $result = '';
837 
838  $label = '';
839 
840  if ($user->hasRight("fournisseur", "commande", "read")) {
841  $label = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
842  if (isset($this->statut)) {
843  $label .= ' '.$this->getLibStatut(5);
844  }
845  if (!empty($this->ref)) {
846  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
847  }
848  if (!empty($this->ref_supplier)) {
849  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
850  }
851  if (!empty($this->total_ht)) {
852  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
853  }
854  if (!empty($this->total_tva)) {
855  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
856  }
857  if (!empty($this->total_ttc)) {
858  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
859  }
860  if (!empty($this->date)) {
861  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
862  }
863  if (!empty($this->delivery_date)) {
864  $label .= '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
865  }
866  }
867 
868  $picto = 'order';
869  $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
870 
871  if ($option !== 'nolink') {
872  // Add param to save lastsearch_values or not
873  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
874  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
875  $add_save_lastsearch_values = 1;
876  }
877  if ($add_save_lastsearch_values) {
878  $url .= '&save_lastsearch_values=1';
879  }
880  }
881 
882  $linkclose = '';
883  if (empty($notooltip)) {
884  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
885  $label = $langs->trans("ShowOrder");
886  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
887  }
888  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
889  $linkclose .= ' class="classfortooltip"';
890  }
891 
892  $linkstart = '<a href="'.$url.'"';
893  $linkstart .= $linkclose.'>';
894  $linkend = '</a>';
895 
896  $result .= $linkstart;
897  if ($withpicto) {
898  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
899  }
900  if ($withpicto != 2) {
901  $result .= $this->ref;
902  }
903  $result .= $linkend;
904 
905  if ($addlinktonotes) {
906  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
907  if ($txttoshow) {
908  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
909  $result .= ' <span class="note inline-block">';
910  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
911  $result .= img_picto('', 'note');
912  $result .= '</a>';
913  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
914  //$result.='</a>';
915  $result .= '</span>';
916  }
917  }
918 
919  global $action;
920  $hookmanager->initHooks(array($this->element . 'dao'));
921  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
922  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
923  if ($reshook > 0) {
924  $result = $hookmanager->resPrint;
925  } else {
926  $result .= $hookmanager->resPrint;
927  }
928  return $result;
929  }
930 
931 
939  public function getNextNumRef($soc)
940  {
941  global $db, $langs, $conf;
942  $langs->load("orders");
943 
944  if (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER)) {
945  $mybool = false;
946 
947  $file = $conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER.'.php';
948  $classname = $conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER;
949 
950  // Include file with class
951  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
952 
953  foreach ($dirmodels as $reldir) {
954  $dir = dol_buildpath($reldir."core/modules/supplier_order/");
955 
956  // Load file with numbering class (if found)
957  $mybool |= @include_once $dir.$file;
958  }
959 
960  if ($mybool === false) {
961  dol_print_error('', "Failed to include file ".$file);
962  return '';
963  }
964 
965  $obj = new $classname();
966  $numref = $obj->getNextValue($soc, $this);
967 
968  if ($numref != "") {
969  return $numref;
970  } else {
971  $this->error = $obj->error;
972  return -1;
973  }
974  } else {
975  $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
976  return -2;
977  }
978  }
985  public function classifyBilled(User $user)
986  {
987  $error = 0;
988 
989  if ($this->billed) {
990  return 0;
991  }
992 
993  $this->db->begin();
994 
995  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
996  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
997 
998  if ($this->db->query($sql)) {
999  if (!$error) {
1000  // Call trigger
1001  $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1002  if ($result < 0) {
1003  $error++;
1004  }
1005  // End call triggers
1006  }
1007 
1008  if (!$error) {
1009  $this->billed = 1;
1010 
1011  $this->db->commit();
1012  return 1;
1013  } else {
1014  $this->db->rollback();
1015  return -1;
1016  }
1017  } else {
1018  dol_print_error($this->db);
1019 
1020  $this->db->rollback();
1021  return -1;
1022  }
1023  }
1024 
1033  public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1034  {
1035  global $langs, $conf;
1036  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1037 
1038  $error = 0;
1039 
1040  dol_syslog(get_class($this)."::approve");
1041 
1042  if ($user->rights->fournisseur->commande->approuver) {
1043  $now = dol_now();
1044 
1045  $this->db->begin();
1046 
1047  // Definition of order numbering model name
1048  $soc = new Societe($this->db);
1049  $soc->fetch($this->fourn_id);
1050 
1051  // Check if object has a temporary ref
1052  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1053  $num = $this->getNextNumRef($soc);
1054  } else {
1055  $num = $this->ref;
1056  }
1057  $this->newref = dol_sanitizeFileName($num);
1058 
1059  // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1060  $movetoapprovestatus = true;
1061  $comment = '';
1062 
1063  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1064  $sql .= " SET ref='".$this->db->escape($num)."',";
1065  if (empty($secondlevel)) { // standard or first level approval
1066  $sql .= " date_approve='".$this->db->idate($now)."',";
1067  $sql .= " fk_user_approve = ".$user->id;
1068  if (!empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1069  if (empty($this->user_approve_id2)) {
1070  $movetoapprovestatus = false; // second level approval not done
1071  $comment = ' (first level)';
1072  }
1073  }
1074  } else // request a second level approval
1075  {
1076  $sql .= " date_approve2='".$this->db->idate($now)."',";
1077  $sql .= " fk_user_approve2 = ".((int) $user->id);
1078  if (empty($this->user_approve_id)) {
1079  $movetoapprovestatus = false; // first level approval not done
1080  }
1081  $comment = ' (second level)';
1082  }
1083  // If double approval is required and first approval, we keep status to 1 = validated
1084  if ($movetoapprovestatus) {
1085  $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1086  } else {
1087  $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1088  }
1089  $sql .= " WHERE rowid = ".((int) $this->id);
1090  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1091 
1092  if ($this->db->query($sql)) {
1093  if (!empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT)) {
1094  $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1095  if ($result < 0 && $result != -2) { // -2 means already exists
1096  $error++;
1097  }
1098  }
1099 
1100  // If stock is incremented on validate order, we must increment it
1101  if (!$error && $movetoapprovestatus && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) {
1102  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1103  $langs->load("agenda");
1104 
1105  $cpt = count($this->lines);
1106  for ($i = 0; $i < $cpt; $i++) {
1107  // Product with reference
1108  if ($this->lines[$i]->fk_product > 0) {
1109  $this->line = $this->lines[$i];
1110  $mouvP = new MouvementStock($this->db);
1111  $mouvP->origin = &$this;
1112  $mouvP->setOrigin($this->element, $this->id);
1113  // We decrement stock of product (and sub-products)
1114  $up_ht_disc = $this->lines[$i]->subprice;
1115  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1116  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1117  }
1118  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1119  if ($result < 0) {
1120  $error++;
1121  }
1122  unset($this->line);
1123  }
1124  }
1125  }
1126 
1127  if (!$error) {
1128  // Call trigger
1129  $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1130  if ($result < 0) {
1131  $error++;
1132  }
1133  // End call triggers
1134  }
1135 
1136  if (!$error) {
1137  $this->ref = $this->newref;
1138 
1139  if ($movetoapprovestatus) {
1140  $this->statut = self::STATUS_ACCEPTED;
1141  } else {
1142  $this->statut = self::STATUS_VALIDATED;
1143  }
1144  if (empty($secondlevel)) { // standard or first level approval
1145  $this->date_approve = $now;
1146  $this->user_approve_id = $user->id;
1147  } else // request a second level approval
1148  {
1149  $this->date_approve2 = $now;
1150  $this->user_approve_id2 = $user->id;
1151  }
1152 
1153  $this->db->commit();
1154  return 1;
1155  } else {
1156  $this->db->rollback();
1157  return -1;
1158  }
1159  } else {
1160  $this->db->rollback();
1161  $this->error = $this->db->lasterror();
1162  return -1;
1163  }
1164  } else {
1165  dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1166  }
1167  return -1;
1168  }
1169 
1176  public function refuse($user)
1177  {
1178  global $conf, $langs;
1179 
1180  $error = 0;
1181 
1182  dol_syslog(get_class($this)."::refuse");
1183  $result = 0;
1184  if ($user->rights->fournisseur->commande->approuver) {
1185  $this->db->begin();
1186 
1187  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1188  $sql .= " WHERE rowid = ".((int) $this->id);
1189 
1190  if ($this->db->query($sql)) {
1191  $result = 0;
1192 
1193  if ($error == 0) {
1194  // Call trigger
1195  $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1196  if ($result < 0) {
1197  $error++;
1198  $this->db->rollback();
1199  } else {
1200  $this->db->commit();
1201  }
1202  // End call triggers
1203  }
1204  } else {
1205  $this->db->rollback();
1206  $this->error = $this->db->lasterror();
1207  dol_syslog(get_class($this)."::refuse Error -1");
1208  $result = -1;
1209  }
1210  } else {
1211  dol_syslog(get_class($this)."::refuse Not Authorized");
1212  }
1213  return $result;
1214  }
1215 
1216  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1225  public function Cancel($user, $idwarehouse = -1)
1226  {
1227  // phpcs:enable
1228  global $langs, $conf;
1229 
1230  $error = 0;
1231 
1232  //dol_syslog("CommandeFournisseur::Cancel");
1233  $result = 0;
1234  if ($user->rights->fournisseur->commande->commander) {
1235  $statut = self::STATUS_CANCELED;
1236 
1237  $this->db->begin();
1238 
1239  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".((int) $statut);
1240  $sql .= " WHERE rowid = ".((int) $this->id);
1241  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1242  if ($this->db->query($sql)) {
1243  $result = 0;
1244 
1245  // Call trigger
1246  $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1247  if ($result < 0) {
1248  $error++;
1249  }
1250  // End call triggers
1251 
1252  if ($error == 0) {
1253  $this->db->commit();
1254  return 1;
1255  } else {
1256  $this->db->rollback();
1257  return -1;
1258  }
1259  } else {
1260  $this->db->rollback();
1261  $this->error = $this->db->lasterror();
1262  dol_syslog(get_class($this)."::cancel ".$this->error);
1263  return -1;
1264  }
1265  } else {
1266  dol_syslog(get_class($this)."::cancel Not Authorized");
1267  return -1;
1268  }
1269  }
1270 
1280  public function commande($user, $date, $methode, $comment = '')
1281  {
1282  global $langs;
1283  dol_syslog(get_class($this)."::commande");
1284  $error = 0;
1285  if ($user->rights->fournisseur->commande->commander) {
1286  $this->db->begin();
1287 
1288  $newnoteprivate = $this->note_private;
1289  if ($comment) {
1290  $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1291  }
1292 
1293  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1294  $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1295  $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1296  $sql .= " WHERE rowid=".((int) $this->id);
1297 
1298  dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1299  if ($this->db->query($sql)) {
1300  $this->statut = self::STATUS_ORDERSENT;
1301  $this->methode_commande_id = $methode;
1302  $this->date_commande = $date;
1303  $this->context = array('comments' => $comment);
1304 
1305  // Call trigger
1306  $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1307  if ($result < 0) {
1308  $error++;
1309  }
1310  // End call triggers
1311  } else {
1312  $error++;
1313  $this->error = $this->db->lasterror();
1314  $this->errors[] = $this->db->lasterror();
1315  }
1316 
1317  if (!$error) {
1318  $this->db->commit();
1319  } else {
1320  $this->db->rollback();
1321  }
1322  } else {
1323  $error++;
1324  $this->error = $langs->trans('NotAuthorized');
1325  $this->errors[] = $langs->trans('NotAuthorized');
1326  dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1327  }
1328 
1329  return ($error ? -1 : 1);
1330  }
1331 
1339  public function create($user, $notrigger = 0)
1340  {
1341  global $langs, $conf, $hookmanager;
1342 
1343  $this->db->begin();
1344 
1345  $error = 0;
1346  $now = dol_now();
1347 
1348  // set tmp vars
1349  $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1350  if (empty($date)) {
1351  $date = $now;
1352  }
1353  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1354 
1355  // Clean parameters
1356  if (empty($this->source)) {
1357  $this->source = 0;
1358  }
1359 
1360  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1361  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1362  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1363  } else {
1364  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1365  }
1366  if (empty($this->fk_multicurrency)) {
1367  $this->multicurrency_code = $conf->currency;
1368  $this->fk_multicurrency = 0;
1369  $this->multicurrency_tx = 1;
1370  }
1371 
1372  // We set order into draft status
1373  $this->brouillon = 1;
1374 
1375  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1376  $sql .= "ref";
1377  $sql .= ", ref_supplier";
1378  $sql .= ", note_private";
1379  $sql .= ", note_public";
1380  $sql .= ", entity";
1381  $sql .= ", fk_soc";
1382  $sql .= ", fk_projet";
1383  $sql .= ", date_creation";
1384  $sql .= ", date_livraison";
1385  $sql .= ", fk_user_author";
1386  $sql .= ", fk_statut";
1387  $sql .= ", source";
1388  $sql .= ", model_pdf";
1389  $sql .= ", fk_mode_reglement";
1390  $sql .= ", fk_cond_reglement";
1391  $sql .= ", fk_account";
1392  $sql .= ", fk_incoterms, location_incoterms";
1393  $sql .= ", fk_multicurrency";
1394  $sql .= ", multicurrency_code";
1395  $sql .= ", multicurrency_tx";
1396  $sql .= ") ";
1397  $sql .= " VALUES (";
1398  $sql .= "'(PROV)'";
1399  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
1400  $sql .= ", '".$this->db->escape($this->note_private)."'";
1401  $sql .= ", '".$this->db->escape($this->note_public)."'";
1402  $sql .= ", ".setEntity($this);
1403  $sql .= ", ".((int) $this->socid);
1404  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1405  $sql .= ", '".$this->db->idate($date)."'";
1406  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1407  $sql .= ", ".((int) $user->id);
1408  $sql .= ", ".self::STATUS_DRAFT;
1409  $sql .= ", ".((int) $this->source);
1410  $sql .= ", '".$this->db->escape($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)."'";
1411  $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1412  $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1413  $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1414  $sql .= ", ".(int) $this->fk_incoterms;
1415  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1416  $sql .= ", ".(int) $this->fk_multicurrency;
1417  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1418  $sql .= ", ".(double) $this->multicurrency_tx;
1419  $sql .= ")";
1420 
1421  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1422  if ($this->db->query($sql)) {
1423  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1424 
1425  if ($this->id) {
1426  $num = count($this->lines);
1427 
1428  // insert products details into database
1429  for ($i = 0; $i < $num; $i++) {
1430  $line = $this->lines[$i];
1431  if (!is_object($line)) {
1432  $line = (object) $line;
1433  }
1434 
1435 
1436  //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1437 
1438  // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1439  $result = $this->addline(
1440  $line->desc,
1441  $line->subprice,
1442  $line->qty,
1443  $line->tva_tx,
1444  $line->localtax1_tx,
1445  $line->localtax2_tx,
1446  $line->fk_product,
1447  0,
1448  $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
1449  $line->remise_percent,
1450  'HT',
1451  0,
1452  $line->product_type,
1453  $line->info_bits,
1454  false,
1455  $line->date_start,
1456  $line->date_end,
1457  $line->array_options,
1458  $line->fk_unit,
1459  $line->special_code
1460  );
1461  if ($result < 0) {
1462  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functionnal error
1463  $this->db->rollback();
1464  return -1;
1465  }
1466  }
1467 
1468  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1469  $sql .= " SET ref='(PROV".$this->id.")'";
1470  $sql .= " WHERE rowid=".((int) $this->id);
1471  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1472  if ($this->db->query($sql)) {
1473  // Add link with price request and supplier order
1474  if ($this->id) {
1475  $this->ref = "(PROV".$this->id.")";
1476 
1477  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1478  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1479  }
1480 
1481  // Add object linked
1482  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1483  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1484  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, ...))
1485  foreach ($tmp_origin_id as $origin_id) {
1486  $ret = $this->add_object_linked($origin, $origin_id);
1487  if (!$ret) {
1488  dol_print_error($this->db);
1489  $error++;
1490  }
1491  }
1492  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1493  {
1494  $origin_id = $tmp_origin_id;
1495  $ret = $this->add_object_linked($origin, $origin_id);
1496  if (!$ret) {
1497  dol_print_error($this->db);
1498  $error++;
1499  }
1500  }
1501  }
1502  }
1503  }
1504 
1505  if (!$error) {
1506  $result = $this->insertExtraFields();
1507  if ($result < 0) {
1508  $error++;
1509  }
1510  }
1511 
1512  if (!$error && !$notrigger) {
1513  // Call trigger
1514  $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1515  if ($result < 0) {
1516  $this->db->rollback();
1517  return -1;
1518  }
1519  // End call triggers
1520  }
1521 
1522  $this->db->commit();
1523  return $this->id;
1524  } else {
1525  $this->error = $this->db->lasterror();
1526  $this->db->rollback();
1527  return -2;
1528  }
1529  }
1530  } else {
1531  $this->error = $this->db->lasterror();
1532  $this->db->rollback();
1533  return -1;
1534  }
1535  }
1536 
1544  public function update(User $user, $notrigger = 0)
1545  {
1546  global $conf;
1547 
1548  $error = 0;
1549 
1550  // Clean parameters
1551  if (isset($this->ref)) {
1552  $this->ref = trim($this->ref);
1553  }
1554  if (isset($this->ref_supplier)) {
1555  $this->ref_supplier = trim($this->ref_supplier);
1556  }
1557  if (isset($this->note_private)) {
1558  $this->note_private = trim($this->note_private);
1559  }
1560  if (isset($this->note_public)) {
1561  $this->note_public = trim($this->note_public);
1562  }
1563  if (isset($this->model_pdf)) {
1564  $this->model_pdf = trim($this->model_pdf);
1565  }
1566  if (isset($this->import_key)) {
1567  $this->import_key = trim($this->import_key);
1568  }
1569 
1570  // Update request
1571  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1572 
1573  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1574  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1575  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1576  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1577  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1578  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1579  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1580  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1581  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1582  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1583  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1584  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1585  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1586  $sql .= " fk_user_valid=".(isset($this->user_valid) && $this->user_valid > 0 ? $this->user_valid : "null").",";
1587  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1588  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1589  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1590  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1591  //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1592  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1593  //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1594  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1595  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1596  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1597  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1598 
1599  $sql .= " WHERE rowid=".((int) $this->id);
1600 
1601  $this->db->begin();
1602 
1603  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1604  $resql = $this->db->query($sql);
1605  if (!$resql) {
1606  $error++;
1607  $this->errors[] = "Error ".$this->db->lasterror();
1608  }
1609 
1610  if (!$error) {
1611  $result = $this->insertExtraFields();
1612  if ($result < 0) {
1613  $error++;
1614  }
1615  }
1616 
1617  if (!$error && !$notrigger) {
1618  // Call trigger
1619  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1620  if ($result < 0) {
1621  $error++;
1622  }
1623  // End call triggers
1624  }
1625 
1626  // Commit or rollback
1627  if ($error) {
1628  foreach ($this->errors as $errmsg) {
1629  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1630  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1631  }
1632  $this->db->rollback();
1633  return -1 * $error;
1634  } else {
1635  $this->db->commit();
1636  return 1;
1637  }
1638  }
1639 
1648  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1649  {
1650  global $conf, $user, $hookmanager;
1651 
1652  $error = 0;
1653 
1654  $this->db->begin();
1655 
1656  // get extrafields so they will be clone
1657  foreach ($this->lines as $line) {
1658  $line->fetch_optionals();
1659  }
1660 
1661  // Load source object
1662  $objFrom = clone $this;
1663 
1664  // Change socid if needed
1665  if (!empty($socid) && $socid != $this->socid) {
1666  $objsoc = new Societe($this->db);
1667 
1668  if ($objsoc->fetch($socid) > 0) {
1669  $this->socid = $objsoc->id;
1670  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1671  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1672  $this->fk_project = 0;
1673  $this->fk_delivery_address = 0;
1674  }
1675 
1676  // TODO Change product price if multi-prices
1677  }
1678 
1679  $this->id = 0;
1680  $this->statut = self::STATUS_DRAFT;
1681 
1682  // Clear fields
1683  $this->user_author_id = $user->id;
1684  $this->user_valid = 0;
1685  $this->date_creation = '';
1686  $this->date_validation = '';
1687  $this->ref_supplier = '';
1688  $this->user_approve_id = '';
1689  $this->user_approve_id2 = '';
1690  $this->date_approve = '';
1691  $this->date_approve2 = '';
1692 
1693  // Create clone
1694  $this->context['createfromclone'] = 'createfromclone';
1695  $result = $this->create($user, $notrigger);
1696  if ($result < 0) {
1697  $error++;
1698  }
1699 
1700  if (!$error) {
1701  // Hook of thirdparty module
1702  if (is_object($hookmanager)) {
1703  $parameters = array('objFrom'=>$objFrom);
1704  $action = '';
1705  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1706  if ($reshook < 0) {
1707  $error++;
1708  }
1709  }
1710  }
1711 
1712  unset($this->context['createfromclone']);
1713 
1714  // End
1715  if (!$error) {
1716  $this->db->commit();
1717  return $this->id;
1718  } else {
1719  $this->db->rollback();
1720  return -1;
1721  }
1722  }
1723 
1753  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)
1754  {
1755  global $langs, $mysoc, $conf;
1756 
1757  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");
1758  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1759 
1760  if ($this->statut == self::STATUS_DRAFT) {
1761  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1762 
1763  // Clean parameters
1764  if (empty($qty)) {
1765  $qty = 0;
1766  }
1767  if (!$info_bits) {
1768  $info_bits = 0;
1769  }
1770  if (empty($txtva)) {
1771  $txtva = 0;
1772  }
1773  if (empty($rang)) {
1774  $rang = 0;
1775  }
1776  if (empty($txlocaltax1)) {
1777  $txlocaltax1 = 0;
1778  }
1779  if (empty($txlocaltax2)) {
1780  $txlocaltax2 = 0;
1781  }
1782  if (empty($remise_percent)) {
1783  $remise_percent = 0;
1784  }
1785 
1786  $remise_percent = price2num($remise_percent);
1787  $qty = price2num($qty);
1788  $pu_ht = price2num($pu_ht);
1789  $pu_ht_devise = price2num($pu_ht_devise);
1790  $pu_ttc = price2num($pu_ttc);
1791  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1792  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1793  }
1794  $txlocaltax1 = price2num($txlocaltax1);
1795  $txlocaltax2 = price2num($txlocaltax2);
1796  if ($price_base_type == 'HT') {
1797  $pu = $pu_ht;
1798  } else {
1799  $pu = $pu_ttc;
1800  }
1801  $desc = trim($desc);
1802 
1803  // Check parameters
1804  if ($qty < 0 && !$fk_product) {
1805  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1806  return -1;
1807  }
1808  if ($type < 0) {
1809  return -1;
1810  }
1811  if ($date_start && $date_end && $date_start > $date_end) {
1812  $langs->load("errors");
1813  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1814  return -1;
1815  }
1816 
1817 
1818  $this->db->begin();
1819 
1820  $product_type = $type;
1821  $label = ''; // deprecated
1822 
1823  if ($fk_product > 0) {
1824  if (!empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY)) { // Not the common case
1825  // Check quantity is enough
1826  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);
1827  $prod = new ProductFournisseur($this->db);
1828  if ($prod->fetch($fk_product) > 0) {
1829  $product_type = $prod->type;
1830  $label = $prod->label;
1831 
1832  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1833  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1834  $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
1835 
1836  // If supplier order created from sales order, we take best supplier price
1837  // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1838  if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1839  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1840  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1841  // is remise percent not keyed but present for the product we add it
1842  if ($remise_percent == 0 && $prod->remise_percent != 0) {
1843  $remise_percent = $prod->remise_percent;
1844  }
1845  }
1846  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1847  $langs->load("errors");
1848  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1849  $this->db->rollback();
1850  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1851  //$pu = $prod->fourn_pu; // We do not overwrite unit price
1852  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1853  return -1;
1854  }
1855  if ($result == -1) {
1856  $langs->load("errors");
1857  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1858  $this->db->rollback();
1859  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1860  return -1;
1861  }
1862  if ($result < -1) {
1863  $this->error = $prod->error;
1864  $this->db->rollback();
1865  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1866  return -1;
1867  }
1868  } else {
1869  $this->error = $prod->error;
1870  $this->db->rollback();
1871  return -1;
1872  }
1873  }
1874 
1875  // Predefine quantity according to packaging
1876  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
1877  $prod = new Product($this->db);
1878  $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid));
1879 
1880  if ($qty < $prod->packaging) {
1881  $qty = $prod->packaging;
1882  } else {
1883  if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1884  $coeff = intval($qty / $prod->packaging) + 1;
1885  $qty = $prod->packaging * $coeff;
1886  setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1887  }
1888  }
1889  }
1890  }
1891 
1892  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1893  $pu = 0;
1894  }
1895 
1896  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1897 
1898  // Clean vat code
1899  $reg = array();
1900  $vat_src_code = '';
1901  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1902  $vat_src_code = $reg[1];
1903  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1904  }
1905 
1906  // Calcul du total TTC et de la TVA pour la ligne a partir de
1907  // qty, pu, remise_percent et txtva
1908  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1909  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1910 
1911  $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);
1912 
1913  $total_ht = $tabprice[0];
1914  $total_tva = $tabprice[1];
1915  $total_ttc = $tabprice[2];
1916  $total_localtax1 = $tabprice[9];
1917  $total_localtax2 = $tabprice[10];
1918  $pu = $pu_ht = $tabprice[3];
1919 
1920  // MultiCurrency
1921  $multicurrency_total_ht = $tabprice[16];
1922  $multicurrency_total_tva = $tabprice[17];
1923  $multicurrency_total_ttc = $tabprice[18];
1924  $pu_ht_devise = $tabprice[19];
1925 
1926  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1927  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1928 
1929  if ($rang < 0) {
1930  $rangmax = $this->line_max();
1931  $rang = $rangmax + 1;
1932  }
1933 
1934  // Insert line
1935  $this->line = new CommandeFournisseurLigne($this->db);
1936 
1937  $this->line->context = $this->context;
1938 
1939  $this->line->fk_commande = $this->id;
1940  $this->line->label = $label;
1941  $this->line->ref_fourn = $ref_supplier;
1942  $this->line->ref_supplier = $ref_supplier;
1943  $this->line->desc = $desc;
1944  $this->line->qty = $qty;
1945  $this->line->tva_tx = $txtva;
1946  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1947  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1948  $this->line->localtax1_type = $localtax1_type;
1949  $this->line->localtax2_type = $localtax2_type;
1950  $this->line->fk_product = $fk_product;
1951  $this->line->product_type = $product_type;
1952  $this->line->remise_percent = $remise_percent;
1953  $this->line->subprice = $pu_ht;
1954  $this->line->rang = $rang;
1955  $this->line->info_bits = $info_bits;
1956 
1957  $this->line->vat_src_code = $vat_src_code;
1958  $this->line->total_ht = $total_ht;
1959  $this->line->total_tva = $total_tva;
1960  $this->line->total_localtax1 = $total_localtax1;
1961  $this->line->total_localtax2 = $total_localtax2;
1962  $this->line->total_ttc = $total_ttc;
1963  $this->line->product_type = $type;
1964  $this->line->special_code = (!empty($this->special_code) ? $this->special_code : 0);
1965  $this->line->origin = $origin;
1966  $this->line->origin_id = $origin_id;
1967  $this->line->fk_unit = $fk_unit;
1968 
1969  $this->line->date_start = $date_start;
1970  $this->line->date_end = $date_end;
1971 
1972  // Multicurrency
1973  $this->line->fk_multicurrency = $this->fk_multicurrency;
1974  $this->line->multicurrency_code = $this->multicurrency_code;
1975  $this->line->multicurrency_subprice = $pu_ht_devise;
1976  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1977  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1978  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1979 
1980  $this->line->subprice = $pu_ht;
1981  $this->line->price = $this->line->subprice;
1982 
1983  $this->line->remise_percent = $remise_percent;
1984 
1985  if (is_array($array_options) && count($array_options) > 0) {
1986  $this->line->array_options = $array_options;
1987  }
1988 
1989  $result = $this->line->insert($notrigger);
1990  if ($result > 0) {
1991  // Reorder if child line
1992  if (!empty($fk_parent_line)) {
1993  $this->line_order(true, 'DESC');
1994  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
1995  $linecount = count($this->lines);
1996  for ($ii = $rang; $ii <= $linecount; $ii++) {
1997  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1998  }
1999  }
2000 
2001  // Mise a jour informations denormalisees au niveau de la commande meme
2002  $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.
2003  if ($result > 0) {
2004  $this->db->commit();
2005  return $this->line->id;
2006  } else {
2007  $this->db->rollback();
2008  return -1;
2009  }
2010  } else {
2011  $this->error = $this->line->error;
2012  $this->errors = $this->line->errors;
2013  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2014  $this->db->rollback();
2015  return -1;
2016  }
2017  }
2018  }
2019 
2020 
2037  public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0)
2038  {
2039  global $conf, $langs;
2040 
2041  $error = 0;
2042  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2043 
2044  // Check parameters (if test are wrong here, there is bug into caller)
2045  if ($entrepot <= 0) {
2046  $this->error = 'ErrorBadValueForParameterWarehouse';
2047  return -1;
2048  }
2049  if ($qty == 0) {
2050  $this->error = 'ErrorBadValueForParameterQty';
2051  return -1;
2052  }
2053 
2054  $dispatchstatus = 1;
2055  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2056  $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2057  }
2058 
2059  $now = dol_now();
2060 
2061  $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2062 
2063  if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2064  $this->db->begin();
2065 
2066  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
2067  $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch) VALUES";
2068  $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2069  $sql .= ($eatby ? "'".$this->db->idate($eatby)."'" : "null").", ".($sellby ? "'".$this->db->idate($sellby)."'" : "null").", ".($batch ? "'".$this->db->escape($batch)."'" : "null");
2070  $sql .= ")";
2071 
2072  dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2073  $resql = $this->db->query($sql);
2074  if ($resql) {
2075  if (!$notrigger) {
2076  global $conf, $langs, $user;
2077  // Call trigger
2078  $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2079  if ($result < 0) {
2080  $error++;
2081  }
2082  // End call triggers
2083  }
2084  } else {
2085  $this->error = $this->db->lasterror();
2086  $error++;
2087  }
2088 
2089  // If module stock is enabled and the stock increase is done on purchase order dispatching
2090  if (!$error && $entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
2091  $mouv = new MouvementStock($this->db);
2092  if ($product > 0) {
2093  // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2094  $mouv->origin = &$this;
2095  $mouv->setOrigin($this->element, $this->id);
2096 
2097  // Method change if qty < 0
2098  if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qty < 0) {
2099  $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2100  } else {
2101  $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2102  }
2103 
2104  if ($result < 0) {
2105  $this->error = $mouv->error;
2106  $this->errors = $mouv->errors;
2107  dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2108  $error++;
2109  }
2110  }
2111  }
2112 
2113  if ($error == 0) {
2114  $this->db->commit();
2115  return 1;
2116  } else {
2117  $this->db->rollback();
2118  return -1;
2119  }
2120  } else {
2121  $this->error = 'BadStatusForObject';
2122  return -2;
2123  }
2124  }
2125 
2133  public function deleteline($idline, $notrigger = 0)
2134  {
2135  if ($this->statut == 0) {
2136  $line = new CommandeFournisseurLigne($this->db);
2137 
2138  if ($line->fetch($idline) <= 0) {
2139  return 0;
2140  }
2141 
2142  if ($line->delete($notrigger) > 0) {
2143  $this->update_price(1);
2144  return 1;
2145  } else {
2146  $this->error = $line->error;
2147  $this->errors = $line->errors;
2148  return -1;
2149  }
2150  } else {
2151  return -2;
2152  }
2153  }
2154 
2162  public function delete(User $user, $notrigger = 0)
2163  {
2164  global $langs, $conf;
2165  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2166 
2167  $error = 0;
2168 
2169  $this->db->begin();
2170 
2171  if (empty($notrigger)) {
2172  // Call trigger
2173  $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2174  if ($result < 0) {
2175  $this->errors[] = 'ErrorWhenRunningTrigger';
2176  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2177  $this->db->rollback();
2178  return -1;
2179  }
2180  // End call triggers
2181  }
2182 
2183  // Test we can delete
2184  $this->fetchObjectLinked(null, 'order_supplier');
2185  if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2186  foreach ($this->linkedObjects['reception'] as $element) {
2187  if ($element->statut >= 0) {
2188  $this->errors[] = $langs->trans('ReceptionExist');
2189  $error++;
2190  break;
2191  }
2192  }
2193  }
2194 
2195  $main = MAIN_DB_PREFIX.'commande_fournisseurdet';
2196  $ef = $main."_extrafields";
2197  $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2198  dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2199  if (!$this->db->query($sql)) {
2200  $this->error = $this->db->lasterror();
2201  $this->errors[] = $this->db->lasterror();
2202  $error++;
2203  }
2204 
2205  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2206  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2207  if (!$this->db->query($sql)) {
2208  $this->error = $this->db->lasterror();
2209  $this->errors[] = $this->db->lasterror();
2210  $error++;
2211  }
2212 
2213  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE rowid =".((int) $this->id);
2214  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2215  if ($resql = $this->db->query($sql)) {
2216  if ($this->db->affected_rows($resql) < 1) {
2217  $this->error = $this->db->lasterror();
2218  $this->errors[] = $this->db->lasterror();
2219  $error++;
2220  }
2221  } else {
2222  $this->error = $this->db->lasterror();
2223  $this->errors[] = $this->db->lasterror();
2224  $error++;
2225  }
2226 
2227  // Remove extrafields
2228  if (!$error) {
2229  $result = $this->deleteExtraFields();
2230  if ($result < 0) {
2231  $this->error = 'FailToDeleteExtraFields';
2232  $this->errors[] = 'FailToDeleteExtraFields';
2233  $error++;
2234  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2235  }
2236  }
2237 
2238  // Delete linked object
2239  $res = $this->deleteObjectLinked();
2240  if ($res < 0) {
2241  $this->error = 'FailToDeleteObjectLinked';
2242  $this->errors[] = 'FailToDeleteObjectLinked';
2243  $error++;
2244  }
2245 
2246  if (!$error) {
2247  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2248  $this->deleteEcmFiles();
2249 
2250  // We remove directory
2251  $ref = dol_sanitizeFileName($this->ref);
2252  if ($conf->fournisseur->commande->dir_output) {
2253  $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2254  $file = $dir."/".$ref.".pdf";
2255  if (file_exists($file)) {
2256  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2257  $this->error = 'ErrorFailToDeleteFile';
2258  $this->errors[] = 'ErrorFailToDeleteFile';
2259  $error++;
2260  }
2261  }
2262  if (file_exists($dir)) {
2263  $res = @dol_delete_dir_recursive($dir);
2264  if (!$res) {
2265  $this->error = 'ErrorFailToDeleteDir';
2266  $this->errors[] = 'ErrorFailToDeleteDir';
2267  $error++;
2268  }
2269  }
2270  }
2271  }
2272 
2273  if (!$error) {
2274  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2275  $this->db->commit();
2276  return 1;
2277  } else {
2278  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2279  $this->db->rollback();
2280  return -$error;
2281  }
2282  }
2283 
2284  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2290  public function get_methodes_commande()
2291  {
2292  // phpcs:enable
2293  $sql = "SELECT rowid, libelle";
2294  $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method";
2295  $sql .= " WHERE active = 1";
2296 
2297  $resql = $this->db->query($sql);
2298  if ($resql) {
2299  $i = 0;
2300  $num = $this->db->num_rows($resql);
2301  $this->methodes_commande = array();
2302  while ($i < $num) {
2303  $row = $this->db->fetch_row($resql);
2304 
2305  $this->methodes_commande[$row[0]] = $row[1];
2306 
2307  $i++;
2308  }
2309  return 0;
2310  } else {
2311  return -1;
2312  }
2313  }
2314 
2323  public function getDispachedLines($status = -1)
2324  {
2325  $ret = array();
2326 
2327  // List of already dispatched lines
2328  $sql = "SELECT p.ref, p.label,";
2329  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2330  $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status";
2331  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
2332  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2333  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2334  $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2335  $sql .= " AND cfd.fk_product = p.rowid";
2336  if ($status >= 0) {
2337  $sql .= " AND cfd.status = ".((int) $status);
2338  }
2339  $sql .= " ORDER BY cfd.rowid ASC";
2340 
2341  $resql = $this->db->query($sql);
2342  if ($resql) {
2343  $num = $this->db->num_rows($resql);
2344  $i = 0;
2345 
2346  while ($i < $num) {
2347  $objp = $this->db->fetch_object($resql);
2348  if ($objp) {
2349  $ret[] = array(
2350  'id' => $objp->dispatchedlineid,
2351  'productid' => $objp->fk_product,
2352  'warehouseid' => $objp->warehouse_id,
2353  'qty' => $objp->qty,
2354  );
2355  }
2356 
2357  $i++;
2358  }
2359  } else {
2360  dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2361  }
2362 
2363  return $ret;
2364  }
2365 
2366  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2376  public function Livraison($user, $date, $type, $comment)
2377  {
2378  // phpcs:enable
2379  global $conf, $langs;
2380 
2381  $result = 0;
2382  $error = 0;
2383 
2384  dol_syslog(get_class($this)."::Livraison");
2385 
2386  $usercanreceive = 0;
2387  if (!isModEnabled('reception')) {
2388  $usercanreceive = $user->rights->fournisseur->commande->receptionner;
2389  } else {
2390  $usercanreceive = $user->rights->reception->creer;
2391  }
2392 
2393  if ($usercanreceive) {
2394  // Define the new status
2395  if ($type == 'par') {
2397  } elseif ($type == 'tot') {
2399  } elseif ($type == 'nev') {
2401  } elseif ($type == 'can') {
2403  } else {
2404  $error++;
2405  dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2406  return -2;
2407  }
2408 
2409  // Some checks to accept the record
2410  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2411  // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2412  if (!$error && ($type == 'tot')) {
2413  $dispatchedlinearray = $this->getDispachedLines(0);
2414  if (count($dispatchedlinearray) > 0) {
2415  $result = -1;
2416  $error++;
2417  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2418  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2419  }
2420  }
2421  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)
2422  $dispatcheddenied = $this->getDispachedLines(2);
2423  if (count($dispatchedlinearray) > 0) {
2424  $result = -1;
2425  $error++;
2426  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2427  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2428  }
2429  }
2430  }
2431 
2432  // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2433 
2434  if (empty($error)) {
2435  $this->db->begin();
2436 
2437  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2438  $sql .= " SET fk_statut = ".((int) $statut);
2439  $sql .= " WHERE rowid = ".((int) $this->id);
2440  $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2441 
2442  dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2443  $resql = $this->db->query($sql);
2444  if ($resql) {
2445  $result = 1;
2446  $old_statut = $this->statut;
2447  $this->statut = $statut;
2448  $this->actionmsg2 = $comment;
2449 
2450  // Call trigger
2451  $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2452  if ($result_trigger < 0) {
2453  $error++;
2454  }
2455  // End call triggers
2456 
2457  if (empty($error)) {
2458  $this->db->commit();
2459  } else {
2460  $this->statut = $old_statut;
2461  $this->db->rollback();
2462  $this->error = $this->db->lasterror();
2463  $result = -1;
2464  }
2465  } else {
2466  $this->db->rollback();
2467  $this->error = $this->db->lasterror();
2468  $result = -1;
2469  }
2470  }
2471  } else {
2472  $this->error = $langs->trans('NotAuthorized');
2473  $this->errors[] = $langs->trans('NotAuthorized');
2474  dol_syslog(get_class($this)."::Livraison Not Authorized");
2475  $result = -3;
2476  }
2477  return $result;
2478  }
2479 
2480  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2490  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2491  {
2492  // phpcs:enable
2493  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2494  }
2495 
2504  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2505  {
2506  if ($user->rights->fournisseur->commande->creer || $user->rights->supplier_order->creer) {
2507  $error = 0;
2508 
2509  $this->db->begin();
2510 
2511  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2512  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2513  $sql .= " WHERE rowid = ".((int) $this->id);
2514 
2515  dol_syslog(__METHOD__, LOG_DEBUG);
2516  $resql = $this->db->query($sql);
2517  if (!$resql) {
2518  $this->errors[] = $this->db->error();
2519  $error++;
2520  }
2521 
2522  if (!$error) {
2523  $this->oldcopy = clone $this;
2524  $this->date_livraison = $delivery_date;
2525  $this->delivery_date = $delivery_date;
2526  }
2527 
2528  if (!$notrigger && empty($error)) {
2529  // Call trigger
2530  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2531  if ($result < 0) {
2532  $error++;
2533  }
2534  // End call triggers
2535  }
2536 
2537  if (!$error) {
2538  $this->db->commit();
2539  return 1;
2540  } else {
2541  foreach ($this->errors as $errmsg) {
2542  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2543  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2544  }
2545  $this->db->rollback();
2546  return -1 * $error;
2547  }
2548  } else {
2549  return -2;
2550  }
2551  }
2552 
2553  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2562  public function set_id_projet($user, $id_projet, $notrigger = 0)
2563  {
2564  // phpcs:enable
2565  if ($user->rights->fournisseur->commande->creer || $user->rights->supplier_order->creer) {
2566  $error = 0;
2567 
2568  $this->db->begin();
2569 
2570  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2571  $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2572  $sql .= " WHERE rowid = ".((int) $this->id);
2573 
2574  dol_syslog(__METHOD__, LOG_DEBUG);
2575  $resql = $this->db->query($sql);
2576  if (!$resql) {
2577  $this->errors[] = $this->db->error();
2578  $error++;
2579  }
2580 
2581  if (!$error) {
2582  $this->oldcopy = clone $this;
2583  $this->fk_projet = $id_projet;
2584  $this->fk_project = $id_projet;
2585  }
2586 
2587  if (!$notrigger && empty($error)) {
2588  // Call trigger
2589  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2590  if ($result < 0) {
2591  $error++;
2592  }
2593  // End call triggers
2594  }
2595 
2596  if (!$error) {
2597  $this->db->commit();
2598  return 1;
2599  } else {
2600  foreach ($this->errors as $errmsg) {
2601  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2602  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2603  }
2604  $this->db->rollback();
2605  return -1 * $error;
2606  }
2607  } else {
2608  return -2;
2609  }
2610  }
2611 
2620  public function updateFromCommandeClient($user, $idc, $comclientid)
2621  {
2622  $comclient = new Commande($this->db);
2623  $comclient->fetch($comclientid);
2624 
2625  $this->id = $idc;
2626 
2627  $this->lines = array();
2628 
2629  $num = count($comclient->lines);
2630  for ($i = 0; $i < $num; $i++) {
2631  $prod = new Product($this->db);
2632  $label = '';
2633  $ref = '';
2634  if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2635  $label = $prod->label;
2636  $ref = $prod->ref;
2637  }
2638 
2639  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2640  $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2641  $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2642  $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2643  $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);
2644  $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2645  if ($this->db->query($sql)) {
2646  $this->update_price(1);
2647  }
2648  }
2649 
2650  return 1;
2651  }
2652 
2660  public function setStatus($user, $status)
2661  {
2662  global $conf, $langs;
2663  $error = 0;
2664 
2665  $this->db->begin();
2666 
2667  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2668  $sql .= " SET fk_statut = ".$status;
2669  $sql .= " WHERE rowid = ".((int) $this->id);
2670 
2671  dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2672  $resql = $this->db->query($sql);
2673  if ($resql) {
2674  // Trigger names for each status
2675  $triggerName = array();
2676  $triggerName[0] = 'DRAFT';
2677  $triggerName[1] = 'VALIDATED';
2678  $triggerName[2] = 'APPROVED';
2679  $triggerName[3] = 'ORDERED'; // Ordered
2680  $triggerName[4] = 'RECEIVED_PARTIALLY';
2681  $triggerName[5] = 'RECEIVED_COMPLETELY';
2682  $triggerName[6] = 'CANCELED';
2683  $triggerName[7] = 'CANCELED';
2684  $triggerName[9] = 'REFUSED';
2685 
2686  // Call trigger
2687  $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2688  if ($result < 0) {
2689  $error++;
2690  }
2691  // End call triggers
2692  } else {
2693  $error++;
2694  $this->error = $this->db->lasterror();
2695  dol_syslog(get_class($this)."::setStatus ".$this->error);
2696  }
2697 
2698  if (!$error) {
2699  $this->statut = $status;
2700  $this->db->commit();
2701  return 1;
2702  } else {
2703  $this->db->rollback();
2704  return -1;
2705  }
2706  }
2707 
2731  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 = '')
2732  {
2733  global $mysoc, $conf, $langs;
2734  dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2735  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2736 
2737  $error = 0;
2738 
2739  if ($this->brouillon) {
2740  // Clean parameters
2741  if (empty($qty)) {
2742  $qty = 0;
2743  }
2744  if (empty($info_bits)) {
2745  $info_bits = 0;
2746  }
2747  if (empty($txtva)) {
2748  $txtva = 0;
2749  }
2750  if (empty($txlocaltax1)) {
2751  $txlocaltax1 = 0;
2752  }
2753  if (empty($txlocaltax2)) {
2754  $txlocaltax2 = 0;
2755  }
2756  if (empty($remise)) {
2757  $remise = 0;
2758  }
2759  if (empty($remise_percent)) {
2760  $remise_percent = 0;
2761  }
2762 
2763  $remise_percent = price2num($remise_percent);
2764  $qty = price2num($qty);
2765  if (!$qty) {
2766  $qty = 1;
2767  }
2768  $pu = price2num($pu);
2769  $pu_ht_devise = price2num($pu_ht_devise);
2770  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2771  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2772  }
2773  $txlocaltax1 = price2num($txlocaltax1);
2774  $txlocaltax2 = price2num($txlocaltax2);
2775 
2776  // Check parameters
2777  if ($type < 0) {
2778  return -1;
2779  }
2780  if ($date_start && $date_end && $date_start > $date_end) {
2781  $langs->load("errors");
2782  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2783  return -1;
2784  }
2785 
2786  $this->db->begin();
2787 
2788  // Calcul du total TTC et de la TVA pour la ligne a partir de
2789  // qty, pu, remise_percent et txtva
2790  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2791  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2792 
2793  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2794 
2795  // Clean vat code
2796  $reg = array();
2797  $vat_src_code = '';
2798  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2799  $vat_src_code = $reg[1];
2800  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2801  }
2802 
2803  $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);
2804  $total_ht = $tabprice[0];
2805  $total_tva = $tabprice[1];
2806  $total_ttc = $tabprice[2];
2807  $total_localtax1 = $tabprice[9];
2808  $total_localtax2 = $tabprice[10];
2809  $pu_ht = $tabprice[3];
2810  $pu_tva = $tabprice[4];
2811  $pu_ttc = $tabprice[5];
2812 
2813  // MultiCurrency
2814  $multicurrency_total_ht = $tabprice[16];
2815  $multicurrency_total_tva = $tabprice[17];
2816  $multicurrency_total_ttc = $tabprice[18];
2817  $pu_ht_devise = $tabprice[19];
2818 
2819  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2820  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2821 
2822  //Fetch current line from the database and then clone the object and set it in $oldline property
2823  $this->line = new CommandeFournisseurLigne($this->db);
2824  $this->line->fetch($rowid);
2825 
2826  $oldline = clone $this->line;
2827  $this->line->oldline = $oldline;
2828 
2829  $this->line->context = $this->context;
2830 
2831  $this->line->fk_commande = $this->id;
2832  //$this->line->label=$label;
2833  $this->line->desc = $desc;
2834 
2835  // redefine quantity according to packaging
2836  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
2837  if ($qty < $this->line->packaging) {
2838  $qty = $this->line->packaging;
2839  } else {
2840  if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2841  $coeff = intval($qty / $this->line->packaging) + 1;
2842  $qty = $this->line->packaging * $coeff;
2843  setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2844  }
2845  }
2846  }
2847 
2848  $this->line->qty = $qty;
2849  $this->line->ref_supplier = $ref_supplier;
2850 
2851  $this->line->vat_src_code = $vat_src_code;
2852  $this->line->tva_tx = $txtva;
2853  $this->line->localtax1_tx = $txlocaltax1;
2854  $this->line->localtax2_tx = $txlocaltax2;
2855  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2856  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2857  $this->line->remise_percent = $remise_percent;
2858  $this->line->subprice = $pu_ht;
2859  $this->line->rang = $this->rang;
2860  $this->line->info_bits = $info_bits;
2861  $this->line->total_ht = $total_ht;
2862  $this->line->total_tva = $total_tva;
2863  $this->line->total_localtax1 = $total_localtax1;
2864  $this->line->total_localtax2 = $total_localtax2;
2865  $this->line->total_ttc = $total_ttc;
2866  $this->line->product_type = $type;
2867  $this->line->special_code = (!empty($this->special_code) ? $this->special_code : 0);
2868  $this->line->origin = $this->origin;
2869  $this->line->fk_unit = $fk_unit;
2870 
2871  $this->line->date_start = $date_start;
2872  $this->line->date_end = $date_end;
2873 
2874  // Multicurrency
2875  $this->line->fk_multicurrency = $this->fk_multicurrency;
2876  $this->line->multicurrency_code = $this->multicurrency_code;
2877  $this->line->multicurrency_subprice = $pu_ht_devise;
2878  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2879  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2880  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2881 
2882  $this->line->subprice = $pu_ht;
2883  $this->line->price = $this->line->subprice;
2884 
2885  $this->line->remise_percent = $remise_percent;
2886 
2887  if (is_array($array_options) && count($array_options) > 0) {
2888  // We replace values in this->line->array_options only for entries defined into $array_options
2889  foreach ($array_options as $key => $value) {
2890  $this->line->array_options[$key] = $array_options[$key];
2891  }
2892  }
2893 
2894  $result = $this->line->update($notrigger);
2895 
2896 
2897  // Mise a jour info denormalisees au niveau facture
2898  if ($result >= 0) {
2899  $this->update_price('1', 'auto');
2900  $this->db->commit();
2901  return $result;
2902  } else {
2903  $this->error = $this->db->lasterror();
2904  $this->db->rollback();
2905  return -1;
2906  }
2907  } else {
2908  $this->error = "Order status makes operation forbidden";
2909  dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
2910  return -2;
2911  }
2912  }
2913 
2914 
2922  public function initAsSpecimen()
2923  {
2924  global $user, $langs, $conf;
2925 
2926  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
2927 
2928  dol_syslog(get_class($this)."::initAsSpecimen");
2929 
2930  $now = dol_now();
2931 
2932  // Find first product
2933  $prodid = 0;
2934  $product = new ProductFournisseur($this->db);
2935  $sql = "SELECT rowid";
2936  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2937  $sql .= " WHERE entity IN (".getEntity('product').")";
2938  $sql .= $this->db->order("rowid", "ASC");
2939  $sql .= $this->db->plimit(1);
2940  $resql = $this->db->query($sql);
2941  if ($resql) {
2942  $obj = $this->db->fetch_object($resql);
2943  $prodid = $obj->rowid;
2944  }
2945 
2946  // Initialise parametres
2947  $this->id = 0;
2948  $this->ref = 'SPECIMEN';
2949  $this->specimen = 1;
2950  $this->socid = 1;
2951  $this->date = $now;
2952  $this->date_commande = $now;
2953  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2954  $this->cond_reglement_code = 'RECEP';
2955  $this->mode_reglement_code = 'CHQ';
2956 
2957  $this->note_public = 'This is a comment (public)';
2958  $this->note_private = 'This is a comment (private)';
2959 
2960  $this->multicurrency_tx = 1;
2961  $this->multicurrency_code = $conf->currency;
2962 
2963  $this->statut = 0;
2964 
2965  // Lines
2966  $nbp = 5;
2967  $xnbp = 0;
2968  while ($xnbp < $nbp) {
2969  $line = new CommandeFournisseurLigne($this->db);
2970  $line->desc = $langs->trans("Description")." ".$xnbp;
2971  $line->qty = 1;
2972  $line->subprice = 100;
2973  $line->price = 100;
2974  $line->tva_tx = 19.6;
2975  $line->localtax1_tx = 0;
2976  $line->localtax2_tx = 0;
2977  if ($xnbp == 2) {
2978  $line->total_ht = 50;
2979  $line->total_ttc = 59.8;
2980  $line->total_tva = 9.8;
2981  $line->remise_percent = 50;
2982  } else {
2983  $line->total_ht = 100;
2984  $line->total_ttc = 119.6;
2985  $line->total_tva = 19.6;
2986  $line->remise_percent = 00;
2987  }
2988  $line->fk_product = $prodid;
2989 
2990  $this->lines[$xnbp] = $line;
2991 
2992  $this->total_ht += $line->total_ht;
2993  $this->total_tva += $line->total_tva;
2994  $this->total_ttc += $line->total_ttc;
2995 
2996  $xnbp++;
2997  }
2998  }
2999 
3006  public function info($id)
3007  {
3008  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3009  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3010  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
3011  $sql .= ' WHERE c.rowid = '.((int) $id);
3012 
3013  $result = $this->db->query($sql);
3014  if ($result) {
3015  if ($this->db->num_rows($result)) {
3016  $obj = $this->db->fetch_object($result);
3017  $this->id = $obj->rowid;
3018  if ($obj->fk_user_author) {
3019  $this->user_creation_id = $obj->fk_user_author;
3020  }
3021  if ($obj->fk_user_valid) {
3022  $this->user_validation_id = $obj->fk_user_valid;
3023  }
3024  if ($obj->fk_user_modif) {
3025  $this->user_modification_id = $obj->fk_user_modif;
3026  }
3027  if ($obj->fk_user_approve) {
3028  $this->user_approve_id = $obj->fk_user_approve;
3029  }
3030  if ($obj->fk_user_approve2) {
3031  $this->user_approve_id2 = $obj->fk_user_approve2;
3032  }
3033 
3034  $this->date_creation = $this->db->jdate($obj->datec);
3035  $this->date_modification = $this->db->jdate($obj->datem);
3036  $this->date_approve = $this->db->jdate($obj->datea);
3037  $this->date_approve2 = $this->db->jdate($obj->datea2);
3038  $this->date_validation = $this->db->jdate($obj->date_validation);
3039  }
3040  $this->db->free($result);
3041  } else {
3042  dol_print_error($this->db);
3043  }
3044  }
3045 
3046  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3052  public function load_state_board()
3053  {
3054  // phpcs:enable
3055  global $conf, $user;
3056 
3057  $this->nb = array();
3058  $clause = "WHERE";
3059 
3060  $sql = "SELECT count(co.rowid) as nb";
3061  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
3062  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3063  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3064  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3065  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3066  $clause = "AND";
3067  }
3068  $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3069 
3070  $resql = $this->db->query($sql);
3071  if ($resql) {
3072  while ($obj = $this->db->fetch_object($resql)) {
3073  $this->nb["supplier_orders"] = $obj->nb;
3074  }
3075  $this->db->free($resql);
3076  return 1;
3077  } else {
3078  dol_print_error($this->db);
3079  $this->error = $this->db->error();
3080  return -1;
3081  }
3082  }
3083 
3084  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3092  public function load_board($user, $mode = 'opened')
3093  {
3094  // phpcs:enable
3095  global $conf, $langs;
3096 
3097  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3098  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
3099  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3100  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3101  }
3102  $sql .= " WHERE c.entity = ".$conf->entity;
3103  if ($mode === 'awaiting') {
3104  $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3105  } else {
3106  $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3107  }
3108  if ($user->socid) {
3109  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3110  }
3111 
3112  $resql = $this->db->query($sql);
3113  if ($resql) {
3114  $commandestatic = new CommandeFournisseur($this->db);
3115 
3116  $response = new WorkboardResponse();
3117  $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3118  $response->label = $langs->trans("SuppliersOrdersToProcess");
3119  $response->labelShort = $langs->trans("Opened");
3120  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3121  $response->img = img_object('', "order");
3122 
3123  if ($mode === 'awaiting') {
3124  $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3125  $response->labelShort = $langs->trans("AwaitingReception");
3126  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3127  }
3128 
3129  while ($obj = $this->db->fetch_object($resql)) {
3130  $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3131  $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3132  $commandestatic->statut = $obj->fk_statut;
3133 
3134  $response->nbtodo++;
3135  $response->total += $obj->total_ht;
3136 
3137  if ($commandestatic->hasDelay()) {
3138  $response->nbtodolate++;
3139  }
3140  }
3141 
3142  return $response;
3143  } else {
3144  $this->error = $this->db->error();
3145  return -1;
3146  }
3147  }
3148 
3155  public function getInputMethod()
3156  {
3157  global $db, $langs;
3158 
3159  if ($this->methode_commande_id > 0) {
3160  $sql = "SELECT rowid, code, libelle as label";
3161  $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_method';
3162  $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3163 
3164  $resql = $this->db->query($sql);
3165  if ($resql) {
3166  if ($this->db->num_rows($resql)) {
3167  $obj = $this->db->fetch_object($resql);
3168 
3169  $string = $langs->trans($obj->code);
3170  if ($string == $obj->code) {
3171  $string = $obj->label != '-' ? $obj->label : '';
3172  }
3173  return $string;
3174  }
3175  } else {
3176  dol_print_error($this->db);
3177  }
3178  }
3179 
3180  return '';
3181  }
3182 
3194  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3195  {
3196  global $conf, $langs;
3197 
3198  if (!dol_strlen($modele)) {
3199  $modele = ''; // No doc template/generation by default
3200 
3201  if (!empty($this->model_pdf)) {
3202  $modele = $this->model_pdf;
3203  } elseif (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
3204  $modele = $conf->global->COMMANDE_SUPPLIER_ADDON_PDF;
3205  }
3206  }
3207 
3208  if (empty($modele)) {
3209  return 0;
3210  } else {
3211  $langs->load("suppliers");
3212  $outputlangs->load("products");
3213 
3214  $modelpath = "core/modules/supplier_order/doc/";
3215  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3216  }
3217  }
3218 
3225  public function getMaxDeliveryTimeDay($langs)
3226  {
3227  if (empty($this->lines)) {
3228  return '';
3229  }
3230 
3231  $obj = new ProductFournisseur($this->db);
3232 
3233  $nb = 0;
3234  foreach ($this->lines as $line) {
3235  if ($line->fk_product > 0) {
3236  $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3237  if ($idp) {
3238  $obj->fetch($idp);
3239  if ($obj->delivery_time_days > $nb) {
3240  $nb = $obj->delivery_time_days;
3241  }
3242  }
3243  }
3244  }
3245 
3246  if ($nb === 0) {
3247  return '';
3248  } else {
3249  return $nb.' '.$langs->trans('Days');
3250  }
3251  }
3252 
3257  public function getRights()
3258  {
3259  global $user;
3260 
3261  return $user->rights->fournisseur->commande;
3262  }
3263 
3264 
3273  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3274  {
3275  $tables = array(
3276  'commande_fournisseur'
3277  );
3278 
3279  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3280  }
3281 
3290  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3291  {
3292  $tables = array(
3293  'commande_fournisseurdet'
3294  );
3295 
3296  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3297  }
3298 
3306  public function hasDelay()
3307  {
3308  global $conf;
3309 
3310  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3311  $this->delivery_date = $this->date_livraison; // For backward compatibility
3312  }
3313 
3314  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3315  $now = dol_now();
3316  if (!empty($this->delivery_date)) {
3317  $date_to_test = $this->delivery_date;
3318  return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3319  } else {
3320  //$date_to_test = $this->date_commande;
3321  //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3322  return false;
3323  }
3324  } else {
3325  $now = dol_now();
3326  $date_to_test = $this->date_commande;
3327 
3328  return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3329  }
3330  }
3331 
3339  public function showDelay()
3340  {
3341  global $conf, $langs;
3342 
3343  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3344  $this->delivery_date = $this->date_livraison; // For backward compatibility
3345  }
3346 
3347  $text = '';
3348 
3349  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3350  if (!empty($this->delivery_date)) {
3351  $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3352  } else {
3353  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3354  }
3355  } else {
3356  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3357  }
3358  if ($text) {
3359  $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3360  }
3361 
3362  return $text;
3363  }
3364 
3365 
3374  public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3375  {
3376  global $conf, $langs;
3377 
3378  if ((isModEnabled("fournisseur") && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || isModEnabled("supplier_order")) {
3379  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3380 
3381  $qtydelivered = array();
3382  $qtywished = array();
3383 
3384  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3385  $filter = array('t.fk_commande'=>$this->id);
3386  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3387  $filter['t.status'] = 1; // Restrict to lines with status validated
3388  }
3389 
3390  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3391  if ($ret < 0) {
3392  $this->error = $supplierorderdispatch->error; $this->errors = $supplierorderdispatch->errors;
3393  return $ret;
3394  } else {
3395  if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3396  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3397  $date_liv = dol_now();
3398 
3399  // Build array with quantity deliverd by product
3400  foreach ($supplierorderdispatch->lines as $line) {
3401  $qtydelivered[$line->fk_product] += $line->qty;
3402  }
3403  foreach ($this->lines as $line) {
3404  // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3405  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $line->product_type > 0) {
3406  continue;
3407  }
3408  $qtywished[$line->fk_product] += $line->qty;
3409  }
3410 
3411  //Compare array
3412  $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3413  $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3414  $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3415  //var_dump(array_keys($qtydelivered));
3416  //var_dump(array_keys($qtywished));
3417  //var_dump($diff_array);
3418  //var_dump($keysinwishednotindelivered);
3419  //var_dump($keysindeliverednotinwished);
3420  //exit;
3421 
3422  if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3423  if ($closeopenorder) {
3424  //$ret=$this->setStatus($user,5);
3425  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3426  if ($ret < 0) {
3427  return -1;
3428  }
3429  return 5;
3430  } else {
3431  //Diff => received partially
3432  //$ret=$this->setStatus($user,4);
3433  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3434  if ($ret < 0) {
3435  return -1;
3436  }
3437  return 4;
3438  }
3439  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
3440  //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3441 
3442  $close = 0;
3443 
3444  if (count($diff_array) > 0) {
3445  //there are some difference between the two arrays
3446 
3447  //scan the array of results
3448  foreach ($diff_array as $key => $value) {
3449  //if the quantity delivered is greater or equal to wish quantity
3450  if ($qtydelivered[$key] >= $qtywished[$key]) {
3451  $close++;
3452  }
3453  }
3454  }
3455 
3456 
3457  if ($close == count($diff_array)) {
3458  //all the products are received equal or more than the wished quantity
3459  if ($closeopenorder) {
3460  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3461  if ($ret < 0) {
3462  return -1;
3463  }
3464  return 5;
3465  } else {
3466  //Diff => received partially
3467  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3468  if ($ret < 0) {
3469  return -1;
3470  }
3471  return 4;
3472  }
3473  } else {
3474  //all the products are not received
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  //Diff => received partially
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  }
3490  return 1;
3491  }
3492  }
3493  return 0;
3494  }
3495 
3503  public function loadReceptions($filtre_statut = -1)
3504  {
3505  $this->receptions = array();
3506 
3507  dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3508 
3509  $sql = 'SELECT cd.rowid, cd.fk_product,';
3510  $sql .= ' sum(cfd.qty) as qty';
3511  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3512  if ($filtre_statut >= 0) {
3513  $sql .= ' '.MAIN_DB_PREFIX.'reception as e,';
3514  }
3515  $sql .= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3516  $sql .= ' WHERE';
3517  if ($filtre_statut >= 0) {
3518  $sql .= ' cfd.fk_reception = e.rowid AND';
3519  }
3520  $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3521  $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3522  if ($this->fk_product > 0) {
3523  $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3524  }
3525  if ($filtre_statut >= 0) {
3526  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3527  }
3528  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3529 
3530  $resql = $this->db->query($sql);
3531  if ($resql) {
3532  $num = $this->db->num_rows($resql);
3533  $i = 0;
3534  while ($i < $num) {
3535  $obj = $this->db->fetch_object($resql);
3536  empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3537  $i++;
3538  }
3539  $this->db->free($resql);
3540 
3541  return $num;
3542  } else {
3543  $this->error = $this->db->lasterror();
3544  return -1;
3545  }
3546  }
3547 }
3548 
3549 
3550 
3555 {
3559  public $element = 'commande_fournisseurdet';
3560 
3564  public $table_element = 'commande_fournisseurdet';
3565 
3566  public $oldline;
3567 
3572  public $fk_commande;
3573 
3574  // From llx_commande_fournisseurdet
3578  public $fk_parent_line;
3579 
3583  public $fk_facture;
3584 
3585  public $rang = 0;
3586  public $special_code = 0;
3587 
3592  public $pu_ht;
3593 
3594  public $date_start;
3595  public $date_end;
3596 
3597  // From llx_product_fournisseur_price
3598 
3603  public $ref_supplier;
3604  public $remise;
3605 
3606 
3612  public function __construct($db)
3613  {
3614  $this->db = $db;
3615  }
3616 
3623  public function fetch($rowid)
3624  {
3625  global $conf;
3626 
3627  $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3628  $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3629  $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3630  $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3631  $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3632  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3633  $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3634  $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3635  $sql .= ' c.fk_soc as socid';
3636  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3637  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3638  $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3639 
3640  $result = $this->db->query($sql);
3641  if ($result) {
3642  $objp = $this->db->fetch_object($result);
3643 
3644  if (!empty($objp)) {
3645  $this->rowid = $objp->rowid;
3646  $this->id = $objp->rowid;
3647  $this->fk_commande = $objp->fk_commande;
3648  $this->desc = $objp->description;
3649  $this->qty = $objp->qty;
3650  $this->ref_fourn = $objp->ref_supplier;
3651  $this->ref_supplier = $objp->ref_supplier;
3652  $this->subprice = $objp->subprice;
3653  $this->tva_tx = $objp->tva_tx;
3654  $this->localtax1_tx = $objp->localtax1_tx;
3655  $this->localtax2_tx = $objp->localtax2_tx;
3656  $this->localtax1_type = $objp->localtax1_type;
3657  $this->localtax2_type = $objp->localtax2_type;
3658  $this->remise = $objp->remise;
3659  $this->remise_percent = $objp->remise_percent;
3660  $this->fk_product = $objp->fk_product;
3661  $this->info_bits = $objp->info_bits;
3662  $this->total_ht = $objp->total_ht;
3663  $this->total_tva = $objp->total_tva;
3664  $this->total_localtax1 = $objp->total_localtax1;
3665  $this->total_localtax2 = $objp->total_localtax2;
3666  $this->total_ttc = $objp->total_ttc;
3667  $this->product_type = $objp->product_type;
3668  $this->special_code = $objp->special_code;
3669 
3670  $this->ref = $objp->product_ref;
3671 
3672  $this->product_ref = $objp->product_ref;
3673  $this->product_label = $objp->product_label;
3674  $this->product_desc = $objp->product_desc;
3675 
3676  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
3677  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3678  // Move this into another method and call it when required.
3679 
3680  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3681  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
3682  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3683  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3684  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3685  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3686  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3687  $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3688  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3689  $sqlsearchpackage .= " LIMIT 1";
3690 
3691  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3692  if ($resqlsearchpackage) {
3693  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3694  if ($objsearchpackage) {
3695  $this->fk_fournprice = $objsearchpackage->rowid;
3696  $this->packaging = $objsearchpackage->packaging;
3697  }
3698  } else {
3699  $this->error = $this->db->lasterror();
3700  return -1;
3701  }
3702  }
3703 
3704  $this->date_start = $this->db->jdate($objp->date_start);
3705  $this->date_end = $this->db->jdate($objp->date_end);
3706  $this->fk_unit = $objp->fk_unit;
3707 
3708  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3709  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3710  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3711  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3712 
3713  $this->fetch_optionals();
3714 
3715  $this->db->free($result);
3716  return 1;
3717  } else {
3718  $this->error = 'Supplier order line with id='.$rowid.' not found';
3719  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3720  return 0;
3721  }
3722  } else {
3723  dol_print_error($this->db);
3724  return -1;
3725  }
3726  }
3727 
3734  public function insert($notrigger = 0)
3735  {
3736  global $conf, $user;
3737 
3738  $error = 0;
3739 
3740  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3741 
3742  // Clean parameters
3743  if (empty($this->tva_tx)) {
3744  $this->tva_tx = 0;
3745  }
3746  if (empty($this->localtax1_tx)) {
3747  $this->localtax1_tx = 0;
3748  }
3749  if (empty($this->localtax2_tx)) {
3750  $this->localtax2_tx = 0;
3751  }
3752  if (empty($this->localtax1_type)) {
3753  $this->localtax1_type = '0';
3754  }
3755  if (empty($this->localtax2_type)) {
3756  $this->localtax2_type = '0';
3757  }
3758  if (empty($this->total_localtax1)) {
3759  $this->total_localtax1 = 0;
3760  }
3761  if (empty($this->total_localtax2)) {
3762  $this->total_localtax2 = 0;
3763  }
3764  if (empty($this->rang)) {
3765  $this->rang = 0;
3766  }
3767  if (empty($this->remise_percent)) {
3768  $this->remise_percent = 0;
3769  }
3770  if (empty($this->info_bits)) {
3771  $this->info_bits = 0;
3772  }
3773  if (empty($this->special_code)) {
3774  $this->special_code = 0;
3775  }
3776  if (empty($this->fk_parent_line)) {
3777  $this->fk_parent_line = 0;
3778  }
3779  if (empty($this->pa_ht)) {
3780  $this->pa_ht = 0;
3781  }
3782 
3783  // Multicurrency
3784  if (!empty($this->multicurrency_code)) {
3785  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3786  }
3787  if (empty($this->fk_multicurrency)) {
3788  $this->multicurrency_code = $conf->currency;
3789  $this->fk_multicurrency = 0;
3790  $this->multicurrency_tx = 1;
3791  }
3792 
3793  // Check parameters
3794  if ($this->product_type < 0) {
3795  return -1;
3796  }
3797 
3798  $this->db->begin();
3799 
3800  // Insertion dans base de la ligne
3801  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3802  $sql .= " (fk_commande, label, description, date_start, date_end,";
3803  $sql .= " fk_product, product_type, special_code, rang,";
3804  $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3805  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3806  $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3807  $sql .= ")";
3808  $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3809  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3810  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3811  if ($this->fk_product) {
3812  $sql .= $this->fk_product.",";
3813  } else {
3814  $sql .= "null,";
3815  }
3816  $sql .= "'".$this->db->escape($this->product_type)."',";
3817  $sql .= "'".$this->db->escape($this->special_code)."',";
3818  $sql .= "'".$this->db->escape($this->rang)."',";
3819  $sql .= "'".$this->db->escape($this->qty)."', ";
3820  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3821  $sql .= " ".price2num($this->tva_tx).", ";
3822  $sql .= " ".price2num($this->localtax1_tx).",";
3823  $sql .= " ".price2num($this->localtax2_tx).",";
3824  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3825  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3826  $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3827  $sql .= " ".price2num($this->total_ht).",";
3828  $sql .= " ".price2num($this->total_tva).",";
3829  $sql .= " ".price2num($this->total_localtax1).",";
3830  $sql .= " ".price2num($this->total_localtax2).",";
3831  $sql .= " ".price2num($this->total_ttc).",";
3832  $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3833  $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3834  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3835  $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3836  $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3837  $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3838  $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3839  $sql .= ")";
3840 
3841  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3842  $resql = $this->db->query($sql);
3843  if ($resql) {
3844  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3845  $this->rowid = $this->id;
3846 
3847  if (!$error) {
3848  $result = $this->insertExtraFields();
3849  if ($result < 0) {
3850  $error++;
3851  }
3852  }
3853 
3854  if (!$error && !$notrigger) {
3855  // Call trigger
3856  $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3857  if ($result < 0) {
3858  $error++;
3859  }
3860  // End call triggers
3861  }
3862 
3863  if (!$error) {
3864  $this->db->commit();
3865  return 1;
3866  }
3867 
3868  foreach ($this->errors as $errmsg) {
3869  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3870  $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
3871  }
3872  $this->db->rollback();
3873  return -1 * $error;
3874  } else {
3875  $this->errors[] = $this->db->error();
3876  $this->db->rollback();
3877  return -2;
3878  }
3879  }
3886  public function update($notrigger = 0)
3887  {
3888  global $conf, $user;
3889 
3890  $error = 0;
3891 
3892  $this->db->begin();
3893 
3894  // Mise a jour ligne en base
3895  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3896  $sql .= " description='".$this->db->escape($this->desc)."'";
3897  $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
3898  $sql .= ", subprice='".price2num($this->subprice)."'";
3899  //$sql.= ",remise='".price2num($remise)."'";
3900  $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
3901 
3902  $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3903  $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
3904  $sql .= ", localtax1_tx='".price2num($this->total_localtax1)."'";
3905  $sql .= ", localtax2_tx='".price2num($this->total_localtax2)."'";
3906  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3907  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3908  $sql .= ", qty='".price2num($this->qty)."'";
3909  $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
3910  $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
3911  $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
3912  $sql .= ", total_ht='".price2num($this->total_ht)."'";
3913  $sql .= ", total_tva='".price2num($this->total_tva)."'";
3914  $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
3915  $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
3916  $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
3917  $sql .= ", product_type=".$this->product_type;
3918  $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
3919  $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
3920 
3921  // Multicurrency
3922  $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
3923  $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3924  $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3925  $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3926 
3927  $sql .= " WHERE rowid = ".((int) $this->id);
3928 
3929  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
3930  $result = $this->db->query($sql);
3931  if ($result > 0) {
3932  if (!$error) {
3933  $result = $this->insertExtraFields();
3934  if ($result < 0) {
3935  $error++;
3936  }
3937  }
3938 
3939  if (!$error && !$notrigger) {
3940  global $user;
3941  // Call trigger
3942  $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
3943  if ($result < 0) {
3944  $this->db->rollback();
3945  return -1;
3946  }
3947  // End call triggers
3948  }
3949 
3950  if (!$error) {
3951  $this->db->commit();
3952  return 1;
3953  } else {
3954  $this->db->rollback();
3955  return -1;
3956  }
3957  } else {
3958  $this->error = $this->db->lasterror();
3959  $this->db->rollback();
3960  return -1;
3961  }
3962  }
3963 
3970  public function delete($notrigger = 0)
3971  {
3972  global $user;
3973 
3974  $error = 0;
3975 
3976  $this->db->begin();
3977 
3978  // extrafields
3979  $result = $this->deleteExtraFields();
3980  if ($result < 0) {
3981  $this->db->rollback();
3982  return -1;
3983  }
3984 
3985  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".((int) $this->id);
3986 
3987  dol_syslog(__METHOD__, LOG_DEBUG);
3988  $resql = $this->db->query($sql);
3989  if ($resql) {
3990  if (!$notrigger) {
3991  // Call trigger
3992  $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
3993  if ($result < 0) {
3994  $error++;
3995  }
3996  // End call triggers
3997  }
3998 
3999  if (!$error) {
4000  $this->db->commit();
4001  return 1;
4002  }
4003 
4004  foreach ($this->errors as $errmsg) {
4005  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4006  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4007  }
4008  $this->db->rollback();
4009  return -1 * $error;
4010  } else {
4011  $this->error = $this->db->lasterror();
4012  return -1;
4013  }
4014  }
4015 }
$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.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
const STATUS_ORDERSENT
Order sent, shipment on process.
commande($user, $date, $methode, $comment='')
Submit a supplier order to supplier.
getRights()
Returns the rights used for this class.
load_board($user, $mode='opened')
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getMaxDeliveryTimeDay($langs)
Return the max number delivery delay in day.
fetch($id, $ref='')
Get object and lines from database.
Livraison($user, $date, $type, $comment)
Set a delivery in database for this supplier order.
getDispachedLines($status=-1)
Return array of dispatched lines waiting to be approved for this order.
classifyBilled(User $user)
Class invoiced the supplier order.
initAsSpecimen()
Initialise an instance with random values.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
const SOURCE_ID_REPLENISHMENT
The constant used into source field to track the order was generated by the replenishement feature.
setStatus($user, $status)
Tag order with a particular status.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLibStatut($mode=0)
Return label of the status of object.
get_methodes_commande()
Get list of order methods.
hasDelay()
Is the supplier order delayed? We suppose a purchase ordered as late if a the purchase order has been...
LibStatut($status, $mode=0, $billed=0)
Return label of a status.
Class to manage line orders.
update($notrigger=0)
Update the line object into db.
insert($notrigger=0)
Insert line into database.
Class to manage customers orders.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Superclass for orders classes.
Superclass for orders classes.
Class to manage Dolibarr database access.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:45
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:1401
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:1250
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