dolibarr 21.0.0-alpha
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-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
37require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
38require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
40if (isModEnabled("propal")) {
41 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
42}
43if (isModEnabled('order')) {
44 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
45}
46
47
52{
54
58 public $code = "";
59
63 public $element = "reception";
64
68 public $fk_element = "fk_reception";
69 public $table_element = "reception";
70 public $table_element_line = "receptiondet_batch";
71
75 public $picto = 'dollyrevert';
76
77 public $socid;
78 public $ref_supplier;
79
80 public $entrepot_id;
81 public $tracking_number;
82 public $tracking_url;
83 public $billed;
84 public $model_pdf;
85
86 public $weight;
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 public $size_units;
98 public $user_author_id;
99
100 public $date_delivery; // Date delivery planned
101
107 public $date;
108
112 public $date_reception;
113
117 public $date_valid;
118
119 public $meths;
120 public $listmeths; // List of carriers
121
125 public $lines = array();
126
127
128 // detail of lot and qty = array(id in receptiondet_batch, batch, qty)
129 // We can use this to know warehouse planned to be used for each lot.
130 public $detail_batch;
131
132 const STATUS_DRAFT = 0;
133 const STATUS_VALIDATED = 1;
134 const STATUS_CLOSED = 2;
135
136
137
143 public function __construct($db)
144 {
145 $this->db = $db;
146
147 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
148 }
149
156 public function getNextNumRef($soc)
157 {
158 global $langs, $conf;
159 $langs->load("receptions");
160
161 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
162 $mybool = false;
163
164 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
165 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
166
167 // Include file with class
168 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
169
170 foreach ($dirmodels as $reldir) {
171 $dir = dol_buildpath($reldir."core/modules/reception/");
172
173 // Load file with numbering class (if found)
174 $mybool = ((bool) @include_once $dir.$file) || $mybool;
175 }
176
177 if (!$mybool) {
178 dol_print_error(null, "Failed to include file ".$file);
179 return '';
180 }
181
182 $obj = new $classname();
183
184 $numref = "";
185 $numref = $obj->getNextValue($soc, $this);
186
187 if ($numref != "") {
188 return $numref;
189 } else {
190 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
191 return "";
192 }
193 } else {
194 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
195 return "";
196 }
197 }
198
206 public function create($user, $notrigger = 0)
207 {
208 global $conf;
209
210 $now = dol_now();
211
212 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
213 $error = 0;
214
215 // Clean parameters
216 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
217 if (empty($this->fk_project)) {
218 $this->fk_project = 0;
219 }
220 if (empty($this->weight_units)) {
221 $this->weight_units = 0;
222 }
223 if (empty($this->size_units)) {
224 $this->size_units = 0;
225 }
226
227 $this->user = $user;
228
229 $this->db->begin();
230
231 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
232 $sql .= "ref";
233 $sql .= ", entity";
234 $sql .= ", ref_supplier";
235 $sql .= ", date_creation";
236 $sql .= ", fk_user_author";
237 $sql .= ", date_reception";
238 $sql .= ", date_delivery";
239 $sql .= ", fk_soc";
240 $sql .= ", fk_projet";
241 $sql .= ", fk_shipping_method";
242 $sql .= ", tracking_number";
243 $sql .= ", weight";
244 $sql .= ", size";
245 $sql .= ", width";
246 $sql .= ", height";
247 $sql .= ", weight_units";
248 $sql .= ", size_units";
249 $sql .= ", note_private";
250 $sql .= ", note_public";
251 $sql .= ", model_pdf";
252 $sql .= ", fk_incoterms, location_incoterms";
253 $sql .= ") VALUES (";
254 $sql .= "'(PROV)'";
255 $sql .= ", ".((int) $conf->entity);
256 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
257 $sql .= ", '".$this->db->idate($now)."'";
258 $sql .= ", ".((int) $user->id);
259 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
260 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
261 $sql .= ", ".((int) $this->socid);
262 $sql .= ", ".((int) $this->fk_project);
263 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
264 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
265 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
266 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
267 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
268 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
269 $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((float) $this->weight_units));
270 $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((float) $this->size_units));
271 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
272 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
273 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
274 $sql .= ", ".(int) $this->fk_incoterms;
275 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
276 $sql .= ")";
277
278 dol_syslog(get_class($this)."::create", LOG_DEBUG);
279
280 $resql = $this->db->query($sql);
281
282 if ($resql) {
283 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
284
285 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
286 $sql .= " SET ref = '(PROV".((int) $this->id).")'";
287 $sql .= " WHERE rowid = ".((int) $this->id);
288
289 dol_syslog(get_class($this)."::create", LOG_DEBUG);
290 if ($this->db->query($sql)) {
291 // Insert of lines
292 $num = count($this->lines);
293 for ($i = 0; $i < $num; $i++) {
294 $this->lines[$i]->fk_reception = $this->id;
295
296 if (!$this->lines[$i]->create($user) > 0) {
297 $error++;
298 }
299 }
300
301 if (!$error && $this->id && $this->origin_id) {
302 $ret = $this->add_object_linked();
303 if (!$ret) {
304 $error++;
305 }
306 }
307
308 // Create extrafields
309 if (!$error) {
310 $result = $this->insertExtraFields();
311 if ($result < 0) {
312 $error++;
313 }
314 }
315
316 if (!$error && !$notrigger) {
317 // Call trigger
318 $result = $this->call_trigger('RECEPTION_CREATE', $user);
319 if ($result < 0) {
320 $error++;
321 }
322 // End call triggers
323 }
324
325 if (!$error) {
326 $this->db->commit();
327 return $this->id;
328 } else {
329 foreach ($this->errors as $errmsg) {
330 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
331 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
332 }
333 $this->db->rollback();
334 return -1 * $error;
335 }
336 } else {
337 $error++;
338 $this->error = $this->db->lasterror()." - sql=$sql";
339 $this->db->rollback();
340 return -2;
341 }
342 } else {
343 $error++;
344 $this->error = $this->db->error()." - sql=$sql";
345 $this->db->rollback();
346 return -1;
347 }
348 }
349
350
351
360 public function fetch($id, $ref = '', $ref_ext = '')
361 {
362 // Check parameters
363 if (empty($id) && empty($ref) && empty($ref_ext)) {
364 return -1;
365 }
366
367 $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 as status, e.billed";
368 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
369 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery";
370 $sql .= ", e.fk_shipping_method, e.tracking_number";
371 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
372 $sql .= ", e.note_private, e.note_public";
373 $sql .= ', e.fk_incoterms, e.location_incoterms';
374 $sql .= ', i.libelle as label_incoterms';
375 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
376 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
377 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
378 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
379 if ($id) {
380 $sql .= " AND e.rowid = ".((int) $id);
381 }
382 if ($ref) {
383 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
384 }
385 if ($ref_ext) {
386 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
387 }
388
389 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
390 $result = $this->db->query($sql);
391 if ($result) {
392 if ($this->db->num_rows($result)) {
393 $obj = $this->db->fetch_object($result);
394
395 $this->id = $obj->rowid;
396 $this->entity = $obj->entity;
397 $this->ref = $obj->ref;
398 $this->socid = $obj->socid;
399 $this->ref_supplier = $obj->ref_supplier;
400 $this->ref_ext = $obj->ref_ext;
401 $this->statut = $obj->status;
402 $this->status = $obj->status;
403 $this->billed = $obj->billed;
404
405 $this->user_author_id = $obj->fk_user_author;
406 $this->date_creation = $this->db->jdate($obj->date_creation);
407 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
408 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
409 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
410 $this->model_pdf = $obj->model_pdf;
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_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
415 $this->origin_id = $obj->origin_id;
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 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
442 //$this->pdf_filename = $file;
443
444 // Tracking url
445 $this->getUrlTrackingStatus($obj->tracking_number);
446
447 /*
448 * Thirdparty
449 */
450 $result = $this->fetch_thirdparty();
451
452
453 // Retrieve all extrafields for reception
454 // fetch optionals attributes and labels
455 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
456 $extrafields = new ExtraFields($this->db);
457 $extrafields->fetch_name_optionals_label($this->table_element, true);
458 $this->fetch_optionals();
459
460 /*
461 * Lines
462 */
463 $result = $this->fetch_lines();
464 if ($result < 0) {
465 return -3;
466 }
467
468 return 1;
469 } else {
470 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
471 $this->error = 'Reception with id '.$id.' not found';
472 return 0;
473 }
474 } else {
475 $this->error = $this->db->error();
476 return -1;
477 }
478 }
479
487 public function valid($user, $notrigger = 0)
488 {
489 global $conf, $langs;
490
491 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
492
493 dol_syslog(get_class($this)."::valid");
494
495 // Protection
496 if ($this->statut) {
497 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
498 return 0;
499 }
500
501 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
502 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
503 $this->error = 'Permission denied';
504 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
505 return -1;
506 }
507
508 $this->db->begin();
509
510 $error = 0;
511
512 // Define new ref
513 $soc = new Societe($this->db);
514 $soc->fetch($this->socid);
515
516
517 // Define new ref
518 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
519 $numref = $this->getNextNumRef($soc);
520 } else {
521 $numref = $this->ref;
522 }
523
524 $this->newref = dol_sanitizeFileName($numref);
525
526 $now = dol_now();
527
528 // Validate
529 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
530 $sql .= " ref='".$this->db->escape($numref)."'";
531 $sql .= ", fk_statut = 1";
532 $sql .= ", date_valid = '".$this->db->idate($now)."'";
533 $sql .= ", fk_user_valid = ".$user->id;
534 $sql .= " WHERE rowid = ".((int) $this->id);
535 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
536 $resql = $this->db->query($sql);
537 if (!$resql) {
538 $this->error = $this->db->lasterror();
539 $error++;
540 }
541
542 // If stock increment is done on reception (recommended choice)
543 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
544 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
545
546 $langs->load("agenda");
547
548 // Loop on each product line to add a stock movement
549 // TODO in future, reception lines may not be linked to order line
550 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
551 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
552 $sql .= " ed.eatby, ed.sellby, ed.batch,";
553 $sql .= " ed.cost_price";
554 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
555 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
556 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
557 $sql .= " AND cd.rowid = ed.fk_elementdet";
558
559 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
560 $resql = $this->db->query($sql);
561 if ($resql) {
562 $cpt = $this->db->num_rows($resql);
563 for ($i = 0; $i < $cpt; $i++) {
564 $obj = $this->db->fetch_object($resql);
565
566 $qty = $obj->qty;
567
568 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
569 continue;
570 }
571 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
572
573 //var_dump($this->lines[$i]);
574 $mouvS = new MouvementStock($this->db);
575 $mouvS->origin = &$this;
576 $mouvS->setOrigin($this->element, $this->id);
577
578 if (empty($obj->batch)) {
579 // line without batch detail
580
581 // 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.
582 $inventorycode = '';
583 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
584
585 if (intval($result) < 0) {
586 $error++;
587 $this->errors[] = $mouvS->error;
588 $this->errors = array_merge($this->errors, $mouvS->errors);
589 break;
590 }
591 } else {
592 // line with batch detail
593
594 // 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.
595 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
596 $inventorycode = '';
597 $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);
598
599 if (intval($result) < 0) {
600 $error++;
601 $this->errors[] = $mouvS->error;
602 $this->errors = array_merge($this->errors, $mouvS->errors);
603 break;
604 }
605 }
606 }
607 } else {
608 $this->db->rollback();
609 $this->error = $this->db->error();
610 return -2;
611 }
612 }
613
614 if (!$error) {
615 // Change status of purchase order to "reception in process" or "totally received"
616 $status = $this->getStatusDispatch();
617 if ($status < 0) {
618 $error++;
619 } else {
620 $trigger_key = '';
621 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
622 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
623 if ($ret < 0) {
624 $error++;
625 $this->errors = array_merge($this->errors, $this->origin_object->errors);
626 }
627 } else {
628 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
629 if ($ret < 0) {
630 $error++;
631 }
632 }
633 }
634 }
635
636 if (!$error && !$notrigger) {
637 // Call trigger
638 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
639 if ($result < 0) {
640 $error++;
641 }
642 // End call triggers
643 }
644
645 if (!$error) {
646 $this->oldref = $this->ref;
647
648 // Rename directory if dir was a temporary ref
649 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
650 // Now we rename also files into index
651 $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)."'";
652 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
653 $resql = $this->db->query($sql);
654 if (!$resql) {
655 $error++;
656 $this->error = $this->db->lasterror();
657 }
658 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
659 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
660 $resql = $this->db->query($sql);
661 if (!$resql) {
662 $error++;
663 $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 = self::STATUS_VALIDATED;
694 $this->status = self::STATUS_VALIDATED;
695 }
696
697 if (!$error) {
698 $this->db->commit();
699 return 1;
700 } else {
701 foreach ($this->errors as $errmsg) {
702 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
703 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
704 }
705 $this->db->rollback();
706 return -1 * $error;
707 }
708 }
709
715 public function getStatusDispatch()
716 {
717 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
718 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
719
721
722 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
723 if (empty($this->origin_object)) {
724 $this->fetch_origin();
725 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
726 $res = $this->origin_object->fetch_lines();
727 if ($this->origin_object instanceof CommandeFournisseur) {
728 $this->commandeFournisseur = $this->origin_object; // deprecated
729 } else {
730 $this->commandeFournisseur = null; // deprecated
731 }
732 if ($res < 0) {
733 return $res;
734 }
735 }
736 }
737
738 $qty_received = array();
739 $qty_wished = array();
740
741 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
742 $filter = array('t.fk_element' => $this->origin_id);
743 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
744 $filter['t.status'] = 1; // Restrict to lines with status validated
745 }
746
747 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
748 if ($ret < 0) {
749 $this->error = $supplierorderdispatch->error;
750 $this->errors = $supplierorderdispatch->errors;
751 return $ret;
752 } else {
753 // build array with quantity received by product in all supplier orders (origin)
754 foreach ($supplierorderdispatch->lines as $dispatch_line) {
755 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
756 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
757 } else {
758 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
759 }
760 }
761
762 // qty wished in origin (purchase order, ...)
763 foreach ($this->origin_object->lines as $origin_line) {
764 // exclude lines not qualified for reception
765 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
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 (getDolGlobalInt('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 = [], $comment = '', $eatby = null, $sellby = null, $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) && !getDolGlobalInt('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 // check sell-by / eat-by date is mandatory
865 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
866 if (!empty($errorMsgArr)) {
867 $errorMessage = '<b>' . $product->ref . '</b> : ';
868 $errorMessage .= '<ul>';
869 foreach ($errorMsgArr as $errorMsg) {
870 $errorMessage .= '<li>' . $errorMsg . '</li>';
871 }
872 $errorMessage .= '</ul>';
873 $this->error = $errorMessage;
874 return -1;
875 }
876 }
877 unset($product);
878
879 // extrafields
880 $line->array_options = $supplierorderline->array_options;
881 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
882 foreach ($array_options as $key => $value) {
883 $line->array_options[$key] = $value;
884 }
885 }
886
887 $line->fk_product = $fk_product;
888 $line->fk_commande = $supplierorderline->fk_commande;
889 $line->fk_user = $user->id;
890 $line->comment = $comment;
891 $line->batch = $batch;
892 $line->eatby = $eatby;
893 $line->sellby = $sellby;
894 $line->status = 1;
895 $line->cost_price = $cost_price;
896 $line->fk_reception = $this->id;
897
898 $this->lines[$num] = $line;
899
900 return $num;
901 }
902
903
911 public function update($user = null, $notrigger = 0)
912 {
913 global $conf;
914 $error = 0;
915
916 // Clean parameters
917
918 if (isset($this->ref)) {
919 $this->ref = trim($this->ref);
920 }
921 if (isset($this->entity)) {
922 $this->entity = (int) $this->entity;
923 }
924 if (isset($this->ref_supplier)) {
925 $this->ref_supplier = trim($this->ref_supplier);
926 }
927 if (isset($this->socid)) {
928 $this->socid = trim($this->socid);
929 }
930 if (isset($this->fk_user_author)) {
931 $this->fk_user_author = (int) $this->fk_user_author;
932 }
933 if (isset($this->fk_user_valid)) {
934 $this->fk_user_valid = (int) $this->fk_user_valid;
935 }
936 if (isset($this->shipping_method_id)) {
937 $this->shipping_method_id = (int) $this->shipping_method_id;
938 }
939 if (isset($this->tracking_number)) {
940 $this->tracking_number = trim($this->tracking_number);
941 }
942 if (isset($this->statut)) {
943 $this->statut = (int) $this->statut;
944 }
945 if (isset($this->trueDepth)) {
946 $this->trueDepth = trim($this->trueDepth);
947 }
948 if (isset($this->trueWidth)) {
949 $this->trueWidth = trim($this->trueWidth);
950 }
951 if (isset($this->trueHeight)) {
952 $this->trueHeight = trim($this->trueHeight);
953 }
954 if (isset($this->size_units)) {
955 $this->size_units = trim((string) $this->size_units);
956 }
957 if (isset($this->weight_units)) {
958 $this->weight_units = trim((string) $this->weight_units);
959 }
960 if (isset($this->trueWeight)) {
961 $this->weight = trim((string) $this->trueWeight);
962 }
963 if (isset($this->note_private)) {
964 $this->note_private = trim($this->note_private);
965 }
966 if (isset($this->note_public)) {
967 $this->note_public = trim($this->note_public);
968 }
969 if (isset($this->model_pdf)) {
970 $this->model_pdf = trim($this->model_pdf);
971 }
972
973
974 // Check parameters
975 // Put here code to add control on parameters values
976
977 // Update request
978 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
979
980 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
981 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
982 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
983 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
984 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
985 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
986 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
987 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
988 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
989 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
990 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
991 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
992 $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
993 $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
994 $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
995 $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
996 $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
997 $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
998 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
999 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1000 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1001 $sql .= " entity = ".((int) $conf->entity);
1002 $sql .= " WHERE rowid=".((int) $this->id);
1003
1004 $this->db->begin();
1005
1006 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1007 $resql = $this->db->query($sql);
1008 if (!$resql) {
1009 $error++;
1010 $this->errors[] = "Error ".$this->db->lasterror();
1011 }
1012
1013 if (!$error) {
1014 if (!$notrigger) {
1015 // Call trigger
1016 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1017 if ($result < 0) {
1018 $error++;
1019 }
1020 // End call triggers
1021 }
1022 }
1023
1024 // Commit or rollback
1025 if ($error) {
1026 foreach ($this->errors as $errmsg) {
1027 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1028 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1029 }
1030 $this->db->rollback();
1031 return -1 * $error;
1032 } else {
1033 $this->db->commit();
1034 return 1;
1035 }
1036 }
1037
1044 public function delete(User $user)
1045 {
1046 global $conf, $langs, $user;
1047 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1048
1049 $error = 0;
1050 $this->error = '';
1051
1052
1053 $this->db->begin();
1054
1055 // Stock control
1056 if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > 0) {
1057 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1058
1059 $langs->load("agenda");
1060
1061 // Loop on each product line to add a stock movement
1062 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1063 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1064 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1065 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1066 $sql .= " AND cd.rowid = ed.fk_elementdet";
1067
1068 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1069 $resql = $this->db->query($sql);
1070 if ($resql) {
1071 $cpt = $this->db->num_rows($resql);
1072 for ($i = 0; $i < $cpt; $i++) {
1073 dol_syslog(get_class($this)."::delete movement index ".$i);
1074 $obj = $this->db->fetch_object($resql);
1075
1076 $mouvS = new MouvementStock($this->db);
1077 // we do not log origin because it will be deleted
1078 $mouvS->origin = null;
1079
1080 $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
1081 }
1082 } else {
1083 $error++;
1084 $this->errors[] = "Error ".$this->db->lasterror();
1085 }
1086 }
1087
1088 if (!$error) {
1089 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1090 $ef = $main."_extrafields";
1091
1092 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1093
1094 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1095 $sql .= " WHERE fk_reception = ".((int) $this->id);
1096
1097 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1098 // Delete linked object
1099 $res = $this->deleteObjectLinked();
1100 if ($res < 0) {
1101 $error++;
1102 }
1103
1104 if (!$error) {
1105 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1106 $sql .= " WHERE rowid = ".((int) $this->id);
1107
1108 if ($this->db->query($sql)) {
1109 // Call trigger
1110 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1111 if ($result < 0) {
1112 $error++;
1113 }
1114 // End call triggers
1115
1116 if (!empty($this->origin) && $this->origin_id > 0) {
1117 $this->fetch_origin();
1118 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
1119 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1120 $this->origin_object->loadReceptions();
1121 //var_dump($this->$origin->receptions);exit;
1122 if (count($this->origin_object->receptions) <= 0) {
1123 $this->origin_object->setStatut(3); // ordered
1124 }
1125 }
1126 }
1127
1128 if (!$error) {
1129 $this->db->commit();
1130
1131 // We delete PDFs
1132 $ref = dol_sanitizeFileName($this->ref);
1133 if (!empty($conf->reception->dir_output)) {
1134 $dir = $conf->reception->dir_output.'/'.$ref;
1135 $file = $dir.'/'.$ref.'.pdf';
1136 if (file_exists($file)) {
1137 if (!dol_delete_file($file)) {
1138 return 0;
1139 }
1140 }
1141 if (file_exists($dir)) {
1142 if (!dol_delete_dir_recursive($dir)) {
1143 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1144 return 0;
1145 }
1146 }
1147 }
1148
1149 return 1;
1150 } else {
1151 $this->db->rollback();
1152 return -1;
1153 }
1154 } else {
1155 $this->error = $this->db->lasterror()." - sql=$sql";
1156 $this->db->rollback();
1157 return -3;
1158 }
1159 } else {
1160 $this->error = $this->db->lasterror()." - sql=$sql";
1161 $this->db->rollback();
1162 return -2;
1163 }
1164 } else {
1165 $this->error = $this->db->lasterror()." - sql=$sql";
1166 $this->db->rollback();
1167 return -1;
1168 }
1169 } else {
1170 $this->db->rollback();
1171 return -1;
1172 }
1173 }
1174
1175 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1181 public function fetch_lines()
1182 {
1183 // phpcs:enable
1184 $this->lines = array();
1185
1186 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1187
1188 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1189 $sql .= " WHERE fk_reception = ".((int) $this->id);
1190
1191 $resql = $this->db->query($sql);
1192
1193 if (!empty($resql)) {
1194 while ($obj = $this->db->fetch_object($resql)) {
1195 $line = new CommandeFournisseurDispatch($this->db);
1196
1197 $line->fetch($obj->rowid);
1198
1199 // TODO Remove or keep this ?
1200 $line->fetch_product();
1201
1202 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1203 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1204 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1205 $sql_commfourndet .= ' ORDER BY rang';
1206
1207 $resql_commfourndet = $this->db->query($sql_commfourndet);
1208 if (!empty($resql_commfourndet)) {
1209 $obj = $this->db->fetch_object($resql_commfourndet);
1210 $line->qty_asked = $obj->qty;
1211 $line->description = $obj->description;
1212 $line->desc = $obj->description;
1213 $line->tva_tx = $obj->tva_tx;
1214 $line->vat_src_code = $obj->vat_src_code;
1215 $line->subprice = $obj->subprice;
1216 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1217 $line->remise_percent = $obj->remise_percent;
1218 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1219 $line->ref_supplier = $obj->ref;
1220 $line->total_ht = $obj->total_ht;
1221 $line->total_ttc = $obj->total_ttc;
1222 $line->total_tva = $obj->total_tva;
1223 } else {
1224 $line->qty_asked = 0;
1225 $line->description = '';
1226 $line->desc = '';
1227 $line->label = $obj->label;
1228 }
1229
1230 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1231 $tva = $pu_ht * $line->tva_tx / 100;
1232 $this->total_ht += $pu_ht;
1233 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1234
1235 $this->total_ttc += $pu_ht + $tva;
1236
1237 if (isModEnabled('productbatch') && !empty($line->batch)) {
1238 $detail_batch = new stdClass();
1239 $detail_batch->eatby = $line->eatby;
1240 $detail_batch->sellby = $line->sellby;
1241 $detail_batch->batch = $line->batch;
1242 $detail_batch->qty = $line->qty;
1243
1244 $line->detail_batch[] = $detail_batch;
1245 }
1246
1247 $this->lines[] = $line;
1248 }
1249
1250 return 1;
1251 } else {
1252 return -1;
1253 }
1254 }
1255
1266 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1267 {
1268 global $langs, $hookmanager;
1269
1270 $result = '';
1271 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1272 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1273 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1274
1275 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1276
1277 if ($short) {
1278 return $url;
1279 }
1280
1281 $linkclose = '';
1282 if (empty($notooltip)) {
1283 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1284 $label = $langs->trans("Reception");
1285 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1286 }
1287 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1288 $linkclose .= ' class="classfortooltip"';
1289 }
1290
1291 $linkstart = '<a href="'.$url.'"';
1292 $linkstart .= $linkclose.'>';
1293 $linkend = '</a>';
1294
1295 $result .= $linkstart;
1296 if ($withpicto) {
1297 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1298 }
1299 if ($withpicto != 2) {
1300 $result .= $this->ref;
1301 }
1302
1303 $result .= $linkend;
1304
1305 global $action;
1306 $hookmanager->initHooks(array($this->element . 'dao'));
1307 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1308 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1309 if ($reshook > 0) {
1310 $result = $hookmanager->resPrint;
1311 } else {
1312 $result .= $hookmanager->resPrint;
1313 }
1314 return $result;
1315 }
1316
1323 public function getLibStatut($mode = 0)
1324 {
1325 return $this->LibStatut($this->statut, $mode);
1326 }
1327
1328 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1336 public function LibStatut($status, $mode)
1337 {
1338 // phpcs:enable
1339 global $langs;
1340
1341 // List of long language codes for status
1342 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1343 $this->labelStatus[0] = 'StatusReceptionDraft';
1344 // product to receive if stock increase is on close or already received if stock increase is on validation
1345 $this->labelStatus[1] = 'StatusReceptionValidated';
1346 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1347 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1348 }
1349 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1350 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1351 }
1352 $this->labelStatus[2] = 'StatusReceptionProcessed';
1353
1354 // List of short language codes for status
1355 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1356 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1357 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1358 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1359
1360 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1361 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1362
1363 $statusType = 'status'.$status;
1364 if ($status == self::STATUS_VALIDATED) {
1365 $statusType = 'status4';
1366 }
1367 if ($status == self::STATUS_CLOSED) {
1368 $statusType = 'status6';
1369 }
1370
1371 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1372 }
1373
1381 public function getKanbanView($option = '', $arraydata = null)
1382 {
1383 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1384
1385 $return = '<div class="box-flex-item box-flex-grow-zero">';
1386 $return .= '<div class="info-box info-box-sm">';
1387 $return .= '<div class="info-box-icon bg-infobox-action">';
1388 $return .= img_picto('', 'order');
1389 $return .= '</div>';
1390 $return .= '<div class="info-box-content">';
1391 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1392 if ($selected >= 0) {
1393 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1394 }
1395 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1396 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1397 }
1398 /*if (property_exists($this, 'total_ht')) {
1399 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1400 }*/
1401 if (method_exists($this, 'getLibStatut')) {
1402 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1403 }
1404 $return .= '</div>';
1405 $return .= '</div>';
1406 $return .= '</div>';
1407
1408 return $return;
1409 }
1410
1418 public function initAsSpecimen()
1419 {
1420 global $langs;
1421
1422 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1423 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1424 $now = dol_now();
1425
1426 dol_syslog(get_class($this)."::initAsSpecimen");
1427
1428 $order = new CommandeFournisseur($this->db);
1429 $order->initAsSpecimen();
1430
1431 // Initialise parameters
1432 $this->id = 0;
1433 $this->ref = 'SPECIMEN';
1434 $this->specimen = 1;
1435 $this->statut = 1;
1436 $this->status = 1;
1437 $this->date = $now;
1438 $this->date_creation = $now;
1439 $this->date_valid = $now;
1440 $this->date_delivery = $now;
1441 $this->date_reception = $now + 24 * 3600;
1442
1443 $this->entrepot_id = 0;
1444 $this->socid = 1;
1445
1446 $this->origin_id = 1;
1447 $this->origin_type = 'supplier_order';
1448 $this->origin_object = $order;
1449
1450 $this->note_private = 'Private note';
1451 $this->note_public = 'Public note';
1452
1453 $this->tracking_number = 'TRACKID-ABC123';
1454
1455 $this->fk_incoterms = 1;
1456
1457 $nbp = 5;
1458 $xnbp = 0;
1459 while ($xnbp < $nbp) {
1460 $line = new CommandeFournisseurDispatch($this->db);
1461 $line->desc = $langs->trans("Description")." ".$xnbp;
1462 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1463 $line->label = $langs->trans("Description")." ".$xnbp;
1464 $line->qty = 10;
1465
1466 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1467
1468 $this->lines[] = $line;
1469 $xnbp++;
1470 }
1471
1472 return 1;
1473 }
1474
1482 public function setDeliveryDate($user, $delivery_date)
1483 {
1484 // phpcs:enable
1485 if ($user->hasRight('reception', 'creer')) {
1486 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1487 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1488 $sql .= " WHERE rowid = ".((int) $this->id);
1489
1490 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1491 $resql = $this->db->query($sql);
1492 if ($resql) {
1493 $this->date_delivery = $delivery_date;
1494 return 1;
1495 } else {
1496 $this->error = $this->db->error();
1497 return -1;
1498 }
1499 } else {
1500 return -2;
1501 }
1502 }
1503
1504 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1510 public function fetch_delivery_methods()
1511 {
1512 // phpcs:enable
1513 global $langs;
1514 $this->meths = array();
1515
1516 $sql = "SELECT em.rowid, em.code, em.libelle";
1517 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1518 $sql .= " WHERE em.active = 1";
1519 $sql .= " ORDER BY em.libelle ASC";
1520
1521 $resql = $this->db->query($sql);
1522 if ($resql) {
1523 while ($obj = $this->db->fetch_object($resql)) {
1524 $label = $langs->trans('ReceptionMethod'.$obj->code);
1525 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1526 }
1527 }
1528 }
1529
1530 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1537 public function list_delivery_methods($id = 0)
1538 {
1539 // phpcs:enable
1540 global $langs;
1541
1542 $this->listmeths = array();
1543 $i = 0;
1544
1545 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1546 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1547 if (!empty($id)) {
1548 $sql .= " WHERE em.rowid = ".((int) $id);
1549 }
1550
1551 $resql = $this->db->query($sql);
1552 if ($resql) {
1553 while ($obj = $this->db->fetch_object($resql)) {
1554 $this->listmeths[$i]['rowid'] = $obj->rowid;
1555 $this->listmeths[$i]['code'] = $obj->code;
1556 $label = $langs->trans('ReceptionMethod'.$obj->code);
1557 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1558 $this->listmeths[$i]['description'] = $obj->description;
1559 $this->listmeths[$i]['tracking'] = $obj->tracking;
1560 $this->listmeths[$i]['active'] = $obj->active;
1561 $i++;
1562 }
1563 }
1564 }
1565
1572 public function getUrlTrackingStatus($value = '')
1573 {
1574 if (!empty($this->shipping_method_id)) {
1575 $sql = "SELECT em.code, em.tracking";
1576 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1577 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1578
1579 $resql = $this->db->query($sql);
1580 if ($resql) {
1581 if ($obj = $this->db->fetch_object($resql)) {
1582 $tracking = $obj->tracking;
1583 }
1584 }
1585 }
1586
1587 if (!empty($tracking) && !empty($value)) {
1588 $url = str_replace('{TRACKID}', $value, $tracking);
1589 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1590 } else {
1591 $this->tracking_url = $value;
1592 }
1593 }
1594
1600 public function setClosed()
1601 {
1602 global $conf, $langs, $user;
1603
1604 $error = 0;
1605
1606 // Protection. This avoid to move stock later when we should not
1607 if ($this->statut == Reception::STATUS_CLOSED) {
1608 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1609 return 0;
1610 }
1611
1612 $this->db->begin();
1613
1614 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1615 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1616
1617 $resql = $this->db->query($sql);
1618 if ($resql) {
1619 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1620 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1621 $order = new CommandeFournisseur($this->db);
1622 $order->fetch($this->origin_id);
1623
1624 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1625
1626 $receptions_match_order = 1;
1627 foreach ($order->lines as $line) {
1628 $lineid = $line->id;
1629 $qty = $line->qty;
1630 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1631 $receptions_match_order = 0;
1632 $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';
1633 dol_syslog($text);
1634 break;
1635 }
1636 }
1637 if ($receptions_match_order) {
1638 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');
1639 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1640 }
1641 }
1642
1643 $this->statut = self::STATUS_CLOSED;
1644 $this->status = self::STATUS_CLOSED;
1645
1646 // If stock increment is done on closing
1647 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1648 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1649
1650 $langs->load("agenda");
1651
1652 // Loop on each product line to add a stock movement
1653 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1654 $sql = "SELECT cd.fk_product, cd.subprice,";
1655 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1656 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1657 $sql .= " ed.cost_price";
1658 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1659 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1660 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1661 $sql .= " AND cd.rowid = ed.fk_elementdet";
1662
1663 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1664 $resql = $this->db->query($sql);
1665
1666 if ($resql) {
1667 $cpt = $this->db->num_rows($resql);
1668 for ($i = 0; $i < $cpt; $i++) {
1669 $obj = $this->db->fetch_object($resql);
1670
1671 $qty = $obj->qty;
1672
1673 if ($qty <= 0) {
1674 continue;
1675 }
1676 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1677
1678 $mouvS = new MouvementStock($this->db);
1679 $mouvS->origin = &$this;
1680 $mouvS->setOrigin($this->element, $this->id);
1681
1682 if (empty($obj->batch)) {
1683 // line without batch detail
1684
1685 // 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
1686 $inventorycode = '';
1687 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1688 if ($result < 0) {
1689 $this->error = $mouvS->error;
1690 $this->errors = $mouvS->errors;
1691 $error++;
1692 break;
1693 }
1694 } else {
1695 // line with batch detail
1696
1697 // 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
1698 $inventorycode = '';
1699 $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);
1700
1701 if ($result < 0) {
1702 $this->error = $mouvS->error;
1703 $this->errors = $mouvS->errors;
1704 $error++;
1705 break;
1706 }
1707 }
1708 }
1709 } else {
1710 $this->error = $this->db->lasterror();
1711 $error++;
1712 }
1713 }
1714
1715 // Call trigger
1716 if (!$error) {
1717 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1718 if ($result < 0) {
1719 $error++;
1720 }
1721 }
1722 } else {
1723 dol_print_error($this->db);
1724 $error++;
1725 }
1726
1727 if (!$error) {
1728 $this->db->commit();
1729 return 1;
1730 } else {
1731 $this->statut = self::STATUS_VALIDATED;
1732 $this->status = self::STATUS_VALIDATED;
1733 $this->db->rollback();
1734 return -1;
1735 }
1736 }
1737
1743 public function setBilled()
1744 {
1745 global $user;
1746 $error = 0;
1747
1748 $this->db->begin();
1749
1750 if ($this->statut == Reception::STATUS_VALIDATED) {
1751 // do not close if already closed
1752 $this->setClosed();
1753 }
1754
1755 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1756 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1757
1758 $resql = $this->db->query($sql);
1759 if ($resql) {
1760 $this->billed = 1;
1761
1762 // Call trigger
1763 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1764 if ($result < 0) {
1765 $this->billed = 0;
1766 $error++;
1767 }
1768 } else {
1769 $error++;
1770 $this->errors[] = $this->db->lasterror;
1771 }
1772
1773 if (empty($error)) {
1774 $this->db->commit();
1775 return 1;
1776 } else {
1777 $this->db->rollback();
1778 return -1;
1779 }
1780 }
1781
1787 public function reOpen()
1788 {
1789 global $conf, $langs, $user;
1790
1791 $error = 0;
1792
1793 $this->db->begin();
1794
1795 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1796 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1797
1798 $resql = $this->db->query($sql);
1799 if ($resql) {
1800 $this->statut = self::STATUS_VALIDATED;
1801 $this->status = self::STATUS_VALIDATED;
1802 $this->billed = 0;
1803
1804 // If stock increment is done on closing
1805 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1806 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1807 $numref = $this->ref;
1808 $langs->load("agenda");
1809
1810 // Loop on each product line to add a stock movement
1811 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1812 $sql = "SELECT ed.fk_product, cd.subprice,";
1813 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1814 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1815 $sql .= " ed.cost_price";
1816 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1817 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1818 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1819 $sql .= " AND cd.rowid = ed.fk_elementdet";
1820
1821 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1822 $resql = $this->db->query($sql);
1823 if ($resql) {
1824 $cpt = $this->db->num_rows($resql);
1825 for ($i = 0; $i < $cpt; $i++) {
1826 $obj = $this->db->fetch_object($resql);
1827
1828 $qty = $obj->qty;
1829
1830 if ($qty <= 0) {
1831 continue;
1832 }
1833
1834 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1835
1836 //var_dump($this->lines[$i]);
1837 $mouvS = new MouvementStock($this->db);
1838 $mouvS->origin = &$this;
1839 $mouvS->setOrigin($this->element, $this->id);
1840
1841 if (empty($obj->batch)) {
1842 // line without batch detail
1843
1844 // 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
1845 $inventorycode = '';
1846 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1847
1848 if ($result < 0) {
1849 $this->error = $mouvS->error;
1850 $this->errors = $mouvS->errors;
1851 $error++;
1852 break;
1853 }
1854 } else {
1855 // line with batch detail
1856
1857 // 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
1858 $inventorycode = '';
1859 $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);
1860 if ($result < 0) {
1861 $this->error = $mouvS->error;
1862 $this->errors = $mouvS->errors;
1863 $error++;
1864 break;
1865 }
1866 }
1867 }
1868 } else {
1869 $this->error = $this->db->lasterror();
1870 $error++;
1871 }
1872 }
1873
1874 if (!$error) {
1875 // Call trigger
1876 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1877 if ($result < 0) {
1878 $error++;
1879 }
1880 }
1881
1882 if (!$error && $this->origin == 'order_supplier') {
1883 $commande = new CommandeFournisseur($this->db);
1884 $commande->fetch($this->origin_id);
1885 $result = $commande->setStatus($user, 4);
1886 if ($result < 0) {
1887 $error++;
1888 $this->error = $commande->error;
1889 $this->errors = $commande->errors;
1890 }
1891 }
1892 } else {
1893 $error++;
1894 $this->errors[] = $this->db->lasterror();
1895 }
1896
1897 if (!$error) {
1898 $this->db->commit();
1899 return 1;
1900 } else {
1901 $this->db->rollback();
1902 return -1;
1903 }
1904 }
1905
1912 public function setDraft($user)
1913 {
1914 // phpcs:enable
1915 global $conf, $langs;
1916
1917 $error = 0;
1918
1919 // Protection
1920 if ($this->statut <= self::STATUS_DRAFT) {
1921 return 0;
1922 }
1923
1924 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1925 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1926 $this->error = 'Permission denied';
1927 return -1;
1928 }
1929
1930 $this->db->begin();
1931
1932 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1933 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1934 $sql .= " WHERE rowid = ".((int) $this->id);
1935
1936 dol_syslog(__METHOD__, LOG_DEBUG);
1937 if ($this->db->query($sql)) {
1938 // If stock increment is done on closing
1939 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1940 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1941
1942 $langs->load("agenda");
1943
1944 // Loop on each product line to add a stock movement
1945 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1946 $sql = "SELECT cd.fk_product, cd.subprice,";
1947 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1948 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1949 $sql .= " ed.cost_price";
1950 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1951 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1952 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1953 $sql .= " AND cd.rowid = ed.fk_elementdet";
1954
1955 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1956 $resql = $this->db->query($sql);
1957 if ($resql) {
1958 $cpt = $this->db->num_rows($resql);
1959 for ($i = 0; $i < $cpt; $i++) {
1960 $obj = $this->db->fetch_object($resql);
1961
1962 $qty = $obj->qty;
1963
1964
1965 if ($qty <= 0) {
1966 continue;
1967 }
1968 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1969
1970 //var_dump($this->lines[$i]);
1971 $mouvS = new MouvementStock($this->db);
1972 $mouvS->origin = &$this;
1973 $mouvS->setOrigin($this->element, $this->id);
1974
1975 if (empty($obj->batch)) {
1976 // line without batch detail
1977
1978 // 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
1979 $inventorycode = '';
1980 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1981 if ($result < 0) {
1982 $this->error = $mouvS->error;
1983 $this->errors = $mouvS->errors;
1984 $error++;
1985 break;
1986 }
1987 } else {
1988 // line with batch detail
1989
1990 // 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
1991 $inventorycode = '';
1992 $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);
1993 if ($result < 0) {
1994 $this->error = $mouvS->error;
1995 $this->errors = $mouvS->errors;
1996 $error++;
1997 break;
1998 }
1999 }
2000 }
2001 } else {
2002 $this->error = $this->db->lasterror();
2003 $error++;
2004 }
2005 }
2006
2007 if (!$error) {
2008 // Call trigger
2009 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2010 if ($result < 0) {
2011 $error++;
2012 }
2013 }
2014 if ($this->origin == 'order_supplier') {
2015 if (!empty($this->origin) && $this->origin_id > 0) {
2016 $this->fetch_origin();
2017 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2018 // Check if there is no more reception validated.
2019 $this->origin_object->fetchObjectLinked();
2020 $setStatut = 1;
2021 if (!empty($this->origin_object->linkedObjects['reception'])) {
2022 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2023 if ($rcption->statut > 0) {
2024 $setStatut = 0;
2025 break;
2026 }
2027 }
2028 //var_dump($this->$origin->receptions);exit;
2029 if ($setStatut) {
2030 $this->origin_object->setStatut(3); // ordered
2031 }
2032 }
2033 }
2034 }
2035 }
2036
2037 if (!$error) {
2038 $this->statut = self::STATUS_DRAFT;
2039 $this->status = self::STATUS_DRAFT;
2040 $this->db->commit();
2041 return 1;
2042 } else {
2043 $this->db->rollback();
2044 return -1;
2045 }
2046 } else {
2047 $this->error = $this->db->error();
2048 $this->db->rollback();
2049 return -1;
2050 }
2051 }
2052
2063 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2064 {
2065 global $conf, $langs;
2066
2067 $langs->load("receptions");
2068
2069 if (!dol_strlen($modele)) {
2070 $modele = 'squille';
2071
2072 if ($this->model_pdf) {
2073 $modele = $this->model_pdf;
2074 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2075 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2076 }
2077 }
2078
2079 $modelpath = "core/modules/reception/doc/";
2080
2081 $this->fetch_origin();
2082
2083 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2084 }
2085
2094 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2095 {
2096 $tables = array('reception');
2097
2098 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2099 }
2100
2109 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2110 {
2111 $tables = array(
2112 'receptiondet_batch'
2113 );
2114
2115 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2116 }
2117}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:626
$object ref
Definition info.php:79
Class to manage table ReceptionLineBatch.
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_RECEIVED_COMPLETELY
Received completely.
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=0, $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.
static checkSellOrEatByMandatoryFromProductAndDates($product, $sellBy, $eatBy, $onlyFieldName='', $alreadyCheckConf=false)
Check sell or eat by date is mandatory from product and sell-by and eat-by dates.
Class to manage receptions.
setBilled()
Classify the reception as invoiced (used for example by trigger when WORKFLOW_RECEPTION_CLASSIFY_BILL...
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 records 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.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
setDeliveryDate($user, $delivery_date)
Set the planned delivery date.
list_delivery_methods($id=0)
Fetch all deliveries method and return an array.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
__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.
addline($entrepot_id, $id, $qty, $array_options=[], $comment='', $eatby=null, $sellby=null, $batch='', $cost_price=0)
Add an reception line.
setDraft($user)
Set draft status.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
trait CommonIncoterm
Superclass for incoterm classes.
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)
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.
dol_dir_list($utf8_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:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (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 a Dolibarr global constant int value.
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:143
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1929