dolibarr 20.0.4
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-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 if ($result < 0) {
1087 $error++;
1088 $this->error = $mouvS->error;
1089 $this->errors = $mouvS->errors;
1090 }
1091 }
1092 } else {
1093 $error++;
1094 $this->errors[] = "Error ".$this->db->lasterror();
1095 }
1096 }
1097
1098 if (!$error) {
1099 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1100 $ef = $main."_extrafields";
1101
1102 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1103
1104 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1105 $sql .= " WHERE fk_reception = ".((int) $this->id);
1106
1107 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1108 // Delete linked object
1109 $res = $this->deleteObjectLinked();
1110 if ($res < 0) {
1111 $error++;
1112 }
1113
1114 if (!$error) {
1115 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1116 $sql .= " WHERE rowid = ".((int) $this->id);
1117
1118 if ($this->db->query($sql)) {
1119 // Call trigger
1120 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1121 if ($result < 0) {
1122 $error++;
1123 }
1124 // End call triggers
1125
1126 if (!empty($this->origin) && $this->origin_id > 0) {
1127 $this->fetch_origin();
1128 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
1129 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1130 $this->origin_object->loadReceptions();
1131 //var_dump($this->$origin->receptions);exit;
1132 if (count($this->origin_object->receptions) <= 0) {
1133 $this->origin_object->setStatut(3); // ordered
1134 }
1135 }
1136 }
1137
1138 if (!$error) {
1139 $this->db->commit();
1140
1141 // We delete PDFs
1142 $ref = dol_sanitizeFileName($this->ref);
1143 if (!empty($conf->reception->dir_output)) {
1144 $dir = $conf->reception->dir_output.'/'.$ref;
1145 $file = $dir.'/'.$ref.'.pdf';
1146 if (file_exists($file)) {
1147 if (!dol_delete_file($file)) {
1148 return 0;
1149 }
1150 }
1151 if (file_exists($dir)) {
1152 if (!dol_delete_dir_recursive($dir)) {
1153 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1154 return 0;
1155 }
1156 }
1157 }
1158
1159 return 1;
1160 } else {
1161 $this->db->rollback();
1162 return -1;
1163 }
1164 } else {
1165 $this->error = $this->db->lasterror()." - sql=$sql";
1166 $this->db->rollback();
1167 return -3;
1168 }
1169 } else {
1170 $this->error = $this->db->lasterror()." - sql=$sql";
1171 $this->db->rollback();
1172 return -2;
1173 }
1174 } else {
1175 $this->error = $this->db->lasterror()." - sql=$sql";
1176 $this->db->rollback();
1177 return -1;
1178 }
1179 } else {
1180 $this->db->rollback();
1181 return -1;
1182 }
1183 }
1184
1185 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1191 public function fetch_lines()
1192 {
1193 // phpcs:enable
1194 $this->lines = array();
1195
1196 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1197
1198 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1199 $sql .= " WHERE fk_reception = ".((int) $this->id);
1200
1201 $resql = $this->db->query($sql);
1202
1203 if (!empty($resql)) {
1204 while ($obj = $this->db->fetch_object($resql)) {
1205 $line = new CommandeFournisseurDispatch($this->db);
1206
1207 $line->fetch($obj->rowid);
1208
1209 // TODO Remove or keep this ?
1210 $line->fetch_product();
1211
1212 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1213 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1214 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1215 $sql_commfourndet .= ' ORDER BY rang';
1216
1217 $resql_commfourndet = $this->db->query($sql_commfourndet);
1218 if (!empty($resql_commfourndet)) {
1219 $obj = $this->db->fetch_object($resql_commfourndet);
1220 $line->qty_asked = $obj->qty;
1221 $line->description = $obj->description;
1222 $line->desc = $obj->description;
1223 $line->tva_tx = $obj->tva_tx;
1224 $line->vat_src_code = $obj->vat_src_code;
1225 $line->subprice = $obj->subprice;
1226 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1227 $line->remise_percent = $obj->remise_percent;
1228 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1229 $line->ref_supplier = $obj->ref;
1230 $line->total_ht = $obj->total_ht;
1231 $line->total_ttc = $obj->total_ttc;
1232 $line->total_tva = $obj->total_tva;
1233 } else {
1234 $line->qty_asked = 0;
1235 $line->description = '';
1236 $line->desc = '';
1237 $line->label = $obj->label;
1238 }
1239
1240 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1241 $tva = $pu_ht * $line->tva_tx / 100;
1242 $this->total_ht += $pu_ht;
1243 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1244
1245 $this->total_ttc += $pu_ht + $tva;
1246
1247 if (isModEnabled('productbatch') && !empty($line->batch)) {
1248 $detail_batch = new stdClass();
1249 $detail_batch->eatby = $line->eatby;
1250 $detail_batch->sellby = $line->sellby;
1251 $detail_batch->batch = $line->batch;
1252 $detail_batch->qty = $line->qty;
1253
1254 $line->detail_batch[] = $detail_batch;
1255 }
1256
1257 $this->lines[] = $line;
1258 }
1259
1260 return 1;
1261 } else {
1262 return -1;
1263 }
1264 }
1265
1276 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1277 {
1278 global $langs, $hookmanager;
1279
1280 $result = '';
1281 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1282 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1283 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1284
1285 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1286
1287 if ($short) {
1288 return $url;
1289 }
1290
1291 $linkclose = '';
1292 if (empty($notooltip)) {
1293 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1294 $label = $langs->trans("Reception");
1295 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1296 }
1297 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1298 $linkclose .= ' class="classfortooltip"';
1299 }
1300
1301 $linkstart = '<a href="'.$url.'"';
1302 $linkstart .= $linkclose.'>';
1303 $linkend = '</a>';
1304
1305 $result .= $linkstart;
1306 if ($withpicto) {
1307 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1308 }
1309 if ($withpicto != 2) {
1310 $result .= $this->ref;
1311 }
1312
1313 $result .= $linkend;
1314
1315 global $action;
1316 $hookmanager->initHooks(array($this->element . 'dao'));
1317 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1318 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1319 if ($reshook > 0) {
1320 $result = $hookmanager->resPrint;
1321 } else {
1322 $result .= $hookmanager->resPrint;
1323 }
1324 return $result;
1325 }
1326
1333 public function getLibStatut($mode = 0)
1334 {
1335 return $this->LibStatut($this->statut, $mode);
1336 }
1337
1338 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1346 public function LibStatut($status, $mode)
1347 {
1348 // phpcs:enable
1349 global $langs;
1350
1351 // List of long language codes for status
1352 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1353 $this->labelStatus[0] = 'StatusReceptionDraft';
1354 // product to receive if stock increase is on close or already received if stock increase is on validation
1355 $this->labelStatus[1] = 'StatusReceptionValidated';
1356 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1357 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1358 }
1359 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1360 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1361 }
1362 $this->labelStatus[2] = 'StatusReceptionProcessed';
1363
1364 // List of short language codes for status
1365 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1366 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1367 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1368 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1369
1370 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1371 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1372
1373 $statusType = 'status'.$status;
1374 if ($status == self::STATUS_VALIDATED) {
1375 $statusType = 'status4';
1376 }
1377 if ($status == self::STATUS_CLOSED) {
1378 $statusType = 'status6';
1379 }
1380
1381 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1382 }
1383
1391 public function getKanbanView($option = '', $arraydata = null)
1392 {
1393 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1394
1395 $return = '<div class="box-flex-item box-flex-grow-zero">';
1396 $return .= '<div class="info-box info-box-sm">';
1397 $return .= '<div class="info-box-icon bg-infobox-action">';
1398 $return .= img_picto('', 'order');
1399 $return .= '</div>';
1400 $return .= '<div class="info-box-content">';
1401 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1402 if ($selected >= 0) {
1403 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1404 }
1405 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1406 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1407 }
1408 /*if (property_exists($this, 'total_ht')) {
1409 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1410 }*/
1411 if (method_exists($this, 'getLibStatut')) {
1412 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1413 }
1414 $return .= '</div>';
1415 $return .= '</div>';
1416 $return .= '</div>';
1417
1418 return $return;
1419 }
1420
1428 public function initAsSpecimen()
1429 {
1430 global $langs;
1431
1432 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1433 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1434 $now = dol_now();
1435
1436 dol_syslog(get_class($this)."::initAsSpecimen");
1437
1438 $order = new CommandeFournisseur($this->db);
1439 $order->initAsSpecimen();
1440
1441 // Initialise parameters
1442 $this->id = 0;
1443 $this->ref = 'SPECIMEN';
1444 $this->specimen = 1;
1445 $this->statut = 1;
1446 $this->status = 1;
1447 $this->date = $now;
1448 $this->date_creation = $now;
1449 $this->date_valid = $now;
1450 $this->date_delivery = $now;
1451 $this->date_reception = $now + 24 * 3600;
1452
1453 $this->entrepot_id = 0;
1454 $this->socid = 1;
1455
1456 $this->origin_id = 1;
1457 $this->origin_type = 'supplier_order';
1458 $this->origin_object = $order;
1459
1460 $this->note_private = 'Private note';
1461 $this->note_public = 'Public note';
1462
1463 $this->tracking_number = 'TRACKID-ABC123';
1464
1465 $this->fk_incoterms = 1;
1466
1467 $nbp = 5;
1468 $xnbp = 0;
1469 while ($xnbp < $nbp) {
1470 $line = new CommandeFournisseurDispatch($this->db);
1471 $line->desc = $langs->trans("Description")." ".$xnbp;
1472 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1473 $line->label = $langs->trans("Description")." ".$xnbp;
1474 $line->qty = 10;
1475
1476 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1477
1478 $this->lines[] = $line;
1479 $xnbp++;
1480 }
1481
1482 return 1;
1483 }
1484
1492 public function setDeliveryDate($user, $delivery_date)
1493 {
1494 // phpcs:enable
1495 if ($user->hasRight('reception', 'creer')) {
1496 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1497 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1498 $sql .= " WHERE rowid = ".((int) $this->id);
1499
1500 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1501 $resql = $this->db->query($sql);
1502 if ($resql) {
1503 $this->date_delivery = $delivery_date;
1504 return 1;
1505 } else {
1506 $this->error = $this->db->error();
1507 return -1;
1508 }
1509 } else {
1510 return -2;
1511 }
1512 }
1513
1514 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1520 public function fetch_delivery_methods()
1521 {
1522 // phpcs:enable
1523 global $langs;
1524 $this->meths = array();
1525
1526 $sql = "SELECT em.rowid, em.code, em.libelle";
1527 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1528 $sql .= " WHERE em.active = 1";
1529 $sql .= " ORDER BY em.libelle ASC";
1530
1531 $resql = $this->db->query($sql);
1532 if ($resql) {
1533 while ($obj = $this->db->fetch_object($resql)) {
1534 $label = $langs->trans('ReceptionMethod'.$obj->code);
1535 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1536 }
1537 }
1538 }
1539
1540 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1547 public function list_delivery_methods($id = 0)
1548 {
1549 // phpcs:enable
1550 global $langs;
1551
1552 $this->listmeths = array();
1553 $i = 0;
1554
1555 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1556 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1557 if (!empty($id)) {
1558 $sql .= " WHERE em.rowid = ".((int) $id);
1559 }
1560
1561 $resql = $this->db->query($sql);
1562 if ($resql) {
1563 while ($obj = $this->db->fetch_object($resql)) {
1564 $this->listmeths[$i]['rowid'] = $obj->rowid;
1565 $this->listmeths[$i]['code'] = $obj->code;
1566 $label = $langs->trans('ReceptionMethod'.$obj->code);
1567 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1568 $this->listmeths[$i]['description'] = $obj->description;
1569 $this->listmeths[$i]['tracking'] = $obj->tracking;
1570 $this->listmeths[$i]['active'] = $obj->active;
1571 $i++;
1572 }
1573 }
1574 }
1575
1582 public function getUrlTrackingStatus($value = '')
1583 {
1584 if (!empty($this->shipping_method_id)) {
1585 $sql = "SELECT em.code, em.tracking";
1586 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1587 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1588
1589 $resql = $this->db->query($sql);
1590 if ($resql) {
1591 if ($obj = $this->db->fetch_object($resql)) {
1592 $tracking = $obj->tracking;
1593 }
1594 }
1595 }
1596
1597 if (!empty($tracking) && !empty($value)) {
1598 $url = str_replace('{TRACKID}', $value, $tracking);
1599 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1600 } else {
1601 $this->tracking_url = $value;
1602 }
1603 }
1604
1610 public function setClosed()
1611 {
1612 global $conf, $langs, $user;
1613
1614 $error = 0;
1615
1616 // Protection. This avoid to move stock later when we should not
1617 if ($this->statut == Reception::STATUS_CLOSED) {
1618 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1619 return 0;
1620 }
1621
1622 $this->db->begin();
1623
1624 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1625 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1626
1627 $resql = $this->db->query($sql);
1628 if ($resql) {
1629 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1630 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1631 $order = new CommandeFournisseur($this->db);
1632 $order->fetch($this->origin_id);
1633
1634 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1635
1636 $receptions_match_order = 1;
1637 foreach ($order->lines as $line) {
1638 $lineid = $line->id;
1639 $qty = $line->qty;
1640 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1641 $receptions_match_order = 0;
1642 $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';
1643 dol_syslog($text);
1644 break;
1645 }
1646 }
1647 if ($receptions_match_order) {
1648 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');
1649 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1650 }
1651 }
1652
1653 $this->statut = self::STATUS_CLOSED;
1654 $this->status = self::STATUS_CLOSED;
1655
1656 // If stock increment is done on closing
1657 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1658 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1659
1660 $langs->load("agenda");
1661
1662 // Loop on each product line to add a stock movement
1663 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1664 $sql = "SELECT cd.fk_product, cd.subprice,";
1665 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1666 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1667 $sql .= " ed.cost_price";
1668 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1669 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1670 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1671 $sql .= " AND cd.rowid = ed.fk_elementdet";
1672
1673 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1674 $resql = $this->db->query($sql);
1675
1676 if ($resql) {
1677 $cpt = $this->db->num_rows($resql);
1678 for ($i = 0; $i < $cpt; $i++) {
1679 $obj = $this->db->fetch_object($resql);
1680
1681 $qty = $obj->qty;
1682
1683 if ($qty <= 0) {
1684 continue;
1685 }
1686 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1687
1688 $mouvS = new MouvementStock($this->db);
1689 $mouvS->origin = &$this;
1690 $mouvS->setOrigin($this->element, $this->id);
1691
1692 if (empty($obj->batch)) {
1693 // line without batch detail
1694
1695 // 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
1696 $inventorycode = '';
1697 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1698 if ($result < 0) {
1699 $this->error = $mouvS->error;
1700 $this->errors = $mouvS->errors;
1701 $error++;
1702 break;
1703 }
1704 } else {
1705 // line with batch detail
1706
1707 // 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
1708 $inventorycode = '';
1709 $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);
1710
1711 if ($result < 0) {
1712 $this->error = $mouvS->error;
1713 $this->errors = $mouvS->errors;
1714 $error++;
1715 break;
1716 }
1717 }
1718 }
1719 } else {
1720 $this->error = $this->db->lasterror();
1721 $error++;
1722 }
1723 }
1724
1725 // Call trigger
1726 if (!$error) {
1727 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1728 if ($result < 0) {
1729 $error++;
1730 }
1731 }
1732 } else {
1733 dol_print_error($this->db);
1734 $error++;
1735 }
1736
1737 if (!$error) {
1738 $this->db->commit();
1739 return 1;
1740 } else {
1741 $this->statut = self::STATUS_VALIDATED;
1742 $this->status = self::STATUS_VALIDATED;
1743 $this->db->rollback();
1744 return -1;
1745 }
1746 }
1747
1753 public function setBilled()
1754 {
1755 global $user;
1756 $error = 0;
1757
1758 $this->db->begin();
1759
1760 if ($this->statut == Reception::STATUS_VALIDATED) {
1761 // do not close if already closed
1762 $this->setClosed();
1763 }
1764
1765 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1766 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1767
1768 $resql = $this->db->query($sql);
1769 if ($resql) {
1770 $this->billed = 1;
1771
1772 // Call trigger
1773 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1774 if ($result < 0) {
1775 $this->billed = 0;
1776 $error++;
1777 }
1778 } else {
1779 $error++;
1780 $this->errors[] = $this->db->lasterror;
1781 }
1782
1783 if (empty($error)) {
1784 $this->db->commit();
1785 return 1;
1786 } else {
1787 $this->db->rollback();
1788 return -1;
1789 }
1790 }
1791
1797 public function reOpen()
1798 {
1799 global $conf, $langs, $user;
1800
1801 $error = 0;
1802
1803 $this->db->begin();
1804
1805 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1806 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1807
1808 $resql = $this->db->query($sql);
1809 if ($resql) {
1810 $this->statut = self::STATUS_VALIDATED;
1811 $this->status = self::STATUS_VALIDATED;
1812 $this->billed = 0;
1813
1814 // If stock increment is done on closing
1815 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1816 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1817 $numref = $this->ref;
1818 $langs->load("agenda");
1819
1820 // Loop on each product line to add a stock movement
1821 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1822 $sql = "SELECT ed.fk_product, cd.subprice,";
1823 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1824 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1825 $sql .= " ed.cost_price";
1826 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1827 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1828 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1829 $sql .= " AND cd.rowid = ed.fk_elementdet";
1830
1831 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1832 $resql = $this->db->query($sql);
1833 if ($resql) {
1834 $cpt = $this->db->num_rows($resql);
1835 for ($i = 0; $i < $cpt; $i++) {
1836 $obj = $this->db->fetch_object($resql);
1837
1838 $qty = $obj->qty;
1839
1840 if ($qty <= 0) {
1841 continue;
1842 }
1843
1844 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1845
1846 //var_dump($this->lines[$i]);
1847 $mouvS = new MouvementStock($this->db);
1848 $mouvS->origin = &$this;
1849 $mouvS->setOrigin($this->element, $this->id);
1850
1851 if (empty($obj->batch)) {
1852 // line without batch detail
1853
1854 // 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
1855 $inventorycode = '';
1856 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1857
1858 if ($result < 0) {
1859 $this->error = $mouvS->error;
1860 $this->errors = $mouvS->errors;
1861 $error++;
1862 break;
1863 }
1864 } else {
1865 // line with batch detail
1866
1867 // 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
1868 $inventorycode = '';
1869 $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);
1870 if ($result < 0) {
1871 $this->error = $mouvS->error;
1872 $this->errors = $mouvS->errors;
1873 $error++;
1874 break;
1875 }
1876 }
1877 }
1878 } else {
1879 $this->error = $this->db->lasterror();
1880 $error++;
1881 }
1882 }
1883
1884 if (!$error) {
1885 // Call trigger
1886 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1887 if ($result < 0) {
1888 $error++;
1889 }
1890 }
1891
1892 if (!$error && $this->origin == 'order_supplier') {
1893 $commande = new CommandeFournisseur($this->db);
1894 $commande->fetch($this->origin_id);
1895 $result = $commande->setStatus($user, 4);
1896 if ($result < 0) {
1897 $error++;
1898 $this->error = $commande->error;
1899 $this->errors = $commande->errors;
1900 }
1901 }
1902 } else {
1903 $error++;
1904 $this->errors[] = $this->db->lasterror();
1905 }
1906
1907 if (!$error) {
1908 $this->db->commit();
1909 return 1;
1910 } else {
1911 $this->db->rollback();
1912 return -1;
1913 }
1914 }
1915
1922 public function setDraft($user)
1923 {
1924 // phpcs:enable
1925 global $conf, $langs;
1926
1927 $error = 0;
1928
1929 // Protection
1930 if ($this->statut <= self::STATUS_DRAFT) {
1931 return 0;
1932 }
1933
1934 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1935 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1936 $this->error = 'Permission denied';
1937 return -1;
1938 }
1939
1940 $this->db->begin();
1941
1942 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1943 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1944 $sql .= " WHERE rowid = ".((int) $this->id);
1945
1946 dol_syslog(__METHOD__, LOG_DEBUG);
1947 if ($this->db->query($sql)) {
1948 // If stock increment is done on closing
1949 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1950 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1951
1952 $langs->load("agenda");
1953
1954 // Loop on each product line to add a stock movement
1955 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1956 $sql = "SELECT cd.fk_product, cd.subprice,";
1957 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1958 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1959 $sql .= " ed.cost_price";
1960 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1961 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1962 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1963 $sql .= " AND cd.rowid = ed.fk_elementdet";
1964
1965 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1966 $resql = $this->db->query($sql);
1967 if ($resql) {
1968 $cpt = $this->db->num_rows($resql);
1969 for ($i = 0; $i < $cpt; $i++) {
1970 $obj = $this->db->fetch_object($resql);
1971
1972 $qty = $obj->qty;
1973
1974
1975 if ($qty <= 0) {
1976 continue;
1977 }
1978 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1979
1980 //var_dump($this->lines[$i]);
1981 $mouvS = new MouvementStock($this->db);
1982 $mouvS->origin = &$this;
1983 $mouvS->setOrigin($this->element, $this->id);
1984
1985 if (empty($obj->batch)) {
1986 // line without batch detail
1987
1988 // 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
1989 $inventorycode = '';
1990 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1991 if ($result < 0) {
1992 $this->error = $mouvS->error;
1993 $this->errors = $mouvS->errors;
1994 $error++;
1995 break;
1996 }
1997 } else {
1998 // line with batch detail
1999
2000 // 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
2001 $inventorycode = '';
2002 $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);
2003 if ($result < 0) {
2004 $this->error = $mouvS->error;
2005 $this->errors = $mouvS->errors;
2006 $error++;
2007 break;
2008 }
2009 }
2010 }
2011 } else {
2012 $this->error = $this->db->lasterror();
2013 $error++;
2014 }
2015 }
2016
2017 if (!$error) {
2018 // Call trigger
2019 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2020 if ($result < 0) {
2021 $error++;
2022 }
2023 }
2024 if ($this->origin == 'order_supplier') {
2025 if (!empty($this->origin) && $this->origin_id > 0) {
2026 $this->fetch_origin();
2027 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2028 // Check if there is no more reception validated.
2029 $this->origin_object->fetchObjectLinked();
2030 $setStatut = 1;
2031 if (!empty($this->origin_object->linkedObjects['reception'])) {
2032 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2033 if ($rcption->statut > 0) {
2034 $setStatut = 0;
2035 break;
2036 }
2037 }
2038 //var_dump($this->$origin->receptions);exit;
2039 if ($setStatut) {
2040 $this->origin_object->setStatut(3); // ordered
2041 }
2042 }
2043 }
2044 }
2045 }
2046
2047 if (!$error) {
2048 $this->statut = self::STATUS_DRAFT;
2049 $this->status = self::STATUS_DRAFT;
2050 $this->db->commit();
2051 return 1;
2052 } else {
2053 $this->db->rollback();
2054 return -1;
2055 }
2056 } else {
2057 $this->error = $this->db->error();
2058 $this->db->rollback();
2059 return -1;
2060 }
2061 }
2062
2073 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2074 {
2075 global $conf, $langs;
2076
2077 $langs->load("receptions");
2078
2079 if (!dol_strlen($modele)) {
2080 $modele = 'squille';
2081
2082 if ($this->model_pdf) {
2083 $modele = $this->model_pdf;
2084 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2085 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2086 }
2087 }
2088
2089 $modelpath = "core/modules/reception/doc/";
2090
2091 $this->fetch_origin();
2092
2093 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2094 }
2095
2104 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2105 {
2106 $tables = array('reception');
2107
2108 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2109 }
2110
2119 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2120 {
2121 $tables = array(
2122 'receptiondet_batch'
2123 );
2124
2125 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2126 }
2127}
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:2010