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