dolibarr  18.0.6
reception.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5  * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6  * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8  * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10  * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11  * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12  * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13  * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14  * Copyright (C) 2022-2023 Frédéric France <frederic.france@netlogic.fr>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program. If not, see <https://www.gnu.org/licenses/>.
28  */
29 
36 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
37 require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
38 require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
39 if (isModEnabled("propal")) {
40  require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
41 }
42 if (isModEnabled('commande')) {
43  require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
44 }
45 
46 
50 class Reception extends CommonObject
51 {
52  use CommonIncoterm;
53 
57  public $code = "";
58 
62  public $element = "reception";
63 
67  public $fk_element = "fk_reception";
68  public $table_element = "reception";
69  public $table_element_line = "commande_fournisseur_dispatch";
70  public $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
71 
75  public $picto = 'dollyrevert';
76 
77  public $socid;
78  public $ref_supplier;
79 
80  public $brouillon;
81  public $entrepot_id;
82  public $tracking_number;
83  public $tracking_url;
84  public $billed;
85  public $model_pdf;
86 
87  public $trueWeight;
88  public $weight_units;
89  public $trueWidth;
90  public $width_units;
91  public $trueHeight;
92  public $height_units;
93  public $trueDepth;
94  public $depth_units;
95  // A denormalized value
96  public $trueSize;
97 
98  public $date_delivery; // Date delivery planed
99 
105  public $date;
106 
110  public $date_reception;
111 
115  public $date_creation;
116 
120  public $date_valid;
121 
122  public $meths;
123  public $listmeths; // List of carriers
124 
128  public $lines = array();
129 
130 
131  // detail of lot and qty = array(id in llx_commande_fournisseur_dispatch, batch, qty)
132  // We can use this to know warehouse planned to be used for each lot.
133  public $detail_batch;
134 
135  const STATUS_DRAFT = 0;
136  const STATUS_VALIDATED = 1;
137  const STATUS_CLOSED = 2;
138 
139 
140 
146  public function __construct($db)
147  {
148  $this->db = $db;
149  }
150 
157  public function getNextNumRef($soc)
158  {
159  global $langs, $conf;
160  $langs->load("receptions");
161 
162  if (!empty($conf->global->RECEPTION_ADDON_NUMBER)) {
163  $mybool = false;
164 
165  $file = $conf->global->RECEPTION_ADDON_NUMBER.".php";
166  $classname = $conf->global->RECEPTION_ADDON_NUMBER;
167 
168  // Include file with class
169  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
170 
171  foreach ($dirmodels as $reldir) {
172  $dir = dol_buildpath($reldir."core/modules/reception/");
173 
174  // Load file with numbering class (if found)
175  $mybool |= @include_once $dir.$file;
176  }
177 
178  if (!$mybool) {
179  dol_print_error('', "Failed to include file ".$file);
180  return '';
181  }
182 
183  $obj = new $classname();
184 
185  $numref = "";
186  $numref = $obj->getNextValue($soc, $this);
187 
188  if ($numref != "") {
189  return $numref;
190  } else {
191  dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
192  return "";
193  }
194  } else {
195  print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
196  return "";
197  }
198  }
199 
207  public function create($user, $notrigger = 0)
208  {
209  global $conf, $hookmanager;
210 
211  $now = dol_now();
212 
213  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
214  $error = 0;
215 
216  // Clean parameters
217  $this->brouillon = 1;
218  $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
219  if (empty($this->fk_project)) {
220  $this->fk_project = 0;
221  }
222  if (empty($this->weight_units)) {
223  $this->weight_units = 0;
224  }
225  if (empty($this->size_units)) {
226  $this->size_units = 0;
227  }
228 
229  $this->user = $user;
230 
231  $this->db->begin();
232 
233  $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
234  $sql .= "ref";
235  $sql .= ", entity";
236  $sql .= ", ref_supplier";
237  $sql .= ", date_creation";
238  $sql .= ", fk_user_author";
239  $sql .= ", date_reception";
240  $sql .= ", date_delivery";
241  $sql .= ", fk_soc";
242  $sql .= ", fk_projet";
243  $sql .= ", fk_shipping_method";
244  $sql .= ", tracking_number";
245  $sql .= ", weight";
246  $sql .= ", size";
247  $sql .= ", width";
248  $sql .= ", height";
249  $sql .= ", weight_units";
250  $sql .= ", size_units";
251  $sql .= ", note_private";
252  $sql .= ", note_public";
253  $sql .= ", model_pdf";
254  $sql .= ", fk_incoterms, location_incoterms";
255  $sql .= ") VALUES (";
256  $sql .= "'(PROV)'";
257  $sql .= ", ".((int) $conf->entity);
258  $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
259  $sql .= ", '".$this->db->idate($now)."'";
260  $sql .= ", ".((int) $user->id);
261  $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
262  $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
263  $sql .= ", ".((int) $this->socid);
264  $sql .= ", ".((int) $this->fk_project);
265  $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
266  $sql .= ", '".$this->db->escape($this->tracking_number)."'";
267  $sql .= ", ".(is_null($this->weight) ? "NULL" : ((double) $this->weight));
268  $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((double) $this->trueDepth));
269  $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((double) $this->trueWidth));
270  $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((double) $this->trueHeight));
271  $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((double) $this->weight_units));
272  $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((double) $this->size_units));
273  $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
274  $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
275  $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
276  $sql .= ", ".(int) $this->fk_incoterms;
277  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
278  $sql .= ")";
279 
280  dol_syslog(get_class($this)."::create", LOG_DEBUG);
281 
282  $resql = $this->db->query($sql);
283 
284  if ($resql) {
285  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
286 
287  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
288  $sql .= " SET ref = '(PROV".$this->id.")'";
289  $sql .= " WHERE rowid = ".((int) $this->id);
290 
291  dol_syslog(get_class($this)."::create", LOG_DEBUG);
292  if ($this->db->query($sql)) {
293  // Insert of lines
294  $num = count($this->lines);
295  for ($i = 0; $i < $num; $i++) {
296  $this->lines[$i]->fk_reception = $this->id;
297 
298  if (!$this->lines[$i]->create($user) > 0) {
299  $error++;
300  }
301  }
302 
303  if (!$error && $this->id && $this->origin_id) {
304  $ret = $this->add_object_linked();
305  if (!$ret) {
306  $error++;
307  }
308  }
309 
310  // Create extrafields
311  if (!$error) {
312  $result = $this->insertExtraFields();
313  if ($result < 0) {
314  $error++;
315  }
316  }
317 
318  if (!$error && !$notrigger) {
319  // Call trigger
320  $result = $this->call_trigger('RECEPTION_CREATE', $user);
321  if ($result < 0) {
322  $error++;
323  }
324  // End call triggers
325  }
326 
327  if (!$error) {
328  $this->db->commit();
329  return $this->id;
330  } else {
331  foreach ($this->errors as $errmsg) {
332  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
333  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
334  }
335  $this->db->rollback();
336  return -1 * $error;
337  }
338  } else {
339  $error++;
340  $this->error = $this->db->lasterror()." - sql=$sql";
341  $this->db->rollback();
342  return -2;
343  }
344  } else {
345  $error++;
346  $this->error = $this->db->error()." - sql=$sql";
347  $this->db->rollback();
348  return -1;
349  }
350  }
351 
352 
353 
362  public function fetch($id, $ref = '', $ref_ext = '')
363  {
364  // Check parameters
365  if (empty($id) && empty($ref) && empty($ref_ext)) {
366  return -1;
367  }
368 
369  $sql = "SELECT e.rowid, e.entity, e.ref, e.fk_soc as socid, e.date_creation, e.ref_supplier, e.ref_ext, e.fk_user_author, e.fk_statut";
370  $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
371  $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery";
372  $sql .= ", e.fk_shipping_method, e.tracking_number";
373  $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
374  $sql .= ", e.note_private, e.note_public";
375  $sql .= ', e.fk_incoterms, e.location_incoterms';
376  $sql .= ', i.libelle as label_incoterms';
377  $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
378  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
379  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
380  $sql .= " WHERE e.entity IN (".getEntity('reception').")";
381  if ($id) {
382  $sql .= " AND e.rowid=".((int) $id);
383  }
384  if ($ref) {
385  $sql .= " AND e.ref='".$this->db->escape($ref)."'";
386  }
387  if ($ref_ext) {
388  $sql .= " AND e.ref_ext='".$this->db->escape($ref_ext)."'";
389  }
390 
391  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
392  $result = $this->db->query($sql);
393  if ($result) {
394  if ($this->db->num_rows($result)) {
395  $obj = $this->db->fetch_object($result);
396 
397  $this->id = $obj->rowid;
398  $this->entity = $obj->entity;
399  $this->ref = $obj->ref;
400  $this->socid = $obj->socid;
401  $this->ref_supplier = $obj->ref_supplier;
402  $this->ref_ext = $obj->ref_ext;
403  $this->statut = $obj->fk_statut;
404  $this->user_author_id = $obj->fk_user_author;
405  $this->date_creation = $this->db->jdate($obj->date_creation);
406  $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
407  $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
408  $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planed
409  $this->model_pdf = $obj->model_pdf;
410  $this->modelpdf = $obj->model_pdf; // deprecated
411  $this->shipping_method_id = $obj->fk_shipping_method;
412  $this->tracking_number = $obj->tracking_number;
413  $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
414  $this->origin_id = $obj->origin_id;
415  $this->billed = ($obj->fk_statut == 2 ? 1 : 0);
416 
417  $this->trueWeight = $obj->weight;
418  $this->weight_units = $obj->weight_units;
419 
420  $this->trueWidth = $obj->width;
421  $this->width_units = $obj->size_units;
422  $this->trueHeight = $obj->height;
423  $this->height_units = $obj->size_units;
424  $this->trueDepth = $obj->size;
425  $this->depth_units = $obj->size_units;
426 
427  $this->note_public = $obj->note_public;
428  $this->note_private = $obj->note_private;
429 
430  // A denormalized value
431  $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
432  $this->size_units = $obj->size_units;
433 
434  //Incoterms
435  $this->fk_incoterms = $obj->fk_incoterms;
436  $this->location_incoterms = $obj->location_incoterms;
437  $this->label_incoterms = $obj->label_incoterms;
438 
439  $this->db->free($result);
440 
441  if ($this->statut == 0) {
442  $this->brouillon = 1;
443  }
444 
445  //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
446  //$this->pdf_filename = $file;
447 
448  // Tracking url
449  $this->getUrlTrackingStatus($obj->tracking_number);
450 
451  /*
452  * Thirdparty
453  */
454  $result = $this->fetch_thirdparty();
455 
456 
457  // Retrieve all extrafields for reception
458  // fetch optionals attributes and labels
459  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
460  $extrafields = new ExtraFields($this->db);
461  $extrafields->fetch_name_optionals_label($this->table_element, true);
462  $this->fetch_optionals();
463 
464  /*
465  * Lines
466  */
467  $result = $this->fetch_lines();
468  if ($result < 0) {
469  return -3;
470  }
471 
472  return 1;
473  } else {
474  dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
475  $this->error = 'Reception with id '.$id.' not found';
476  return 0;
477  }
478  } else {
479  $this->error = $this->db->error();
480  return -1;
481  }
482  }
483 
491  public function valid($user, $notrigger = 0)
492  {
493  global $conf, $langs;
494 
495  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
496 
497  dol_syslog(get_class($this)."::valid");
498 
499  // Protection
500  if ($this->statut) {
501  dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
502  return 0;
503  }
504 
505  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer))
506  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate)))) {
507  $this->error = 'Permission denied';
508  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
509  return -1;
510  }
511 
512  $this->db->begin();
513 
514  $error = 0;
515 
516  // Define new ref
517  $soc = new Societe($this->db);
518  $soc->fetch($this->socid);
519 
520 
521  // Define new ref
522  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
523  $numref = $this->getNextNumRef($soc);
524  } else {
525  $numref = $this->ref;
526  }
527 
528  $this->newref = dol_sanitizeFileName($numref);
529 
530  $now = dol_now();
531 
532  // Validate
533  $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
534  $sql .= " ref='".$this->db->escape($numref)."'";
535  $sql .= ", fk_statut = 1";
536  $sql .= ", date_valid = '".$this->db->idate($now)."'";
537  $sql .= ", fk_user_valid = ".$user->id;
538  $sql .= " WHERE rowid = ".((int) $this->id);
539  dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
540  $resql = $this->db->query($sql);
541  if (!$resql) {
542  $this->error = $this->db->lasterror();
543  $error++;
544  }
545 
546  // If stock increment is done on reception (recommanded choice)
547  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION)) {
548  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
549 
550  $langs->load("agenda");
551 
552  // Loop on each product line to add a stock movement
553  // TODO in future, reception lines may not be linked to order line
554  $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
555  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
556  $sql .= " ed.eatby, ed.sellby, ed.batch,";
557  $sql .= " ed.cost_price";
558  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
559  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
560  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
561  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
562 
563  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
564  $resql = $this->db->query($sql);
565  if ($resql) {
566  $cpt = $this->db->num_rows($resql);
567  for ($i = 0; $i < $cpt; $i++) {
568  $obj = $this->db->fetch_object($resql);
569 
570  $qty = $obj->qty;
571 
572  if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
573  continue;
574  }
575  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
576 
577  //var_dump($this->lines[$i]);
578  $mouvS = new MouvementStock($this->db);
579  $mouvS->origin = &$this;
580  $mouvS->setOrigin($this->element, $this->id);
581 
582  if (empty($obj->batch)) {
583  // line without batch detail
584 
585  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
586  $inventorycode = '';
587  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
588 
589  if (intval($result) < 0) {
590  $error++;
591  $this->errors[] = $mouvS->error;
592  $this->errors = array_merge($this->errors, $mouvS->errors);
593  break;
594  }
595  } else {
596  // line with batch detail
597 
598  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
599  // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
600  $inventorycode = '';
601  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, '', 0, $inventorycode);
602 
603  if (intval($result) < 0) {
604  $error++;
605  $this->errors[] = $mouvS->error;
606  $this->errors = array_merge($this->errors, $mouvS->errors);
607  break;
608  }
609  }
610  }
611  } else {
612  $this->db->rollback();
613  $this->error = $this->db->error();
614  return -2;
615  }
616  }
617 
618  // Change status of order to "reception in process" or "totally received"
619  $status = $this->getStatusDispatch();
620  if ($status < 0) {
621  $error++;
622  } else {
623  $trigger_key = '';
625  $ret = $this->commandeFournisseur->Livraison($user, dol_now(), 'tot', '');
626  if ($ret < 0) {
627  $error++;
628  $this->errors = array_merge($this->errors, $this->commandeFournisseur->errors);
629  }
630  } else {
631  $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
632  if ($ret < 0) {
633  $error++;
634  }
635  }
636  }
637 
638  if (!$error && !$notrigger) {
639  // Call trigger
640  $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
641  if ($result < 0) {
642  $error++;
643  }
644  // End call triggers
645  }
646 
647  if (!$error) {
648  $this->oldref = $this->ref;
649 
650  // Rename directory if dir was a temporary ref
651  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
652  // Now we rename also files into index
653  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'reception/".$this->db->escape($this->newref)."'";
654  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
655  $resql = $this->db->query($sql);
656  if (!$resql) {
657  $error++; $this->error = $this->db->lasterror();
658  }
659  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
660  $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
661  $resql = $this->db->query($sql);
662  if (!$resql) {
663  $error++; $this->error = $this->db->lasterror();
664  }
665 
666  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
667  $oldref = dol_sanitizeFileName($this->ref);
668  $newref = dol_sanitizeFileName($numref);
669  $dirsource = $conf->reception->dir_output.'/'.$oldref;
670  $dirdest = $conf->reception->dir_output.'/'.$newref;
671  if (!$error && file_exists($dirsource)) {
672  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
673 
674  if (@rename($dirsource, $dirdest)) {
675  dol_syslog("Rename ok");
676  // Rename docs starting with $oldref with $newref
677  $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
678  foreach ($listoffiles as $fileentry) {
679  $dirsource = $fileentry['name'];
680  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
681  $dirsource = $fileentry['path'].'/'.$dirsource;
682  $dirdest = $fileentry['path'].'/'.$dirdest;
683  @rename($dirsource, $dirdest);
684  }
685  }
686  }
687  }
688  }
689 
690  // Set new ref and current status
691  if (!$error) {
692  $this->ref = $numref;
693  $this->statut = 1;
694  }
695 
696  if (!$error) {
697  $this->db->commit();
698  return 1;
699  } else {
700  foreach ($this->errors as $errmsg) {
701  dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
702  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
703  }
704  $this->db->rollback();
705  return -1 * $error;
706  }
707  }
708 
714  public function getStatusDispatch()
715  {
716  global $conf;
717 
718  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
719  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
720 
722 
723  if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
724  if (empty($this->commandeFournisseur)) {
725  $this->fetch_origin();
726  if (empty($this->commandeFournisseur->lines)) {
727  $res = $this->commandeFournisseur->fetch_lines();
728  if ($res < 0) return $res;
729  }
730  }
731 
732  $qty_received = array();
733  $qty_wished = array();
734 
735  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
736  $filter = array('t.fk_commande'=>$this->origin_id);
737  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
738  $filter['t.status'] = 1; // Restrict to lines with status validated
739  }
740 
741  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
742  if ($ret < 0) {
743  $this->error = $supplierorderdispatch->error;
744  $this->errors = $supplierorderdispatch->errors;
745  return $ret;
746  } else {
747  // build array with quantity received by product in all supplier orders (origin)
748  foreach ($supplierorderdispatch->lines as $dispatch_line) {
749  if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
750  $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
751  } else {
752  $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
753  }
754  }
755 
756  // qty wished in order supplier (origin)
757  foreach ($this->commandeFournisseur->lines as $origin_line) {
758  // exclude lines not qualified for reception
759  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $origin_line->product_type > 0) {
760  continue;
761  }
762 
763  $qty_wished[$origin_line->fk_product] += $origin_line->qty;
764  }
765 
766  // compare array
767  $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
768  $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
769  $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
770 
771  if (count($diff_array) == 0 && count($keys_in_wished_not_in_received) == 0 && count($keys_in_received_not_in_wished) == 0) { // no diff => mean everything is received
773  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
774  // set totally received if more products received than ordered
775  $close = 0;
776 
777  if (count($diff_array) > 0) {
778  // there are some difference between the two arrays
779  // scan the array of results
780  foreach ($diff_array as $key => $value) {
781  // if the quantity delivered is greater or equal to ordered quantity
782  if ($qty_received[$key] >= $qty_wished[$key]) {
783  $close++;
784  }
785  }
786  }
787 
788  if ($close == count($diff_array)) {
789  // all the products are received equal or more than the ordered quantity
791  }
792  }
793  }
794  }
795 
796  return $status;
797  }
798 
815  public function addline($entrepot_id, $id, $qty, $array_options = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $cost_price = 0)
816  {
817  global $conf, $langs, $user;
818 
819  $num = count($this->lines);
820  $line = new CommandeFournisseurDispatch($this->db);
821 
822  $line->fk_entrepot = $entrepot_id;
823  $line->fk_commandefourndet = $id;
824  $line->qty = $qty;
825 
826  $supplierorderline = new CommandeFournisseurLigne($this->db);
827  $result = $supplierorderline->fetch($id);
828  if ($result <= 0) {
829  $this->error = $supplierorderline->error;
830  $this->errors = $supplierorderline->errors;
831  return -1;
832  }
833 
834  $fk_product = 0;
835  if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
836  $fk_product = $supplierorderline->fk_product;
837 
838  if (!($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS)) {
839  $langs->load("errors");
840  $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
841  return -1;
842  }
843  }
844 
845  // Check batch is set
846  $product = new Product($this->db);
847  $product->fetch($fk_product);
848  if (isModEnabled('productbatch')) {
849  $langs->load("errors");
850  if (!empty($product->status_batch) && empty($batch)) {
851  $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
852  return -1;
853  } elseif (empty($product->status_batch) && !empty($batch)) {
854  $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
855  return -1;
856  }
857  }
858  unset($product);
859 
860  // extrafields
861  $line->array_options = $supplierorderline->array_options;
862  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options) > 0) {
863  foreach ($array_options as $key => $value) {
864  $line->array_options[$key] = $value;
865  }
866  }
867 
868  $line->fk_product = $fk_product;
869  $line->fk_commande = $supplierorderline->fk_commande;
870  $line->fk_user = $user->id;
871  $line->comment = $comment;
872  $line->batch = $batch;
873  $line->eatby = $eatby;
874  $line->sellby = $sellby;
875  $line->status = 1;
876  $line->cost_price = $cost_price;
877  $line->fk_reception = $this->id;
878 
879  $this->lines[$num] = $line;
880 
881  return $num;
882  }
883 
884 
892  public function update($user = null, $notrigger = 0)
893  {
894  global $conf;
895  $error = 0;
896 
897  // Clean parameters
898 
899  if (isset($this->ref)) {
900  $this->ref = trim($this->ref);
901  }
902  if (isset($this->entity)) {
903  $this->entity = trim($this->entity);
904  }
905  if (isset($this->ref_supplier)) {
906  $this->ref_supplier = trim($this->ref_supplier);
907  }
908  if (isset($this->socid)) {
909  $this->socid = trim($this->socid);
910  }
911  if (isset($this->fk_user_author)) {
912  $this->fk_user_author = trim($this->fk_user_author);
913  }
914  if (isset($this->fk_user_valid)) {
915  $this->fk_user_valid = trim($this->fk_user_valid);
916  }
917  if (isset($this->shipping_method_id)) {
918  $this->shipping_method_id = trim($this->shipping_method_id);
919  }
920  if (isset($this->tracking_number)) {
921  $this->tracking_number = trim($this->tracking_number);
922  }
923  if (isset($this->statut)) {
924  $this->statut = (int) $this->statut;
925  }
926  if (isset($this->trueDepth)) {
927  $this->trueDepth = trim($this->trueDepth);
928  }
929  if (isset($this->trueWidth)) {
930  $this->trueWidth = trim($this->trueWidth);
931  }
932  if (isset($this->trueHeight)) {
933  $this->trueHeight = trim($this->trueHeight);
934  }
935  if (isset($this->size_units)) {
936  $this->size_units = trim($this->size_units);
937  }
938  if (isset($this->weight_units)) {
939  $this->weight_units = trim($this->weight_units);
940  }
941  if (isset($this->trueWeight)) {
942  $this->weight = trim($this->trueWeight);
943  }
944  if (isset($this->note_private)) {
945  $this->note_private = trim($this->note_private);
946  }
947  if (isset($this->note_public)) {
948  $this->note_public = trim($this->note_public);
949  }
950  if (isset($this->model_pdf)) {
951  $this->model_pdf = trim($this->model_pdf);
952  }
953 
954 
955  // Check parameters
956  // Put here code to add control on parameters values
957 
958  // Update request
959  $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
960 
961  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
962  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
963  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
964  $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
965  $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
966  $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
967  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
968  $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
969  $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
970  $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
971  $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
972  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
973  $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
974  $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
975  $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
976  $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
977  $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
978  $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
979  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
980  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
981  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
982  $sql .= " entity = ".((int) $conf->entity);
983  $sql .= " WHERE rowid=".((int) $this->id);
984 
985  $this->db->begin();
986 
987  dol_syslog(get_class($this)."::update", LOG_DEBUG);
988  $resql = $this->db->query($sql);
989  if (!$resql) {
990  $error++; $this->errors[] = "Error ".$this->db->lasterror();
991  }
992 
993  if (!$error) {
994  if (!$notrigger) {
995  // Call trigger
996  $result = $this->call_trigger('RECEPTION_MODIFY', $user);
997  if ($result < 0) {
998  $error++;
999  }
1000  // End call triggers
1001  }
1002  }
1003 
1004  // Commit or rollback
1005  if ($error) {
1006  foreach ($this->errors as $errmsg) {
1007  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1008  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1009  }
1010  $this->db->rollback();
1011  return -1 * $error;
1012  } else {
1013  $this->db->commit();
1014  return 1;
1015  }
1016  }
1017 
1024  public function delete(User $user)
1025  {
1026  global $conf, $langs, $user;
1027  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1028 
1029  $error = 0;
1030  $this->error = '';
1031 
1032 
1033  $this->db->begin();
1034 
1035  // Stock control
1036  if (isModEnabled('stock') && $conf->global->STOCK_CALCULATE_ON_RECEPTION && $this->statut > 0) {
1037  require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1038 
1039  $langs->load("agenda");
1040 
1041  // Loop on each product line to add a stock movement
1042  $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as commande_fournisseur_dispatch_id";
1043  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1044  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1045  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1046  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1047 
1048  dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1049  $resql = $this->db->query($sql);
1050  if ($resql) {
1051  $cpt = $this->db->num_rows($resql);
1052  for ($i = 0; $i < $cpt; $i++) {
1053  dol_syslog(get_class($this)."::delete movement index ".$i);
1054  $obj = $this->db->fetch_object($resql);
1055 
1056  $mouvS = new MouvementStock($this->db);
1057  // we do not log origin because it will be deleted
1058  $mouvS->origin = null;
1059 
1060  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ReceptionDeletedInDolibarr", $this->ref), '', $obj->eatby, $obj->sellby, $obj->batch); // Price is set to 0, because we don't want to see WAP changed
1061  }
1062  } else {
1063  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1064  }
1065  }
1066 
1067  if (!$error) {
1068  $main = MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
1069  $ef = $main."_extrafields";
1070 
1071  $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1072 
1073  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
1074  $sql .= " WHERE fk_reception = ".((int) $this->id);
1075 
1076  if ($this->db->query($sqlef) && $this->db->query($sql)) {
1077  // Delete linked object
1078  $res = $this->deleteObjectLinked();
1079  if ($res < 0) {
1080  $error++;
1081  }
1082 
1083  if (!$error) {
1084  $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1085  $sql .= " WHERE rowid = ".((int) $this->id);
1086 
1087  if ($this->db->query($sql)) {
1088  // Call trigger
1089  $result = $this->call_trigger('RECEPTION_DELETE', $user);
1090  if ($result < 0) {
1091  $error++;
1092  }
1093  // End call triggers
1094 
1095  if (!empty($this->origin) && $this->origin_id > 0) {
1096  $this->fetch_origin();
1097  $origin = $this->origin;
1098  if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1099  // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1100  $this->$origin->loadReceptions();
1101  //var_dump($this->$origin->receptions);exit;
1102  if (count($this->$origin->receptions) <= 0) {
1103  $this->$origin->setStatut(3); // ordered
1104  }
1105  }
1106  }
1107 
1108  if (!$error) {
1109  $this->db->commit();
1110 
1111  // We delete PDFs
1112  $ref = dol_sanitizeFileName($this->ref);
1113  if (!empty($conf->reception->dir_output)) {
1114  $dir = $conf->reception->dir_output.'/'.$ref;
1115  $file = $dir.'/'.$ref.'.pdf';
1116  if (file_exists($file)) {
1117  if (!dol_delete_file($file)) {
1118  return 0;
1119  }
1120  }
1121  if (file_exists($dir)) {
1122  if (!dol_delete_dir_recursive($dir)) {
1123  $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1124  return 0;
1125  }
1126  }
1127  }
1128 
1129  return 1;
1130  } else {
1131  $this->db->rollback();
1132  return -1;
1133  }
1134  } else {
1135  $this->error = $this->db->lasterror()." - sql=$sql";
1136  $this->db->rollback();
1137  return -3;
1138  }
1139  } else {
1140  $this->error = $this->db->lasterror()." - sql=$sql";
1141  $this->db->rollback();
1142  return -2;
1143  }
1144  } else {
1145  $this->error = $this->db->lasterror()." - sql=$sql";
1146  $this->db->rollback();
1147  return -1;
1148  }
1149  } else {
1150  $this->db->rollback();
1151  return -1;
1152  }
1153  }
1154 
1155  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1161  public function fetch_lines()
1162  {
1163  // phpcs:enable
1164  $this->lines = array();
1165 
1166  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1167 
1168  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch WHERE fk_reception = ".((int) $this->id);
1169  $resql = $this->db->query($sql);
1170 
1171  if (!empty($resql)) {
1172  while ($obj = $this->db->fetch_object($resql)) {
1173  $line = new CommandeFournisseurDispatch($this->db);
1174 
1175  $line->fetch($obj->rowid);
1176 
1177  // TODO Remove or keep this ?
1178  $line->fetch_product();
1179 
1180  $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1181  $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1182  $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1183  $sql_commfourndet .= ' ORDER BY rang';
1184 
1185  $resql_commfourndet = $this->db->query($sql_commfourndet);
1186  if (!empty($resql_commfourndet)) {
1187  $obj = $this->db->fetch_object($resql_commfourndet);
1188  $line->qty_asked = $obj->qty;
1189  $line->description = $obj->description;
1190  $line->desc = $obj->description;
1191  $line->tva_tx = $obj->tva_tx;
1192  $line->vat_src_code = $obj->vat_src_code;
1193  $line->subprice = $obj->subprice;
1194  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1195  $line->remise_percent = $obj->remise_percent;
1196  $line->label = !empty($obj->label) ? $obj->label : $line->product->label;
1197  $line->ref_supplier = $obj->ref;
1198  $line->total_ht = $obj->total_ht;
1199  $line->total_ttc = $obj->total_ttc;
1200  $line->total_tva = $obj->total_tva;
1201  } else {
1202  $line->qty_asked = 0;
1203  $line->description = '';
1204  $line->desc = '';
1205  $line->label = $obj->label;
1206  }
1207 
1208  $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1209  $tva = $pu_ht * $line->tva_tx / 100;
1210  $this->total_ht += $pu_ht;
1211  $this->total_tva += $pu_ht * $line->tva_tx / 100;
1212 
1213  $this->total_ttc += $pu_ht + $tva;
1214 
1215  if (isModEnabled('productbatch') && !empty($line->batch)) {
1216  $detail_batch = new stdClass();
1217  $detail_batch->eatby = $line->eatby;
1218  $detail_batch->sellby = $line->sellby;
1219  $detail_batch->batch = $line->batch;
1220  $detail_batch->qty = $line->qty;
1221  $line->detail_batch[] = $detail_batch;
1222  }
1223 
1224  $this->lines[] = $line;
1225  }
1226 
1227  return 1;
1228  } else {
1229  return -1;
1230  }
1231  }
1232 
1243  public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1244  {
1245  global $conf, $langs, $hookmanager;
1246  $result = '';
1247  $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1248  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1249  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1250 
1251  $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1252 
1253  if ($short) {
1254  return $url;
1255  }
1256 
1257  $linkclose = '';
1258  if (empty($notooltip)) {
1259  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1260  $label = $langs->trans("Reception");
1261  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1262  }
1263  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1264  $linkclose .= ' class="classfortooltip"';
1265  }
1266 
1267  $linkstart = '<a href="'.$url.'"';
1268  $linkstart .= $linkclose.'>';
1269  $linkend = '</a>';
1270 
1271  $result .= $linkstart;
1272  if ($withpicto) {
1273  $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1274  }
1275  if ($withpicto != 2) {
1276  $result .= $this->ref;
1277  }
1278 
1279  $result .= $linkend;
1280 
1281  global $action;
1282  $hookmanager->initHooks(array($this->element . 'dao'));
1283  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1284  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1285  if ($reshook > 0) {
1286  $result = $hookmanager->resPrint;
1287  } else {
1288  $result .= $hookmanager->resPrint;
1289  }
1290  return $result;
1291  }
1292 
1299  public function getLibStatut($mode = 0)
1300  {
1301  return $this->LibStatut($this->statut, $mode);
1302  }
1303 
1304  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1312  public function LibStatut($status, $mode)
1313  {
1314  // phpcs:enable
1315  global $langs;
1316 
1317  // List of long language codes for status
1318  $this->labelStatus[-1] = 'StatusReceptionCanceled';
1319  $this->labelStatus[0] = 'StatusReceptionDraft';
1320  // product to receive if stock increase is on close or already received if stock increase is on validation
1321  $this->labelStatus[1] = 'StatusReceptionValidated';
1322  if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1323  $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1324  }
1325  if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1326  $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1327  }
1328  $this->labelStatus[2] = 'StatusReceptionProcessed';
1329 
1330  // List of short language codes for status
1331  $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1332  $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1333  $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1334  $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1335 
1336  $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1337  $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1338 
1339  $statusType = 'status'.$status;
1340  if ($status == self::STATUS_VALIDATED) {
1341  $statusType = 'status4';
1342  }
1343  if ($status == self::STATUS_CLOSED) {
1344  $statusType = 'status6';
1345  }
1346 
1347  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1348  }
1349 
1357  public function initAsSpecimen()
1358  {
1359  global $langs;
1360 
1361  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1362  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1363  $now = dol_now();
1364 
1365  dol_syslog(get_class($this)."::initAsSpecimen");
1366 
1367  $order = new CommandeFournisseur($this->db);
1368  $order->initAsSpecimen();
1369 
1370  // Initialise parametres
1371  $this->id = 0;
1372  $this->ref = 'SPECIMEN';
1373  $this->specimen = 1;
1374  $this->statut = 1;
1375  $this->livraison_id = 0;
1376  $this->date = $now;
1377  $this->date_creation = $now;
1378  $this->date_valid = $now;
1379  $this->date_delivery = $now;
1380  $this->date_reception = $now + 24 * 3600;
1381 
1382  $this->entrepot_id = 0;
1383  $this->socid = 1;
1384 
1385  $this->commande_id = 0;
1386  $this->commande = $order;
1387 
1388  $this->origin_id = 1;
1389  $this->origin = 'commande';
1390 
1391  $this->note_private = 'Private note';
1392  $this->note_public = 'Public note';
1393 
1394  $nbp = 5;
1395  $xnbp = 0;
1396  while ($xnbp < $nbp) {
1397  $line = new CommandeFournisseurDispatch($this->db);
1398  $line->desc = $langs->trans("Description")." ".$xnbp;
1399  $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1400  $line->label = $langs->trans("Description")." ".$xnbp;
1401  $line->qty = 10;
1402 
1403  $line->fk_product = $this->commande->lines[$xnbp]->fk_product;
1404 
1405  $this->lines[] = $line;
1406  $xnbp++;
1407  }
1408  }
1409 
1417  public function setDeliveryDate($user, $delivery_date)
1418  {
1419  // phpcs:enable
1420  if ($user->rights->reception->creer) {
1421  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1422  $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1423  $sql .= " WHERE rowid = ".((int) $this->id);
1424 
1425  dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1426  $resql = $this->db->query($sql);
1427  if ($resql) {
1428  $this->date_delivery = $delivery_date;
1429  return 1;
1430  } else {
1431  $this->error = $this->db->error();
1432  return -1;
1433  }
1434  } else {
1435  return -2;
1436  }
1437  }
1438 
1439  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1445  public function fetch_delivery_methods()
1446  {
1447  // phpcs:enable
1448  global $langs;
1449  $this->meths = array();
1450 
1451  $sql = "SELECT em.rowid, em.code, em.libelle";
1452  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1453  $sql .= " WHERE em.active = 1";
1454  $sql .= " ORDER BY em.libelle ASC";
1455 
1456  $resql = $this->db->query($sql);
1457  if ($resql) {
1458  while ($obj = $this->db->fetch_object($resql)) {
1459  $label = $langs->trans('ReceptionMethod'.$obj->code);
1460  $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1461  }
1462  }
1463  }
1464 
1465  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1472  public function list_delivery_methods($id = '')
1473  {
1474  // phpcs:enable
1475  global $langs;
1476 
1477  $this->listmeths = array();
1478  $i = 0;
1479 
1480  $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1481  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1482  if ($id != '') {
1483  $sql .= " WHERE em.rowid = ".((int) $id);
1484  }
1485 
1486  $resql = $this->db->query($sql);
1487  if ($resql) {
1488  while ($obj = $this->db->fetch_object($resql)) {
1489  $this->listmeths[$i]['rowid'] = $obj->rowid;
1490  $this->listmeths[$i]['code'] = $obj->code;
1491  $label = $langs->trans('ReceptionMethod'.$obj->code);
1492  $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1493  $this->listmeths[$i]['description'] = $obj->description;
1494  $this->listmeths[$i]['tracking'] = $obj->tracking;
1495  $this->listmeths[$i]['active'] = $obj->active;
1496  $i++;
1497  }
1498  }
1499  }
1500 
1507  public function getUrlTrackingStatus($value = '')
1508  {
1509  if (!empty($this->shipping_method_id)) {
1510  $sql = "SELECT em.code, em.tracking";
1511  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1512  $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1513 
1514  $resql = $this->db->query($sql);
1515  if ($resql) {
1516  if ($obj = $this->db->fetch_object($resql)) {
1517  $tracking = $obj->tracking;
1518  }
1519  }
1520  }
1521 
1522  if (!empty($tracking) && !empty($value)) {
1523  $url = str_replace('{TRACKID}', $value, $tracking);
1524  $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">'.($value ? $value : 'url').'</a>', $url, $url);
1525  } else {
1526  $this->tracking_url = $value;
1527  }
1528  }
1529 
1535  public function setClosed()
1536  {
1537  global $conf, $langs, $user;
1538 
1539  $error = 0;
1540 
1541  // Protection
1542  if ($this->statut == Reception::STATUS_CLOSED) {
1543  dol_syslog(get_class($this)."::Already in closed status", LOG_WARNING);
1544  return 0;
1545  }
1546 
1547  $this->db->begin();
1548 
1549  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut='.self::STATUS_CLOSED;
1550  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1551 
1552  $resql = $this->db->query($sql);
1553  if ($resql) {
1554  // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1555  if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1556  $order = new CommandeFournisseur($this->db);
1557  $order->fetch($this->origin_id);
1558 
1559  $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1560 
1561  $receptions_match_order = 1;
1562  foreach ($order->lines as $line) {
1563  $lineid = $line->id;
1564  $qty = $line->qty;
1565  if (($line->product_type == 0 || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) && $order->receptions[$lineid] < $qty) {
1566  $receptions_match_order = 0;
1567  $text = 'Qty for order line id '.$lineid.' is '.$qty.'. However in the receptions with status Reception::STATUS_CLOSED='.self::STATUS_CLOSED.' we have qty = '.$order->receptions[$lineid].', so we can t close order';
1568  dol_syslog($text);
1569  break;
1570  }
1571  }
1572  if ($receptions_match_order) {
1573  dol_syslog("Qty for the ".count($order->lines)." lines of order have same value for receptions with status Reception::STATUS_CLOSED=".self::STATUS_CLOSED.', so we close order');
1574  $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1575  }
1576  }
1577 
1578  $this->statut = self::STATUS_CLOSED;
1579 
1580 
1581  // If stock increment is done on closing
1582  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) {
1583  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1584 
1585  $langs->load("agenda");
1586 
1587  // Loop on each product line to add a stock movement
1588  // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1589  $sql = "SELECT cd.fk_product, cd.subprice,";
1590  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1591  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1592  $sql .= " ed.cost_price";
1593  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1594  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1595  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1596  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1597 
1598  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1599  $resql = $this->db->query($sql);
1600 
1601  if ($resql) {
1602  $cpt = $this->db->num_rows($resql);
1603  for ($i = 0; $i < $cpt; $i++) {
1604  $obj = $this->db->fetch_object($resql);
1605 
1606  $qty = $obj->qty;
1607 
1608  if ($qty <= 0) {
1609  continue;
1610  }
1611  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1612 
1613  $mouvS = new MouvementStock($this->db);
1614  $mouvS->origin = &$this;
1615  $mouvS->setOrigin($this->element, $this->id);
1616 
1617  if (empty($obj->batch)) {
1618  // line without batch detail
1619 
1620  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1621  $inventorycode = '';
1622  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1623  if ($result < 0) {
1624  $this->error = $mouvS->error;
1625  $this->errors = $mouvS->errors;
1626  $error++; break;
1627  }
1628  } else {
1629  // line with batch detail
1630 
1631  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1632  $inventorycode = '';
1633  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, '', 0, $inventorycode);
1634 
1635  if ($result < 0) {
1636  $this->error = $mouvS->error;
1637  $this->errors = $mouvS->errors;
1638  $error++; break;
1639  }
1640  }
1641  }
1642  } else {
1643  $this->error = $this->db->lasterror();
1644  $error++;
1645  }
1646  }
1647 
1648  // Call trigger
1649  if (!$error) {
1650  $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1651  if ($result < 0) {
1652  $error++;
1653  }
1654  }
1655  } else {
1656  dol_print_error($this->db);
1657  $error++;
1658  }
1659 
1660  if (!$error) {
1661  $this->db->commit();
1662  return 1;
1663  } else {
1664  $this->db->rollback();
1665  return -1;
1666  }
1667  }
1668 
1674  public function setBilled()
1675  {
1676  global $user;
1677  $error = 0;
1678 
1679  $this->db->begin();
1680 
1681  if ($this->statut == Reception::STATUS_VALIDATED) {
1682  // do not close if already closed
1683  $this->setClosed();
1684  }
1685 
1686  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1687  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1688 
1689  $resql = $this->db->query($sql);
1690  if ($resql) {
1691  $this->statut = 2;
1692  $this->billed = 1;
1693 
1694  // Call trigger
1695  $result = $this->call_trigger('RECEPTION_BILLED', $user);
1696  if ($result < 0) {
1697  $error++;
1698  }
1699  } else {
1700  $error++;
1701  $this->errors[] = $this->db->lasterror;
1702  }
1703 
1704  if (empty($error)) {
1705  $this->db->commit();
1706  return 1;
1707  } else {
1708  $this->db->rollback();
1709  return -1;
1710  }
1711  }
1712 
1718  public function reOpen()
1719  {
1720  global $conf, $langs, $user;
1721 
1722  $error = 0;
1723 
1724  $this->db->begin();
1725 
1726  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1727  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1728 
1729  $resql = $this->db->query($sql);
1730  if ($resql) {
1731  $this->statut = 1;
1732  $this->billed = 0;
1733 
1734  // If stock increment is done on closing
1735  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) {
1736  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1737  $numref = $this->ref;
1738  $langs->load("agenda");
1739 
1740  // Loop on each product line to add a stock movement
1741  // TODO possibilite de receptionner a partir d'une propale ou autre origine
1742  $sql = "SELECT ed.fk_product, cd.subprice,";
1743  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1744  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1745  $sql .= " ed.cost_price";
1746  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1747  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1748  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1749  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1750 
1751  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1752  $resql = $this->db->query($sql);
1753  if ($resql) {
1754  $cpt = $this->db->num_rows($resql);
1755  for ($i = 0; $i < $cpt; $i++) {
1756  $obj = $this->db->fetch_object($resql);
1757 
1758  $qty = $obj->qty;
1759 
1760  if ($qty <= 0) {
1761  continue;
1762  }
1763 
1764  dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1765 
1766  //var_dump($this->lines[$i]);
1767  $mouvS = new MouvementStock($this->db);
1768  $mouvS->origin = &$this;
1769  $mouvS->setOrigin($this->element, $this->id);
1770 
1771  if (empty($obj->batch)) {
1772  // line without batch detail
1773 
1774  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1775  $inventorycode = '';
1776  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1777 
1778  if ($result < 0) {
1779  $this->error = $mouvS->error;
1780  $this->errors = $mouvS->errors;
1781  $error++; break;
1782  }
1783  } else {
1784  // line with batch detail
1785 
1786  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1787  $inventorycode = '';
1788  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock, $inventorycode);
1789  if ($result < 0) {
1790  $this->error = $mouvS->error;
1791  $this->errors = $mouvS->errors;
1792  $error++; break;
1793  }
1794  }
1795  }
1796  } else {
1797  $this->error = $this->db->lasterror();
1798  $error++;
1799  }
1800  }
1801 
1802  if (!$error) {
1803  // Call trigger
1804  $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1805  if ($result < 0) {
1806  $error++;
1807  }
1808  }
1809 
1810  if (!$error && $this->origin == 'order_supplier') {
1811  $commande = new CommandeFournisseur($this->db);
1812  $commande->fetch($this->origin_id);
1813  $result = $commande->setStatus($user, 4);
1814  if ($result < 0) {
1815  $error++;
1816  $this->error = $commande->error;
1817  $this->errors = $commande->errors;
1818  }
1819  }
1820  } else {
1821  $error++;
1822  $this->errors[] = $this->db->lasterror();
1823  }
1824 
1825  if (!$error) {
1826  $this->db->commit();
1827  return 1;
1828  } else {
1829  $this->db->rollback();
1830  return -1;
1831  }
1832  }
1833 
1840  public function setDraft($user)
1841  {
1842  // phpcs:enable
1843  global $conf, $langs;
1844 
1845  $error = 0;
1846 
1847  // Protection
1848  if ($this->statut <= self::STATUS_DRAFT) {
1849  return 0;
1850  }
1851 
1852  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer))
1853  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate)))) {
1854  $this->error = 'Permission denied';
1855  return -1;
1856  }
1857 
1858  $this->db->begin();
1859 
1860  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1861  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1862  $sql .= " WHERE rowid = ".((int) $this->id);
1863 
1864  dol_syslog(__METHOD__, LOG_DEBUG);
1865  if ($this->db->query($sql)) {
1866  // If stock increment is done on closing
1867  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION)) {
1868  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1869 
1870  $langs->load("agenda");
1871 
1872  // Loop on each product line to add a stock movement
1873  // TODO possibilite de receptionner a partir d'une propale ou autre origine
1874  $sql = "SELECT cd.fk_product, cd.subprice,";
1875  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1876  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1877  $sql .= " ed.cost_price";
1878  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1879  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1880  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1881  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1882 
1883  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1884  $resql = $this->db->query($sql);
1885  if ($resql) {
1886  $cpt = $this->db->num_rows($resql);
1887  for ($i = 0; $i < $cpt; $i++) {
1888  $obj = $this->db->fetch_object($resql);
1889 
1890  $qty = $obj->qty;
1891 
1892 
1893  if ($qty <= 0) {
1894  continue;
1895  }
1896  dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1897 
1898  //var_dump($this->lines[$i]);
1899  $mouvS = new MouvementStock($this->db);
1900  $mouvS->origin = &$this;
1901  $mouvS->setOrigin($this->element, $this->id);
1902 
1903  if (empty($obj->batch)) {
1904  // line without batch detail
1905 
1906  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1907  $inventorycode = '';
1908  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1909  if ($result < 0) {
1910  $this->error = $mouvS->error;
1911  $this->errors = $mouvS->errors;
1912  $error++;
1913  break;
1914  }
1915  } else {
1916  // line with batch detail
1917 
1918  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1919  $inventorycode = '';
1920  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, 0, $inventorycode);
1921  if ($result < 0) {
1922  $this->error = $mouvS->error;
1923  $this->errors = $mouvS->errors;
1924  $error++; break;
1925  }
1926  }
1927  }
1928  } else {
1929  $this->error = $this->db->lasterror();
1930  $error++;
1931  }
1932  }
1933 
1934  if (!$error) {
1935  // Call trigger
1936  $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
1937  if ($result < 0) {
1938  $error++;
1939  }
1940  }
1941  if ($this->origin == 'order_supplier') {
1942  if (!empty($this->origin) && $this->origin_id > 0) {
1943  $this->fetch_origin();
1944  $origin = $this->origin;
1945  if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1946  // Check if there is no more reception validated.
1947  $this->$origin->fetchObjectLinked();
1948  $setStatut = 1;
1949  if (!empty($this->$origin->linkedObjects['reception'])) {
1950  foreach ($this->$origin->linkedObjects['reception'] as $rcption) {
1951  if ($rcption->statut > 0) {
1952  $setStatut = 0;
1953  break;
1954  }
1955  }
1956  //var_dump($this->$origin->receptions);exit;
1957  if ($setStatut) {
1958  $this->$origin->setStatut(3); // ordered
1959  }
1960  }
1961  }
1962  }
1963  }
1964 
1965  if (!$error) {
1966  $this->statut = self::STATUS_DRAFT;
1967  $this->db->commit();
1968  return 1;
1969  } else {
1970  $this->db->rollback();
1971  return -1;
1972  }
1973  } else {
1974  $this->error = $this->db->error();
1975  $this->db->rollback();
1976  return -1;
1977  }
1978  }
1979 
1990  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1991  {
1992  global $conf, $langs;
1993 
1994  $langs->load("receptions");
1995 
1996  if (!dol_strlen($modele)) {
1997  $modele = 'squille';
1998 
1999  if ($this->model_pdf) {
2000  $modele = $this->model_pdf;
2001  } elseif (!empty($conf->global->RECEPTION_ADDON_PDF)) {
2002  $modele = $conf->global->RECEPTION_ADDON_PDF;
2003  }
2004  }
2005 
2006  $modelpath = "core/modules/reception/doc/";
2007 
2008  $this->fetch_origin();
2009 
2010  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2011  }
2012 
2021  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2022  {
2023  $tables = array('reception');
2024 
2025  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2026  }
2027 
2036  public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2037  {
2038  $tables = array(
2039  'commande_fournisseur_dispatch'
2040  );
2041 
2042  return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2043  }
2044 }
$object ref
Definition: info.php:78
Class to manage table commandefournisseurdispatch.
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_RECEIVED_COMPLETELY
Received completely.
Class to manage line orders.
Parent class of all other business classes (invoices, contracts, proposals, 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...
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='fk_statut')
Set status of an object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
fetch_origin()
Read linked origin object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Class to manage Dolibarr database access.
Class to manage standard extra fields.
Class to manage stock movements.
Class to manage products or services.
Class to manage receptions.
setBilled()
Classify the reception as invoiced (used when WORKFLOW_EXPEDITION_CLASSIFY_CLOSED_INVOICE is on)
fetch_delivery_methods()
Fetch deliveries method and return an array.
getLibStatut($mode=0)
Return status label.
valid($user, $notrigger=0)
Validate object and update stock if option enabled.
getUrlTrackingStatus($value='')
Forge an set tracking url.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getNomUrl($withpicto=0, $option=0, $max=0, $short=0, $notooltip=0)
Return clicable link of object (with eventually picto)
update($user=null, $notrigger=0)
Update database.
setClosed()
Classify the reception as closed (this record also the stock movement)
getNextNumRef($soc)
Return next contract ref.
static replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a product id with another one.
LibStatut($status, $mode)
Return label of a status.
addline($entrepot_id, $id, $qty, $array_options=0, $comment='', $eatby='', $sellby='', $batch='', $cost_price=0)
Add an reception line.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
list_delivery_methods($id='')
Fetch all deliveries method and return an array.
setDeliveryDate($user, $delivery_date)
Set the planned delivery date.
__construct($db)
Constructor.
initAsSpecimen()
Initialise an instance with random values.
fetch_lines()
Load lines.
create($user, $notrigger=0)
Create reception en base.
fetch($id, $ref='', $ref_ext='')
Get object and lines from database.
reOpen()
Classify the reception as validated/opened.
getStatusDispatch()
Get status from all dispatched lines.
setDraft($user)
Set draft status.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:48
trait CommonIncoterm
Superclass for incoterm classes.
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
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:1507
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:1356
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
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)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
$conf db user
Definition: repair.php:124