dolibarr  17.0.4
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 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 
100 
104  public $date_reception;
105 
109  public $date_creation;
110 
114  public $date_valid;
115 
116  public $meths;
117  public $listmeths; // List of carriers
118 
122  public $lines = array();
123 
124 
125  // detail of lot and qty = array(id in llx_commande_fournisseur_dispatch, batch, qty)
126  // We can use this to know warehouse planned to be used for each lot.
127  public $detail_batch;
128 
129  const STATUS_DRAFT = 0;
130  const STATUS_VALIDATED = 1;
131  const STATUS_CLOSED = 2;
132 
133 
134 
140  public function __construct($db)
141  {
142  $this->db = $db;
143 
144  // List of long language codes for status
145  $this->statuts = array();
146  $this->statuts[-1] = 'StatusReceptionCanceled';
147  $this->statuts[0] = 'StatusReceptionDraft';
148  // product to receive if stock increase is on close or already received if stock increase is on validation
149  $this->statuts[1] = 'StatusReceptionValidated';
150  if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
151  $this->statuts[1] = 'StatusReceptionValidatedReceived';
152  }
153  if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
154  $this->statuts[1] = 'StatusReceptionValidatedToReceive';
155  }
156  $this->statuts[2] = 'StatusReceptionProcessed';
157 
158  // List of short language codes for status
159  $this->statuts_short = array();
160  $this->statuts_short[-1] = 'StatusReceptionCanceledShort';
161  $this->statuts_short[0] = 'StatusReceptionDraftShort';
162  $this->statuts_short[1] = 'StatusReceptionValidatedShort';
163  $this->statuts_short[2] = 'StatusReceptionProcessedShort';
164  }
165 
172  public function getNextNumRef($soc)
173  {
174  global $langs, $conf;
175  $langs->load("receptions");
176 
177  if (!empty($conf->global->RECEPTION_ADDON_NUMBER)) {
178  $mybool = false;
179 
180  $file = $conf->global->RECEPTION_ADDON_NUMBER.".php";
181  $classname = $conf->global->RECEPTION_ADDON_NUMBER;
182 
183  // Include file with class
184  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
185 
186  foreach ($dirmodels as $reldir) {
187  $dir = dol_buildpath($reldir."core/modules/reception/");
188 
189  // Load file with numbering class (if found)
190  $mybool |= @include_once $dir.$file;
191  }
192 
193  if (!$mybool) {
194  dol_print_error('', "Failed to include file ".$file);
195  return '';
196  }
197 
198  $obj = new $classname();
199 
200  $numref = "";
201  $numref = $obj->getNextValue($soc, $this);
202 
203  if ($numref != "") {
204  return $numref;
205  } else {
206  dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
207  return "";
208  }
209  } else {
210  print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
211  return "";
212  }
213  }
214 
222  public function create($user, $notrigger = 0)
223  {
224  global $conf, $hookmanager;
225 
226  $now = dol_now();
227 
228  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
229  $error = 0;
230 
231  // Clean parameters
232  $this->brouillon = 1;
233  $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
234  if (empty($this->fk_project)) {
235  $this->fk_project = 0;
236  }
237  if (empty($this->weight_units)) {
238  $this->weight_units = 0;
239  }
240  if (empty($this->size_units)) {
241  $this->size_units = 0;
242  }
243 
244  $this->user = $user;
245 
246  $this->db->begin();
247 
248  $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
249  $sql .= "ref";
250  $sql .= ", entity";
251  $sql .= ", ref_supplier";
252  $sql .= ", date_creation";
253  $sql .= ", fk_user_author";
254  $sql .= ", date_reception";
255  $sql .= ", date_delivery";
256  $sql .= ", fk_soc";
257  $sql .= ", fk_projet";
258  $sql .= ", fk_shipping_method";
259  $sql .= ", tracking_number";
260  $sql .= ", weight";
261  $sql .= ", size";
262  $sql .= ", width";
263  $sql .= ", height";
264  $sql .= ", weight_units";
265  $sql .= ", size_units";
266  $sql .= ", note_private";
267  $sql .= ", note_public";
268  $sql .= ", model_pdf";
269  $sql .= ", fk_incoterms, location_incoterms";
270  $sql .= ") VALUES (";
271  $sql .= "'(PROV)'";
272  $sql .= ", ".((int) $conf->entity);
273  $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
274  $sql .= ", '".$this->db->idate($now)."'";
275  $sql .= ", ".((int) $user->id);
276  $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
277  $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
278  $sql .= ", ".((int) $this->socid);
279  $sql .= ", ".((int) $this->fk_project);
280  $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
281  $sql .= ", '".$this->db->escape($this->tracking_number)."'";
282  $sql .= ", ".(is_null($this->weight) ? "NULL" : ((double) $this->weight));
283  $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((double) $this->trueDepth));
284  $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((double) $this->trueWidth));
285  $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((double) $this->trueHeight));
286  $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((double) $this->weight_units));
287  $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((double) $this->size_units));
288  $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
289  $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
290  $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
291  $sql .= ", ".(int) $this->fk_incoterms;
292  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
293  $sql .= ")";
294 
295  dol_syslog(get_class($this)."::create", LOG_DEBUG);
296 
297  $resql = $this->db->query($sql);
298 
299  if ($resql) {
300  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
301 
302  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
303  $sql .= " SET ref = '(PROV".$this->id.")'";
304  $sql .= " WHERE rowid = ".((int) $this->id);
305 
306  dol_syslog(get_class($this)."::create", LOG_DEBUG);
307  if ($this->db->query($sql)) {
308  // Insert of lines
309  $num = count($this->lines);
310  for ($i = 0; $i < $num; $i++) {
311  $this->lines[$i]->fk_reception = $this->id;
312 
313  if (!$this->lines[$i]->create($user) > 0) {
314  $error++;
315  }
316  }
317 
318  if (!$error && $this->id && $this->origin_id) {
319  $ret = $this->add_object_linked();
320  if (!$ret) {
321  $error++;
322  }
323  }
324 
325  // Create extrafields
326  if (!$error) {
327  $result = $this->insertExtraFields();
328  if ($result < 0) {
329  $error++;
330  }
331  }
332 
333  if (!$error && !$notrigger) {
334  // Call trigger
335  $result = $this->call_trigger('RECEPTION_CREATE', $user);
336  if ($result < 0) {
337  $error++;
338  }
339  // End call triggers
340  }
341 
342  if (!$error) {
343  $this->db->commit();
344  return $this->id;
345  } else {
346  foreach ($this->errors as $errmsg) {
347  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
348  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
349  }
350  $this->db->rollback();
351  return -1 * $error;
352  }
353  } else {
354  $error++;
355  $this->error = $this->db->lasterror()." - sql=$sql";
356  $this->db->rollback();
357  return -2;
358  }
359  } else {
360  $error++;
361  $this->error = $this->db->error()." - sql=$sql";
362  $this->db->rollback();
363  return -1;
364  }
365  }
366 
367 
368 
377  public function fetch($id, $ref = '', $ref_ext = '')
378  {
379  // Check parameters
380  if (empty($id) && empty($ref) && empty($ref_ext)) {
381  return -1;
382  }
383 
384  $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";
385  $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
386  $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery";
387  $sql .= ", e.fk_shipping_method, e.tracking_number";
388  $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
389  $sql .= ", e.note_private, e.note_public";
390  $sql .= ', e.fk_incoterms, e.location_incoterms';
391  $sql .= ', i.libelle as label_incoterms';
392  $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
393  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
394  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
395  $sql .= " WHERE e.entity IN (".getEntity('reception').")";
396  if ($id) {
397  $sql .= " AND e.rowid=".((int) $id);
398  }
399  if ($ref) {
400  $sql .= " AND e.ref='".$this->db->escape($ref)."'";
401  }
402  if ($ref_ext) {
403  $sql .= " AND e.ref_ext='".$this->db->escape($ref_ext)."'";
404  }
405 
406  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
407  $result = $this->db->query($sql);
408  if ($result) {
409  if ($this->db->num_rows($result)) {
410  $obj = $this->db->fetch_object($result);
411 
412  $this->id = $obj->rowid;
413  $this->entity = $obj->entity;
414  $this->ref = $obj->ref;
415  $this->socid = $obj->socid;
416  $this->ref_supplier = $obj->ref_supplier;
417  $this->ref_ext = $obj->ref_ext;
418  $this->statut = $obj->fk_statut;
419  $this->user_author_id = $obj->fk_user_author;
420  $this->date_creation = $this->db->jdate($obj->date_creation);
421  $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
422  $this->date_reception = $this->db->jdate($obj->date_reception); // TODO deprecated
423  $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
424  $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planed
425  $this->model_pdf = $obj->model_pdf;
426  $this->modelpdf = $obj->model_pdf; // deprecated
427  $this->shipping_method_id = $obj->fk_shipping_method;
428  $this->tracking_number = $obj->tracking_number;
429  $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
430  $this->origin_id = $obj->origin_id;
431  $this->billed = ($obj->fk_statut == 2 ? 1 : 0);
432 
433  $this->trueWeight = $obj->weight;
434  $this->weight_units = $obj->weight_units;
435 
436  $this->trueWidth = $obj->width;
437  $this->width_units = $obj->size_units;
438  $this->trueHeight = $obj->height;
439  $this->height_units = $obj->size_units;
440  $this->trueDepth = $obj->size;
441  $this->depth_units = $obj->size_units;
442 
443  $this->note_public = $obj->note_public;
444  $this->note_private = $obj->note_private;
445 
446  // A denormalized value
447  $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
448  $this->size_units = $obj->size_units;
449 
450  //Incoterms
451  $this->fk_incoterms = $obj->fk_incoterms;
452  $this->location_incoterms = $obj->location_incoterms;
453  $this->label_incoterms = $obj->label_incoterms;
454 
455  $this->db->free($result);
456 
457  if ($this->statut == 0) {
458  $this->brouillon = 1;
459  }
460 
461  //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
462  //$this->pdf_filename = $file;
463 
464  // Tracking url
465  $this->getUrlTrackingStatus($obj->tracking_number);
466 
467  /*
468  * Thirdparty
469  */
470  $result = $this->fetch_thirdparty();
471 
472 
473  // Retrieve all extrafields for reception
474  // fetch optionals attributes and labels
475  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
476  $extrafields = new ExtraFields($this->db);
477  $extrafields->fetch_name_optionals_label($this->table_element, true);
478  $this->fetch_optionals();
479 
480  /*
481  * Lines
482  */
483  $result = $this->fetch_lines();
484  if ($result < 0) {
485  return -3;
486  }
487 
488  return 1;
489  } else {
490  dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
491  $this->error = 'Delivery with id '.$id.' not found';
492  return 0;
493  }
494  } else {
495  $this->error = $this->db->error();
496  return -1;
497  }
498  }
499 
507  public function valid($user, $notrigger = 0)
508  {
509  global $conf, $langs;
510 
511  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
512 
513  dol_syslog(get_class($this)."::valid");
514 
515  // Protection
516  if ($this->statut) {
517  dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
518  return 0;
519  }
520 
521  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer))
522  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate)))) {
523  $this->error = 'Permission denied';
524  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
525  return -1;
526  }
527 
528  $this->db->begin();
529 
530  $error = 0;
531 
532  // Define new ref
533  $soc = new Societe($this->db);
534  $soc->fetch($this->socid);
535 
536 
537  // Define new ref
538  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
539  $numref = $this->getNextNumRef($soc);
540  } else {
541  $numref = $this->ref;
542  }
543 
544  $this->newref = dol_sanitizeFileName($numref);
545 
546  $now = dol_now();
547 
548  // Validate
549  $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
550  $sql .= " ref='".$this->db->escape($numref)."'";
551  $sql .= ", fk_statut = 1";
552  $sql .= ", date_valid = '".$this->db->idate($now)."'";
553  $sql .= ", fk_user_valid = ".$user->id;
554  $sql .= " WHERE rowid = ".((int) $this->id);
555  dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
556  $resql = $this->db->query($sql);
557  if (!$resql) {
558  $this->error = $this->db->lasterror();
559  $error++;
560  }
561 
562  // If stock increment is done on reception (recommanded choice)
563  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION)) {
564  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
565 
566  $langs->load("agenda");
567 
568  // Loop on each product line to add a stock movement
569  // TODO in future, reception lines may not be linked to order line
570  $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
571  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
572  $sql .= " ed.eatby, ed.sellby, ed.batch,";
573  $sql .= " ed.cost_price";
574  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
575  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
576  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
577  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
578 
579  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
580  $resql = $this->db->query($sql);
581  if ($resql) {
582  $cpt = $this->db->num_rows($resql);
583  for ($i = 0; $i < $cpt; $i++) {
584  $obj = $this->db->fetch_object($resql);
585 
586  $qty = $obj->qty;
587 
588  if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
589  continue;
590  }
591  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
592 
593  //var_dump($this->lines[$i]);
594  $mouvS = new MouvementStock($this->db);
595  $mouvS->origin = &$this;
596  $mouvS->setOrigin($this->element, $this->id);
597 
598  if (empty($obj->batch)) {
599  // line without batch detail
600 
601  // 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.
602  $inventorycode = '';
603  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
604 
605  if (intval($result) < 0) {
606  $error++;
607  $this->errors[] = $mouvS->error;
608  $this->errors = array_merge($this->errors, $mouvS->errors);
609  break;
610  }
611  } else {
612  // line with batch detail
613 
614  // 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.
615  // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
616  $inventorycode = '';
617  $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);
618 
619  if (intval($result) < 0) {
620  $error++;
621  $this->errors[] = $mouvS->error;
622  $this->errors = array_merge($this->errors, $mouvS->errors);
623  break;
624  }
625  }
626  }
627  } else {
628  $this->db->rollback();
629  $this->error = $this->db->error();
630  return -2;
631  }
632  }
633 
634  // Change status of order to "reception in process" or "totally received"
635  $status = $this->getStatusDispatch();
636  if ($status < 0) {
637  $error++;
638  } else {
639  $trigger_key = '';
641  $ret = $this->commandeFournisseur->Livraison($user, dol_now(), 'tot', '');
642  if ($ret < 0) {
643  $error++;
644  $this->errors = array_merge($this->errors, $this->commandeFournisseur->errors);
645  }
646  } else {
647  $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
648  if ($ret < 0) {
649  $error++;
650  }
651  }
652  }
653 
654  if (!$error && !$notrigger) {
655  // Call trigger
656  $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
657  if ($result < 0) {
658  $error++;
659  }
660  // End call triggers
661  }
662 
663  if (!$error) {
664  $this->oldref = $this->ref;
665 
666  // Rename directory if dir was a temporary ref
667  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
668  // Now we rename also files into index
669  $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)."'";
670  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
671  $resql = $this->db->query($sql);
672  if (!$resql) {
673  $error++; $this->error = $this->db->lasterror();
674  }
675 
676  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
677  $oldref = dol_sanitizeFileName($this->ref);
678  $newref = dol_sanitizeFileName($numref);
679  $dirsource = $conf->reception->dir_output.'/'.$oldref;
680  $dirdest = $conf->reception->dir_output.'/'.$newref;
681  if (!$error && file_exists($dirsource)) {
682  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
683 
684  if (@rename($dirsource, $dirdest)) {
685  dol_syslog("Rename ok");
686  // Rename docs starting with $oldref with $newref
687  $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
688  foreach ($listoffiles as $fileentry) {
689  $dirsource = $fileentry['name'];
690  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
691  $dirsource = $fileentry['path'].'/'.$dirsource;
692  $dirdest = $fileentry['path'].'/'.$dirdest;
693  @rename($dirsource, $dirdest);
694  }
695  }
696  }
697  }
698  }
699 
700  // Set new ref and current status
701  if (!$error) {
702  $this->ref = $numref;
703  $this->statut = 1;
704  }
705 
706  if (!$error) {
707  $this->db->commit();
708  return 1;
709  } else {
710  foreach ($this->errors as $errmsg) {
711  dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
712  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
713  }
714  $this->db->rollback();
715  return -1 * $error;
716  }
717  }
718 
724  public function getStatusDispatch()
725  {
726  global $conf;
727 
728  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
729  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
730 
732 
733  if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
734  if (empty($this->commandeFournisseur)) {
735  $this->fetch_origin();
736  if (empty($this->commandeFournisseur->lines)) {
737  $res = $this->commandeFournisseur->fetch_lines();
738  if ($res < 0) return $res;
739  }
740  }
741 
742  $qty_received = array();
743  $qty_wished = array();
744 
745  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
746  $filter = array('t.fk_commande'=>$this->origin_id);
747  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
748  $filter['t.status'] = 1; // Restrict to lines with status validated
749  }
750 
751  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
752  if ($ret < 0) {
753  $this->error = $supplierorderdispatch->error;
754  $this->errors = $supplierorderdispatch->errors;
755  return $ret;
756  } else {
757  // build array with quantity received by product in all supplier orders (origin)
758  foreach ($supplierorderdispatch->lines as $dispatch_line) {
759  $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
760  }
761 
762  // qty wished in order supplier (origin)
763  foreach ($this->commandeFournisseur->lines as $origin_line) {
764  // exclude lines not qualified for reception
765  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $origin_line->product_type > 0) {
766  continue;
767  }
768 
769  $qty_wished[$origin_line->fk_product] += $origin_line->qty;
770  }
771 
772  // compare array
773  $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
774  $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
775  $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
776 
777  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
779  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
780  // set totally received if more products received than ordered
781  $close = 0;
782 
783  if (count($diff_array) > 0) {
784  // there are some difference between the two arrays
785  // scan the array of results
786  foreach ($diff_array as $key => $value) {
787  // if the quantity delivered is greater or equal to ordered quantity
788  if ($qty_received[$key] >= $qty_wished[$key]) {
789  $close++;
790  }
791  }
792  }
793 
794  if ($close == count($diff_array)) {
795  // all the products are received equal or more than the ordered quantity
797  }
798  }
799  }
800  }
801 
802  return $status;
803  }
804 
821  public function addline($entrepot_id, $id, $qty, $array_options = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $cost_price = 0)
822  {
823  global $conf, $langs, $user;
824 
825  $num = count($this->lines);
826  $line = new CommandeFournisseurDispatch($this->db);
827 
828  $line->fk_entrepot = $entrepot_id;
829  $line->fk_commandefourndet = $id;
830  $line->qty = $qty;
831 
832  $supplierorderline = new CommandeFournisseurLigne($this->db);
833  $result = $supplierorderline->fetch($id);
834  if ($result <= 0) {
835  $this->error = $supplierorderline->error;
836  $this->errors = $supplierorderline->errors;
837  return -1;
838  }
839 
840  $fk_product = 0;
841  if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
842  $fk_product = $supplierorderline->fk_product;
843 
844  if (!($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS)) {
845  $langs->load("errors");
846  $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
847  return -1;
848  }
849  }
850 
851  // Check batch is set
852  $product = new Product($this->db);
853  $product->fetch($fk_product);
854  if (isModEnabled('productbatch')) {
855  $langs->load("errors");
856  if (!empty($product->status_batch) && empty($batch)) {
857  $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
858  return -1;
859  } elseif (empty($product->status_batch) && !empty($batch)) {
860  $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
861  return -1;
862  }
863  }
864  unset($product);
865 
866  // extrafields
867  $line->array_options = $supplierorderline->array_options;
868  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options) > 0) {
869  foreach ($array_options as $key => $value) {
870  $line->array_options[$key] = $value;
871  }
872  }
873 
874  $line->fk_product = $fk_product;
875  $line->fk_commande = $supplierorderline->fk_commande;
876  $line->fk_user = $user->id;
877  $line->comment = $comment;
878  $line->batch = $batch;
879  $line->eatby = $eatby;
880  $line->sellby = $sellby;
881  $line->status = 1;
882  $line->cost_price = $cost_price;
883  $line->fk_reception = $this->id;
884 
885  $this->lines[$num] = $line;
886 
887  return $num;
888  }
889 
890 
898  public function update($user = null, $notrigger = 0)
899  {
900  global $conf;
901  $error = 0;
902 
903  // Clean parameters
904 
905  if (isset($this->ref)) {
906  $this->ref = trim($this->ref);
907  }
908  if (isset($this->entity)) {
909  $this->entity = trim($this->entity);
910  }
911  if (isset($this->ref_supplier)) {
912  $this->ref_supplier = trim($this->ref_supplier);
913  }
914  if (isset($this->socid)) {
915  $this->socid = trim($this->socid);
916  }
917  if (isset($this->fk_user_author)) {
918  $this->fk_user_author = trim($this->fk_user_author);
919  }
920  if (isset($this->fk_user_valid)) {
921  $this->fk_user_valid = trim($this->fk_user_valid);
922  }
923  if (isset($this->shipping_method_id)) {
924  $this->shipping_method_id = trim($this->shipping_method_id);
925  }
926  if (isset($this->tracking_number)) {
927  $this->tracking_number = trim($this->tracking_number);
928  }
929  if (isset($this->statut)) {
930  $this->statut = (int) $this->statut;
931  }
932  if (isset($this->trueDepth)) {
933  $this->trueDepth = trim($this->trueDepth);
934  }
935  if (isset($this->trueWidth)) {
936  $this->trueWidth = trim($this->trueWidth);
937  }
938  if (isset($this->trueHeight)) {
939  $this->trueHeight = trim($this->trueHeight);
940  }
941  if (isset($this->size_units)) {
942  $this->size_units = trim($this->size_units);
943  }
944  if (isset($this->weight_units)) {
945  $this->weight_units = trim($this->weight_units);
946  }
947  if (isset($this->trueWeight)) {
948  $this->weight = trim($this->trueWeight);
949  }
950  if (isset($this->note_private)) {
951  $this->note_private = trim($this->note_private);
952  }
953  if (isset($this->note_public)) {
954  $this->note_public = trim($this->note_public);
955  }
956  if (isset($this->model_pdf)) {
957  $this->model_pdf = trim($this->model_pdf);
958  }
959 
960 
961  // Check parameters
962  // Put here code to add control on parameters values
963 
964  // Update request
965  $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
966 
967  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
968  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
969  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
970  $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
971  $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
972  $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
973  $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
974  $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
975  $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
976  $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
977  $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
978  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
979  $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
980  $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
981  $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
982  $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
983  $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
984  $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
985  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
986  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
987  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
988  $sql .= " entity = ".((int) $conf->entity);
989  $sql .= " WHERE rowid=".((int) $this->id);
990 
991  $this->db->begin();
992 
993  dol_syslog(get_class($this)."::update", LOG_DEBUG);
994  $resql = $this->db->query($sql);
995  if (!$resql) {
996  $error++; $this->errors[] = "Error ".$this->db->lasterror();
997  }
998 
999  if (!$error) {
1000  if (!$notrigger) {
1001  // Call trigger
1002  $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1003  if ($result < 0) {
1004  $error++;
1005  }
1006  // End call triggers
1007  }
1008  }
1009 
1010  // Commit or rollback
1011  if ($error) {
1012  foreach ($this->errors as $errmsg) {
1013  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1014  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1015  }
1016  $this->db->rollback();
1017  return -1 * $error;
1018  } else {
1019  $this->db->commit();
1020  return 1;
1021  }
1022  }
1023 
1030  public function delete(User $user)
1031  {
1032  global $conf, $langs, $user;
1033  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1034 
1035  $error = 0;
1036  $this->error = '';
1037 
1038 
1039  $this->db->begin();
1040 
1041  // Stock control
1042  if ($conf->stock->enabled && $conf->global->STOCK_CALCULATE_ON_RECEPTION && $this->statut > 0) {
1043  require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1044 
1045  $langs->load("agenda");
1046 
1047  // Loop on each product line to add a stock movement
1048  $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";
1049  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1050  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1051  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1052  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1053 
1054  dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1055  $resql = $this->db->query($sql);
1056  if ($resql) {
1057  $cpt = $this->db->num_rows($resql);
1058  for ($i = 0; $i < $cpt; $i++) {
1059  dol_syslog(get_class($this)."::delete movement index ".$i);
1060  $obj = $this->db->fetch_object($resql);
1061 
1062  $mouvS = new MouvementStock($this->db);
1063  // we do not log origin because it will be deleted
1064  $mouvS->origin = null;
1065 
1066  $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
1067  }
1068  } else {
1069  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1070  }
1071  }
1072 
1073  if (!$error) {
1074  $main = MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
1075  $ef = $main."_extrafields";
1076 
1077  $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1078 
1079  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
1080  $sql .= " WHERE fk_reception = ".((int) $this->id);
1081 
1082  if ($this->db->query($sqlef) && $this->db->query($sql)) {
1083  // Delete linked object
1084  $res = $this->deleteObjectLinked();
1085  if ($res < 0) {
1086  $error++;
1087  }
1088 
1089  if (!$error) {
1090  $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1091  $sql .= " WHERE rowid = ".((int) $this->id);
1092 
1093  if ($this->db->query($sql)) {
1094  // Call trigger
1095  $result = $this->call_trigger('RECEPTION_DELETE', $user);
1096  if ($result < 0) {
1097  $error++;
1098  }
1099  // End call triggers
1100 
1101  if (!empty($this->origin) && $this->origin_id > 0) {
1102  $this->fetch_origin();
1103  $origin = $this->origin;
1104  if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1105  // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1106  $this->$origin->loadReceptions();
1107  //var_dump($this->$origin->receptions);exit;
1108  if (count($this->$origin->receptions) <= 0) {
1109  $this->$origin->setStatut(3); // ordered
1110  }
1111  }
1112  }
1113 
1114  if (!$error) {
1115  $this->db->commit();
1116 
1117  // We delete PDFs
1118  $ref = dol_sanitizeFileName($this->ref);
1119  if (!empty($conf->reception->dir_output)) {
1120  $dir = $conf->reception->dir_output.'/'.$ref;
1121  $file = $dir.'/'.$ref.'.pdf';
1122  if (file_exists($file)) {
1123  if (!dol_delete_file($file)) {
1124  return 0;
1125  }
1126  }
1127  if (file_exists($dir)) {
1128  if (!dol_delete_dir_recursive($dir)) {
1129  $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1130  return 0;
1131  }
1132  }
1133  }
1134 
1135  return 1;
1136  } else {
1137  $this->db->rollback();
1138  return -1;
1139  }
1140  } else {
1141  $this->error = $this->db->lasterror()." - sql=$sql";
1142  $this->db->rollback();
1143  return -3;
1144  }
1145  } else {
1146  $this->error = $this->db->lasterror()." - sql=$sql";
1147  $this->db->rollback();
1148  return -2;
1149  }
1150  } else {
1151  $this->error = $this->db->lasterror()." - sql=$sql";
1152  $this->db->rollback();
1153  return -1;
1154  }
1155  } else {
1156  $this->db->rollback();
1157  return -1;
1158  }
1159  }
1160 
1161  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1167  public function fetch_lines()
1168  {
1169  // phpcs:enable
1170  $this->lines = array();
1171 
1172  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1173 
1174  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch WHERE fk_reception = ".((int) $this->id);
1175  $resql = $this->db->query($sql);
1176 
1177  if (!empty($resql)) {
1178  while ($obj = $this->db->fetch_object($resql)) {
1179  $line = new CommandeFournisseurDispatch($this->db);
1180 
1181  $line->fetch($obj->rowid);
1182 
1183  // TODO Remove or keep this ?
1184  $line->fetch_product();
1185 
1186  $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1187  $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1188  $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1189  $sql_commfourndet .= ' ORDER BY rang';
1190 
1191  $resql_commfourndet = $this->db->query($sql_commfourndet);
1192  if (!empty($resql_commfourndet)) {
1193  $obj = $this->db->fetch_object($resql_commfourndet);
1194  $line->qty_asked = $obj->qty;
1195  $line->description = $obj->description;
1196  $line->desc = $obj->description;
1197  $line->tva_tx = $obj->tva_tx;
1198  $line->vat_src_code = $obj->vat_src_code;
1199  $line->subprice = $obj->subprice;
1200  $line->multicurrency_subprice = $obj->multicurrency_subprice;
1201  $line->remise_percent = $obj->remise_percent;
1202  $line->label = !empty($obj->label) ? $obj->label : $line->product->label;
1203  $line->ref_supplier = $obj->ref;
1204  $line->total_ht = $obj->total_ht;
1205  $line->total_ttc = $obj->total_ttc;
1206  $line->total_tva = $obj->total_tva;
1207  } else {
1208  $line->qty_asked = 0;
1209  $line->description = '';
1210  $line->desc = '';
1211  $line->label = $obj->label;
1212  }
1213 
1214  $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1215  $tva = $pu_ht * $line->tva_tx / 100;
1216  $this->total_ht += $pu_ht;
1217  $this->total_tva += $pu_ht * $line->tva_tx / 100;
1218 
1219  $this->total_ttc += $pu_ht + $tva;
1220 
1221  if (isModEnabled('productbatch') && !empty($line->batch)) {
1222  $detail_batch = new stdClass();
1223  $detail_batch->eatby = $line->eatby;
1224  $detail_batch->sellby = $line->sellby;
1225  $detail_batch->batch = $line->batch;
1226  $detail_batch->qty = $line->qty;
1227  $line->detail_batch[] = $detail_batch;
1228  }
1229 
1230  $this->lines[] = $line;
1231  }
1232 
1233  return 1;
1234  } else {
1235  return -1;
1236  }
1237  }
1238 
1249  public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1250  {
1251  global $conf, $langs, $hookmanager;
1252  $result = '';
1253  $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1254  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1255  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1256 
1257  $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1258 
1259  if ($short) {
1260  return $url;
1261  }
1262 
1263  $linkclose = '';
1264  if (empty($notooltip)) {
1265  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1266  $label = $langs->trans("Reception");
1267  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1268  }
1269  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1270  $linkclose .= ' class="classfortooltip"';
1271  }
1272 
1273  $linkstart = '<a href="'.$url.'"';
1274  $linkstart .= $linkclose.'>';
1275  $linkend = '</a>';
1276 
1277  if ($withpicto) {
1278  $result .= ($linkstart.img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? '' : 'class="classfortooltip"'), 0, 0, $notooltip ? 0 : 1).$linkend);
1279  }
1280  if ($withpicto && $withpicto != 2) {
1281  $result .= ' ';
1282  }
1283  $result .= $linkstart.$this->ref.$linkend;
1284 
1285  global $action;
1286  $hookmanager->initHooks(array($this->element . 'dao'));
1287  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1288  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1289  if ($reshook > 0) {
1290  $result = $hookmanager->resPrint;
1291  } else {
1292  $result .= $hookmanager->resPrint;
1293  }
1294  return $result;
1295  }
1296 
1303  public function getLibStatut($mode = 0)
1304  {
1305  return $this->LibStatut($this->statut, $mode);
1306  }
1307 
1308  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1316  public function LibStatut($status, $mode)
1317  {
1318  // phpcs:enable
1319  global $langs;
1320 
1321  $labelStatus = $langs->transnoentitiesnoconv($this->statuts[$status]);
1322  $labelStatusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
1323 
1324  $statusType = 'status'.$status;
1325  if ($status == self::STATUS_VALIDATED) {
1326  $statusType = 'status4';
1327  }
1328  if ($status == self::STATUS_CLOSED) {
1329  $statusType = 'status6';
1330  }
1331 
1332  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1333  }
1334 
1342  public function initAsSpecimen()
1343  {
1344  global $langs;
1345 
1346  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1347  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1348  $now = dol_now();
1349 
1350  dol_syslog(get_class($this)."::initAsSpecimen");
1351 
1352  // Load array of products prodids
1353  $num_prods = 0;
1354  $prodids = array();
1355  $sql = "SELECT rowid";
1356  $sql .= " FROM ".MAIN_DB_PREFIX."product";
1357  $sql .= " WHERE entity IN (".getEntity('product').")";
1358  $sql .= $this->db->plimit(100);
1359 
1360  $resql = $this->db->query($sql);
1361  if ($resql) {
1362  $num_prods = $this->db->num_rows($resql);
1363  $i = 0;
1364  while ($i < $num_prods) {
1365  $i++;
1366  $row = $this->db->fetch_row($resql);
1367  $prodids[$i] = $row[0];
1368  }
1369  }
1370 
1371  $order = new CommandeFournisseur($this->db);
1372  $order->initAsSpecimen();
1373 
1374  // Initialise parametres
1375  $this->id = 0;
1376  $this->ref = 'SPECIMEN';
1377  $this->specimen = 1;
1378  $this->statut = 1;
1379  $this->livraison_id = 0;
1380  $this->date = $now;
1381  $this->date_creation = $now;
1382  $this->date_valid = $now;
1383  $this->date_delivery = $now;
1384  $this->date_reception = $now + 24 * 3600;
1385 
1386  $this->entrepot_id = 0;
1387  $this->socid = 1;
1388 
1389  $this->commande_id = 0;
1390  $this->commande = $order;
1391 
1392  $this->origin_id = 1;
1393  $this->origin = 'commande';
1394 
1395  $this->note_private = 'Private note';
1396  $this->note_public = 'Public note';
1397 
1398  $nbp = 5;
1399  $xnbp = 0;
1400  while ($xnbp < $nbp) {
1401  $line = new CommandeFournisseurDispatch($this->db);
1402  $line->desc = $langs->trans("Description")." ".$xnbp;
1403  $line->libelle = $langs->trans("Description")." ".$xnbp;
1404  $line->qty = 10;
1405 
1406  $line->fk_product = $this->commande->lines[$xnbp]->fk_product;
1407 
1408  $this->lines[] = $line;
1409  $xnbp++;
1410  }
1411  }
1412 
1420  public function setDeliveryDate($user, $delivery_date)
1421  {
1422  // phpcs:enable
1423  if ($user->rights->reception->creer) {
1424  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1425  $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1426  $sql .= " WHERE rowid = ".((int) $this->id);
1427 
1428  dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1429  $resql = $this->db->query($sql);
1430  if ($resql) {
1431  $this->date_delivery = $delivery_date;
1432  return 1;
1433  } else {
1434  $this->error = $this->db->error();
1435  return -1;
1436  }
1437  } else {
1438  return -2;
1439  }
1440  }
1441 
1442  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1448  public function fetch_delivery_methods()
1449  {
1450  // phpcs:enable
1451  global $langs;
1452  $this->meths = array();
1453 
1454  $sql = "SELECT em.rowid, em.code, em.libelle";
1455  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1456  $sql .= " WHERE em.active = 1";
1457  $sql .= " ORDER BY em.libelle ASC";
1458 
1459  $resql = $this->db->query($sql);
1460  if ($resql) {
1461  while ($obj = $this->db->fetch_object($resql)) {
1462  $label = $langs->trans('ReceptionMethod'.$obj->code);
1463  $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1464  }
1465  }
1466  }
1467 
1468  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1475  public function list_delivery_methods($id = '')
1476  {
1477  // phpcs:enable
1478  global $langs;
1479 
1480  $this->listmeths = array();
1481  $i = 0;
1482 
1483  $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1484  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1485  if ($id != '') {
1486  $sql .= " WHERE em.rowid = ".((int) $id);
1487  }
1488 
1489  $resql = $this->db->query($sql);
1490  if ($resql) {
1491  while ($obj = $this->db->fetch_object($resql)) {
1492  $this->listmeths[$i]['rowid'] = $obj->rowid;
1493  $this->listmeths[$i]['code'] = $obj->code;
1494  $label = $langs->trans('ReceptionMethod'.$obj->code);
1495  $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1496  $this->listmeths[$i]['description'] = $obj->description;
1497  $this->listmeths[$i]['tracking'] = $obj->tracking;
1498  $this->listmeths[$i]['active'] = $obj->active;
1499  $i++;
1500  }
1501  }
1502  }
1503 
1510  public function getUrlTrackingStatus($value = '')
1511  {
1512  if (!empty($this->shipping_method_id)) {
1513  $sql = "SELECT em.code, em.tracking";
1514  $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1515  $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1516 
1517  $resql = $this->db->query($sql);
1518  if ($resql) {
1519  if ($obj = $this->db->fetch_object($resql)) {
1520  $tracking = $obj->tracking;
1521  }
1522  }
1523  }
1524 
1525  if (!empty($tracking) && !empty($value)) {
1526  $url = str_replace('{TRACKID}', $value, $tracking);
1527  $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">'.($value ? $value : 'url').'</a>', $url, $url);
1528  } else {
1529  $this->tracking_url = $value;
1530  }
1531  }
1532 
1538  public function setClosed()
1539  {
1540  global $conf, $langs, $user;
1541 
1542  $error = 0;
1543 
1544  $this->db->begin();
1545 
1546  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut='.self::STATUS_CLOSED;
1547  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1548 
1549  $resql = $this->db->query($sql);
1550  if ($resql) {
1551  // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1552  if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1553  $order = new CommandeFournisseur($this->db);
1554  $order->fetch($this->origin_id);
1555 
1556  $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1557 
1558  $receptions_match_order = 1;
1559  foreach ($order->lines as $line) {
1560  $lineid = $line->id;
1561  $qty = $line->qty;
1562  if (($line->product_type == 0 || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) && $order->receptions[$lineid] < $qty) {
1563  $receptions_match_order = 0;
1564  $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';
1565  dol_syslog($text);
1566  break;
1567  }
1568  }
1569  if ($receptions_match_order) {
1570  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');
1571  $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1572  }
1573  }
1574 
1575  $this->statut = self::STATUS_CLOSED;
1576 
1577 
1578  // If stock increment is done on closing
1579  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) {
1580  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1581 
1582  $langs->load("agenda");
1583 
1584  // Loop on each product line to add a stock movement
1585  // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1586  $sql = "SELECT cd.fk_product, cd.subprice,";
1587  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1588  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1589  $sql .= " ed.cost_price";
1590  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1591  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1592  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1593  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1594 
1595  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1596  $resql = $this->db->query($sql);
1597 
1598  if ($resql) {
1599  $cpt = $this->db->num_rows($resql);
1600  for ($i = 0; $i < $cpt; $i++) {
1601  $obj = $this->db->fetch_object($resql);
1602 
1603  $qty = $obj->qty;
1604 
1605  if ($qty <= 0) {
1606  continue;
1607  }
1608  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1609 
1610  $mouvS = new MouvementStock($this->db);
1611  $mouvS->origin = &$this;
1612  $mouvS->setOrigin($this->element, $this->id);
1613 
1614  if (empty($obj->batch)) {
1615  // line without batch detail
1616 
1617  // 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
1618  $inventorycode = '';
1619  $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1620  if ($result < 0) {
1621  $this->error = $mouvS->error;
1622  $this->errors = $mouvS->errors;
1623  $error++; break;
1624  }
1625  } else {
1626  // line with batch detail
1627 
1628  // 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
1629  $inventorycode = '';
1630  $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);
1631 
1632  if ($result < 0) {
1633  $this->error = $mouvS->error;
1634  $this->errors = $mouvS->errors;
1635  $error++; break;
1636  }
1637  }
1638  }
1639  } else {
1640  $this->error = $this->db->lasterror();
1641  $error++;
1642  }
1643  }
1644 
1645  // Call trigger
1646  if (!$error) {
1647  $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1648  if ($result < 0) {
1649  $error++;
1650  }
1651  }
1652  } else {
1653  dol_print_error($this->db);
1654  $error++;
1655  }
1656 
1657  if (!$error) {
1658  $this->db->commit();
1659  return 1;
1660  } else {
1661  $this->db->rollback();
1662  return -1;
1663  }
1664  }
1665 
1671  public function setBilled()
1672  {
1673  global $user;
1674  $error = 0;
1675 
1676  $this->db->begin();
1677 
1678  $this->setClosed();
1679 
1680  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1681  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1682 
1683  $resql = $this->db->query($sql);
1684  if ($resql) {
1685  $this->statut = 2;
1686  $this->billed = 1;
1687 
1688  // Call trigger
1689  $result = $this->call_trigger('RECEPTION_BILLED', $user);
1690  if ($result < 0) {
1691  $error++;
1692  }
1693  } else {
1694  $error++;
1695  $this->errors[] = $this->db->lasterror;
1696  }
1697 
1698  if (empty($error)) {
1699  $this->db->commit();
1700  return 1;
1701  } else {
1702  $this->db->rollback();
1703  return -1;
1704  }
1705  }
1706 
1712  public function reOpen()
1713  {
1714  global $conf, $langs, $user;
1715 
1716  $error = 0;
1717 
1718  $this->db->begin();
1719 
1720  $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1721  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1722 
1723  $resql = $this->db->query($sql);
1724  if ($resql) {
1725  $this->statut = 1;
1726  $this->billed = 0;
1727 
1728  // If stock increment is done on closing
1729  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) {
1730  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1731  $numref = $this->ref;
1732  $langs->load("agenda");
1733 
1734  // Loop on each product line to add a stock movement
1735  // TODO possibilite de receptionner a partir d'une propale ou autre origine
1736  $sql = "SELECT ed.fk_product, cd.subprice,";
1737  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1738  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1739  $sql .= " ed.cost_price";
1740  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1741  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1742  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1743  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1744 
1745  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1746  $resql = $this->db->query($sql);
1747  if ($resql) {
1748  $cpt = $this->db->num_rows($resql);
1749  for ($i = 0; $i < $cpt; $i++) {
1750  $obj = $this->db->fetch_object($resql);
1751 
1752  $qty = $obj->qty;
1753 
1754  if ($qty <= 0) {
1755  continue;
1756  }
1757 
1758  dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1759 
1760  //var_dump($this->lines[$i]);
1761  $mouvS = new MouvementStock($this->db);
1762  $mouvS->origin = &$this;
1763  $mouvS->setOrigin($this->element, $this->id);
1764 
1765  if (empty($obj->batch)) {
1766  // line without batch detail
1767 
1768  // 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
1769  $inventorycode = '';
1770  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1771 
1772  if ($result < 0) {
1773  $this->error = $mouvS->error;
1774  $this->errors = $mouvS->errors;
1775  $error++; break;
1776  }
1777  } else {
1778  // line with batch detail
1779 
1780  // 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
1781  $inventorycode = '';
1782  $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);
1783  if ($result < 0) {
1784  $this->error = $mouvS->error;
1785  $this->errors = $mouvS->errors;
1786  $error++; break;
1787  }
1788  }
1789  }
1790  } else {
1791  $this->error = $this->db->lasterror();
1792  $error++;
1793  }
1794  }
1795 
1796  if (!$error) {
1797  // Call trigger
1798  $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1799  if ($result < 0) {
1800  $error++;
1801  }
1802  }
1803 
1804  if (!$error && $this->origin == 'order_supplier') {
1805  $commande = new CommandeFournisseur($this->db);
1806  $commande->fetch($this->origin_id);
1807  $result = $commande->setStatus($user, 4);
1808  if ($result < 0) {
1809  $error++;
1810  $this->error = $commande->error;
1811  $this->errors = $commande->errors;
1812  }
1813  }
1814  } else {
1815  $error++;
1816  $this->errors[] = $this->db->lasterror();
1817  }
1818 
1819  if (!$error) {
1820  $this->db->commit();
1821  return 1;
1822  } else {
1823  $this->db->rollback();
1824  return -1;
1825  }
1826  }
1827 
1834  public function setDraft($user)
1835  {
1836  // phpcs:enable
1837  global $conf, $langs;
1838 
1839  $error = 0;
1840 
1841  // Protection
1842  if ($this->statut <= self::STATUS_DRAFT) {
1843  return 0;
1844  }
1845 
1846  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer))
1847  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate)))) {
1848  $this->error = 'Permission denied';
1849  return -1;
1850  }
1851 
1852  $this->db->begin();
1853 
1854  $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1855  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1856  $sql .= " WHERE rowid = ".((int) $this->id);
1857 
1858  dol_syslog(__METHOD__, LOG_DEBUG);
1859  if ($this->db->query($sql)) {
1860  // If stock increment is done on closing
1861  if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION)) {
1862  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1863 
1864  $langs->load("agenda");
1865 
1866  // Loop on each product line to add a stock movement
1867  // TODO possibilite de receptionner a partir d'une propale ou autre origine
1868  $sql = "SELECT cd.fk_product, cd.subprice,";
1869  $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1870  $sql .= " ed.eatby, ed.sellby, ed.batch,";
1871  $sql .= " ed.cost_price";
1872  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1873  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1874  $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1875  $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1876 
1877  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1878  $resql = $this->db->query($sql);
1879  if ($resql) {
1880  $cpt = $this->db->num_rows($resql);
1881  for ($i = 0; $i < $cpt; $i++) {
1882  $obj = $this->db->fetch_object($resql);
1883 
1884  $qty = $obj->qty;
1885 
1886 
1887  if ($qty <= 0) {
1888  continue;
1889  }
1890  dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1891 
1892  //var_dump($this->lines[$i]);
1893  $mouvS = new MouvementStock($this->db);
1894  $mouvS->origin = &$this;
1895  $mouvS->setOrigin($this->element, $this->id);
1896 
1897  if (empty($obj->batch)) {
1898  // line without batch detail
1899 
1900  // 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
1901  $inventorycode = '';
1902  $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1903  if ($result < 0) {
1904  $this->error = $mouvS->error;
1905  $this->errors = $mouvS->errors;
1906  $error++;
1907  break;
1908  }
1909  } else {
1910  // line with batch detail
1911 
1912  // 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
1913  $inventorycode = '';
1914  $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);
1915  if ($result < 0) {
1916  $this->error = $mouvS->error;
1917  $this->errors = $mouvS->errors;
1918  $error++; break;
1919  }
1920  }
1921  }
1922  } else {
1923  $this->error = $this->db->lasterror();
1924  $error++;
1925  }
1926  }
1927 
1928  if (!$error) {
1929  // Call trigger
1930  $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
1931  if ($result < 0) {
1932  $error++;
1933  }
1934  }
1935  if ($this->origin == 'order_supplier') {
1936  if (!empty($this->origin) && $this->origin_id > 0) {
1937  $this->fetch_origin();
1938  $origin = $this->origin;
1939  if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1940  // Check if there is no more reception validated.
1941  $this->$origin->fetchObjectLinked();
1942  $setStatut = 1;
1943  if (!empty($this->$origin->linkedObjects['reception'])) {
1944  foreach ($this->$origin->linkedObjects['reception'] as $rcption) {
1945  if ($rcption->statut > 0) {
1946  $setStatut = 0;
1947  break;
1948  }
1949  }
1950  //var_dump($this->$origin->receptions);exit;
1951  if ($setStatut) {
1952  $this->$origin->setStatut(3); // ordered
1953  }
1954  }
1955  }
1956  }
1957  }
1958 
1959  if (!$error) {
1960  $this->statut = self::STATUS_DRAFT;
1961  $this->db->commit();
1962  return 1;
1963  } else {
1964  $this->db->rollback();
1965  return -1;
1966  }
1967  } else {
1968  $this->error = $this->db->error();
1969  $this->db->rollback();
1970  return -1;
1971  }
1972  }
1973 
1984  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1985  {
1986  global $conf, $langs;
1987 
1988  $langs->load("receptions");
1989 
1990  if (!dol_strlen($modele)) {
1991  $modele = 'squille';
1992 
1993  if ($this->model_pdf) {
1994  $modele = $this->model_pdf;
1995  } elseif (!empty($conf->global->RECEPTION_ADDON_PDF)) {
1996  $modele = $conf->global->RECEPTION_ADDON_PDF;
1997  }
1998  }
1999 
2000  $modelpath = "core/modules/reception/doc/";
2001 
2002  $this->fetch_origin();
2003 
2004  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2005  }
2006 
2015  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2016  {
2017  $tables = array('reception');
2018 
2019  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2020  }
2021 
2030  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2031  {
2032  $tables = array(
2033  'commande_fournisseur_dispatch'
2034  );
2035 
2036  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2037  }
2038 }
$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 commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty 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.
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.
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.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetch_lines()
Load lines.
create($user, $notrigger=0)
Create reception en base.
fetch($id, $ref='', $ref_ext='')
Get object and lines from database.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
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:47
trait CommonIncoterm
Superclass for incoterm classes.
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
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1402
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1251
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:61
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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:123
$conf db
API class for accounts.
Definition: inc.php:41