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 $this->commandeFournisseur = null; // deprecated
733 if ($res < 0) {
734 return $res;
735 }
736 } elseif ($this->origin_object instanceof CommandeFournisseur && empty($this->origin_object->lines)) {
737 $res = $this->origin_object->fetch_lines();
738 $this->commandeFournisseur = $this->origin_object; // deprecated
739 if ($res < 0) {
740 return $res;
741 }
742 }
743 }
744
745 $qty_received = array();
746 $qty_wished = array();
747
748 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
749 $filter = array('t.fk_element' => $this->origin_id);
750 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
751 $filter['t.status'] = 1; // Restrict to lines with status validated
752 }
753
754 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
755 if ($ret < 0) {
756 $this->error = $supplierorderdispatch->error;
757 $this->errors = $supplierorderdispatch->errors;
758 return $ret;
759 } else {
760 // build array with quantity received by product in all supplier orders (origin)
761 foreach ($supplierorderdispatch->lines as $dispatch_line) {
762 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
763 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
764 } else {
765 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
766 }
767 }
768
769 // qty wished in origin (purchase order, ...)
770 foreach ($this->origin_object->lines as $origin_line) {
771 // exclude lines not qualified for reception
772 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
773 continue;
774 }
775
776 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
777 }
778
779 // compare array
780 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
781 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
782 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
783
784 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
786 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
787 // set totally received if more products received than ordered
788 $close = 0;
789
790 if (count($diff_array) > 0) {
791 // there are some difference between the two arrays
792 // scan the array of results
793 foreach ($diff_array as $key => $value) {
794 // if the quantity delivered is greater or equal to ordered quantity
795 if ($qty_received[$key] >= $qty_wished[$key]) {
796 $close++;
797 }
798 }
799 }
800
801 if ($close == count($diff_array)) {
802 // all the products are received equal or more than the ordered quantity
804 }
805 }
806 }
807 }
808
809 return $status;
810 }
811
828 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
829 {
830 global $conf, $langs, $user;
831
832 $num = count($this->lines);
833 $line = new CommandeFournisseurDispatch($this->db);
834
835 $line->fk_entrepot = $entrepot_id;
836 $line->fk_commandefourndet = $id;
837 $line->qty = $qty;
838
839 $supplierorderline = new CommandeFournisseurLigne($this->db);
840 $result = $supplierorderline->fetch($id);
841 if ($result <= 0) {
842 $this->error = $supplierorderline->error;
843 $this->errors = $supplierorderline->errors;
844 return -1;
845 }
846
847 $fk_product = 0;
848 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
849 $fk_product = $supplierorderline->fk_product;
850
851 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
852 $langs->load("errors");
853 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
854 return -1;
855 }
856 }
857
858 // Check batch is set
859 $product = new Product($this->db);
860 $product->fetch($fk_product);
861 if (isModEnabled('productbatch')) {
862 $langs->load("errors");
863 if (!empty($product->status_batch) && empty($batch)) {
864 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
865 return -1;
866 } elseif (empty($product->status_batch) && !empty($batch)) {
867 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
868 return -1;
869 }
870
871 // check sell-by / eat-by date is mandatory
872 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
873 if (!empty($errorMsgArr)) {
874 $errorMessage = '<b>' . $product->ref . '</b> : ';
875 $errorMessage .= '<ul>';
876 foreach ($errorMsgArr as $errorMsg) {
877 $errorMessage .= '<li>' . $errorMsg . '</li>';
878 }
879 $errorMessage .= '</ul>';
880 $this->error = $errorMessage;
881 return -1;
882 }
883 }
884 unset($product);
885
886 // extrafields
887 $line->array_options = $supplierorderline->array_options;
888 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
889 foreach ($array_options as $key => $value) {
890 $line->array_options[$key] = $value;
891 }
892 }
893
894 $line->fk_product = $fk_product;
895 $line->fk_commande = $supplierorderline->fk_commande;
896 $line->fk_user = $user->id;
897 $line->comment = $comment;
898 $line->batch = $batch;
899 $line->eatby = $eatby;
900 $line->sellby = $sellby;
901 $line->status = 1;
902 $line->cost_price = $cost_price;
903 $line->fk_reception = $this->id;
904
905 $this->lines[$num] = $line;
906
907 return $num;
908 }
909
910
918 public function update($user = null, $notrigger = 0)
919 {
920 global $conf;
921 $error = 0;
922
923 // Clean parameters
924
925 if (isset($this->ref)) {
926 $this->ref = trim($this->ref);
927 }
928 if (isset($this->entity)) {
929 $this->entity = (int) $this->entity;
930 }
931 if (isset($this->ref_supplier)) {
932 $this->ref_supplier = trim($this->ref_supplier);
933 }
934 if (isset($this->socid)) {
935 $this->socid = trim($this->socid);
936 }
937 if (isset($this->fk_user_author)) {
938 $this->fk_user_author = (int) $this->fk_user_author;
939 }
940 if (isset($this->fk_user_valid)) {
941 $this->fk_user_valid = (int) $this->fk_user_valid;
942 }
943 if (isset($this->shipping_method_id)) {
944 $this->shipping_method_id = (int) $this->shipping_method_id;
945 }
946 if (isset($this->tracking_number)) {
947 $this->tracking_number = trim($this->tracking_number);
948 }
949 if (isset($this->statut)) {
950 $this->statut = (int) $this->statut;
951 }
952 if (isset($this->trueDepth)) {
953 $this->trueDepth = trim($this->trueDepth);
954 }
955 if (isset($this->trueWidth)) {
956 $this->trueWidth = trim($this->trueWidth);
957 }
958 if (isset($this->trueHeight)) {
959 $this->trueHeight = trim($this->trueHeight);
960 }
961 if (isset($this->size_units)) {
962 $this->size_units = trim((string) $this->size_units);
963 }
964 if (isset($this->weight_units)) {
965 $this->weight_units = trim((string) $this->weight_units);
966 }
967 if (isset($this->trueWeight)) {
968 $this->weight = trim((string) $this->trueWeight);
969 }
970 if (isset($this->note_private)) {
971 $this->note_private = trim($this->note_private);
972 }
973 if (isset($this->note_public)) {
974 $this->note_public = trim($this->note_public);
975 }
976 if (isset($this->model_pdf)) {
977 $this->model_pdf = trim($this->model_pdf);
978 }
979
980
981 // Check parameters
982 // Put here code to add control on parameters values
983
984 // Update request
985 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
986
987 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
988 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
989 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
990 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
991 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
992 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
993 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
994 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
995 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
996 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
997 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
998 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
999 $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
1000 $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
1001 $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
1002 $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
1003 $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
1004 $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
1005 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1006 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1007 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1008 $sql .= " entity = ".((int) $conf->entity);
1009 $sql .= " WHERE rowid=".((int) $this->id);
1010
1011 $this->db->begin();
1012
1013 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1014 $resql = $this->db->query($sql);
1015 if (!$resql) {
1016 $error++;
1017 $this->errors[] = "Error ".$this->db->lasterror();
1018 }
1019
1020 if (!$error) {
1021 if (!$notrigger) {
1022 // Call trigger
1023 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1024 if ($result < 0) {
1025 $error++;
1026 }
1027 // End call triggers
1028 }
1029 }
1030
1031 // Commit or rollback
1032 if ($error) {
1033 foreach ($this->errors as $errmsg) {
1034 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1035 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1036 }
1037 $this->db->rollback();
1038 return -1 * $error;
1039 } else {
1040 $this->db->commit();
1041 return 1;
1042 }
1043 }
1044
1051 public function delete(User $user)
1052 {
1053 global $conf, $langs, $user;
1054 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1055
1056 $error = 0;
1057 $this->error = '';
1058
1059
1060 $this->db->begin();
1061
1062 // Stock control
1063 if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > 0) {
1064 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1065
1066 $langs->load("agenda");
1067
1068 // Loop on each product line to add a stock movement
1069 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1070 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1071 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1072 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1073 $sql .= " AND cd.rowid = ed.fk_elementdet";
1074
1075 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1076 $resql = $this->db->query($sql);
1077 if ($resql) {
1078 $cpt = $this->db->num_rows($resql);
1079 for ($i = 0; $i < $cpt; $i++) {
1080 dol_syslog(get_class($this)."::delete movement index ".$i);
1081 $obj = $this->db->fetch_object($resql);
1082
1083 $mouvS = new MouvementStock($this->db);
1084 // we do not log origin because it will be deleted
1085 $mouvS->origin = null;
1086
1087 $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
1088 if ($result < 0) {
1089 $error++;
1090 $this->error = $mouvS->error;
1091 $this->errors = $mouvS->errors;
1092 }
1093 }
1094 } else {
1095 $error++;
1096 $this->errors[] = "Error ".$this->db->lasterror();
1097 }
1098 }
1099
1100 if (!$error) {
1101 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1102 $ef = $main."_extrafields";
1103
1104 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1105
1106 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1107 $sql .= " WHERE fk_reception = ".((int) $this->id);
1108
1109 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1110 // Delete linked object
1111 $res = $this->deleteObjectLinked();
1112 if ($res < 0) {
1113 $error++;
1114 }
1115
1116 if (!$error) {
1117 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1118 $sql .= " WHERE rowid = ".((int) $this->id);
1119
1120 if ($this->db->query($sql)) {
1121 // Call trigger
1122 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1123 if ($result < 0) {
1124 $error++;
1125 }
1126 // End call triggers
1127
1128 if (!empty($this->origin) && $this->origin_id > 0) {
1129 $this->fetch_origin();
1130 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
1131 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1132 $this->origin_object->loadReceptions();
1133 //var_dump($this->$origin->receptions);exit;
1134 if (count($this->origin_object->receptions) <= 0) {
1135 $this->origin_object->setStatut(3); // ordered
1136 }
1137 }
1138 }
1139
1140 if (!$error) {
1141 $this->db->commit();
1142
1143 // We delete PDFs
1144 $ref = dol_sanitizeFileName($this->ref);
1145 if (!empty($conf->reception->dir_output)) {
1146 $dir = $conf->reception->dir_output.'/'.$ref;
1147 $file = $dir.'/'.$ref.'.pdf';
1148 if (file_exists($file)) {
1149 if (!dol_delete_file($file)) {
1150 return 0;
1151 }
1152 }
1153 if (file_exists($dir)) {
1154 if (!dol_delete_dir_recursive($dir)) {
1155 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1156 return 0;
1157 }
1158 }
1159 }
1160
1161 return 1;
1162 } else {
1163 $this->db->rollback();
1164 return -1;
1165 }
1166 } else {
1167 $this->error = $this->db->lasterror()." - sql=$sql";
1168 $this->db->rollback();
1169 return -3;
1170 }
1171 } else {
1172 $this->error = $this->db->lasterror()." - sql=$sql";
1173 $this->db->rollback();
1174 return -2;
1175 }
1176 } else {
1177 $this->error = $this->db->lasterror()." - sql=$sql";
1178 $this->db->rollback();
1179 return -1;
1180 }
1181 } else {
1182 $this->db->rollback();
1183 return -1;
1184 }
1185 }
1186
1187 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1193 public function fetch_lines()
1194 {
1195 // phpcs:enable
1196 $this->lines = array();
1197
1198 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1199
1200 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1201 $sql .= " WHERE fk_reception = ".((int) $this->id);
1202
1203 $resql = $this->db->query($sql);
1204
1205 if (!empty($resql)) {
1206 while ($obj = $this->db->fetch_object($resql)) {
1207 $line = new CommandeFournisseurDispatch($this->db);
1208
1209 $line->fetch($obj->rowid);
1210
1211 // TODO Remove or keep this ?
1212 $line->fetch_product();
1213
1214 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1215 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1216 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1217 $sql_commfourndet .= ' ORDER BY rang';
1218
1219 $resql_commfourndet = $this->db->query($sql_commfourndet);
1220 if (!empty($resql_commfourndet)) {
1221 $obj = $this->db->fetch_object($resql_commfourndet);
1222 $line->qty_asked = $obj->qty;
1223 $line->description = $obj->description;
1224 $line->desc = $obj->description;
1225 $line->tva_tx = $obj->tva_tx;
1226 $line->vat_src_code = $obj->vat_src_code;
1227 $line->subprice = $obj->subprice;
1228 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1229 $line->remise_percent = $obj->remise_percent;
1230 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1231 $line->ref_supplier = $obj->ref;
1232 $line->total_ht = $obj->total_ht;
1233 $line->total_ttc = $obj->total_ttc;
1234 $line->total_tva = $obj->total_tva;
1235 } else {
1236 $line->qty_asked = 0;
1237 $line->description = '';
1238 $line->desc = '';
1239 $line->label = $obj->label;
1240 }
1241
1242 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1243 $tva = $pu_ht * $line->tva_tx / 100;
1244 $this->total_ht += $pu_ht;
1245 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1246
1247 $this->total_ttc += $pu_ht + $tva;
1248
1249 if (isModEnabled('productbatch') && !empty($line->batch)) {
1250 $detail_batch = new stdClass();
1251 $detail_batch->eatby = $line->eatby;
1252 $detail_batch->sellby = $line->sellby;
1253 $detail_batch->batch = $line->batch;
1254 $detail_batch->qty = $line->qty;
1255
1256 $line->detail_batch[] = $detail_batch;
1257 }
1258
1259 $this->lines[] = $line;
1260 }
1261
1262 return 1;
1263 } else {
1264 return -1;
1265 }
1266 }
1267
1278 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1279 {
1280 global $langs, $hookmanager;
1281
1282 $result = '';
1283 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1284 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1285 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1286
1287 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1288
1289 if ($short) {
1290 return $url;
1291 }
1292
1293 $linkclose = '';
1294 if (empty($notooltip)) {
1295 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1296 $label = $langs->trans("Reception");
1297 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1298 }
1299 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1300 $linkclose .= ' class="classfortooltip"';
1301 }
1302
1303 $linkstart = '<a href="'.$url.'"';
1304 $linkstart .= $linkclose.'>';
1305 $linkend = '</a>';
1306
1307 $result .= $linkstart;
1308 if ($withpicto) {
1309 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1310 }
1311 if ($withpicto != 2) {
1312 $result .= $this->ref;
1313 }
1314
1315 $result .= $linkend;
1316
1317 global $action;
1318 $hookmanager->initHooks(array($this->element . 'dao'));
1319 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1320 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1321 if ($reshook > 0) {
1322 $result = $hookmanager->resPrint;
1323 } else {
1324 $result .= $hookmanager->resPrint;
1325 }
1326 return $result;
1327 }
1328
1335 public function getLibStatut($mode = 0)
1336 {
1337 return $this->LibStatut($this->statut, $mode);
1338 }
1339
1340 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1348 public function LibStatut($status, $mode)
1349 {
1350 // phpcs:enable
1351 global $langs;
1352
1353 // List of long language codes for status
1354 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1355 $this->labelStatus[0] = 'StatusReceptionDraft';
1356 // product to receive if stock increase is on close or already received if stock increase is on validation
1357 $this->labelStatus[1] = 'StatusReceptionValidated';
1358 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1359 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1360 }
1361 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1362 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1363 }
1364 $this->labelStatus[2] = 'StatusReceptionProcessed';
1365
1366 // List of short language codes for status
1367 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1368 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1369 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1370 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1371
1372 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1373 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1374
1375 $statusType = 'status'.$status;
1376 if ($status == self::STATUS_VALIDATED) {
1377 $statusType = 'status4';
1378 }
1379 if ($status == self::STATUS_CLOSED) {
1380 $statusType = 'status6';
1381 }
1382
1383 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1384 }
1385
1393 public function getKanbanView($option = '', $arraydata = null)
1394 {
1395 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1396
1397 $return = '<div class="box-flex-item box-flex-grow-zero">';
1398 $return .= '<div class="info-box info-box-sm">';
1399 $return .= '<div class="info-box-icon bg-infobox-action">';
1400 $return .= img_picto('', 'order');
1401 $return .= '</div>';
1402 $return .= '<div class="info-box-content">';
1403 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1404 if ($selected >= 0) {
1405 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1406 }
1407 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1408 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1409 }
1410 /*if (property_exists($this, 'total_ht')) {
1411 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1412 }*/
1413 if (method_exists($this, 'getLibStatut')) {
1414 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1415 }
1416 $return .= '</div>';
1417 $return .= '</div>';
1418 $return .= '</div>';
1419
1420 return $return;
1421 }
1422
1430 public function initAsSpecimen()
1431 {
1432 global $langs;
1433
1434 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1435 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1436 $now = dol_now();
1437
1438 dol_syslog(get_class($this)."::initAsSpecimen");
1439
1440 $order = new CommandeFournisseur($this->db);
1441 $order->initAsSpecimen();
1442
1443 // Initialise parameters
1444 $this->id = 0;
1445 $this->ref = 'SPECIMEN';
1446 $this->specimen = 1;
1447 $this->statut = 1;
1448 $this->status = 1;
1449 $this->date = $now;
1450 $this->date_creation = $now;
1451 $this->date_valid = $now;
1452 $this->date_delivery = $now;
1453 $this->date_reception = $now + 24 * 3600;
1454
1455 $this->entrepot_id = 0;
1456 $this->socid = 1;
1457
1458 $this->origin_id = 1;
1459 $this->origin_type = 'supplier_order';
1460 $this->origin_object = $order;
1461
1462 $this->note_private = 'Private note';
1463 $this->note_public = 'Public note';
1464
1465 $this->tracking_number = 'TRACKID-ABC123';
1466
1467 $this->fk_incoterms = 1;
1468
1469 $nbp = 5;
1470 $xnbp = 0;
1471 while ($xnbp < $nbp) {
1472 $line = new CommandeFournisseurDispatch($this->db);
1473 $line->desc = $langs->trans("Description")." ".$xnbp;
1474 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1475 $line->label = $langs->trans("Description")." ".$xnbp;
1476 $line->qty = 10;
1477
1478 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1479
1480 $this->lines[] = $line;
1481 $xnbp++;
1482 }
1483
1484 return 1;
1485 }
1486
1494 public function setDeliveryDate($user, $delivery_date)
1495 {
1496 // phpcs:enable
1497 if ($user->hasRight('reception', 'creer')) {
1498 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1499 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1500 $sql .= " WHERE rowid = ".((int) $this->id);
1501
1502 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1503 $resql = $this->db->query($sql);
1504 if ($resql) {
1505 $this->date_delivery = $delivery_date;
1506 return 1;
1507 } else {
1508 $this->error = $this->db->error();
1509 return -1;
1510 }
1511 } else {
1512 return -2;
1513 }
1514 }
1515
1516 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1522 public function fetch_delivery_methods()
1523 {
1524 // phpcs:enable
1525 global $langs;
1526 $this->meths = array();
1527
1528 $sql = "SELECT em.rowid, em.code, em.libelle";
1529 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1530 $sql .= " WHERE em.active = 1";
1531 $sql .= " ORDER BY em.libelle ASC";
1532
1533 $resql = $this->db->query($sql);
1534 if ($resql) {
1535 while ($obj = $this->db->fetch_object($resql)) {
1536 $label = $langs->trans('ReceptionMethod'.$obj->code);
1537 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1538 }
1539 }
1540 }
1541
1542 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1549 public function list_delivery_methods($id = 0)
1550 {
1551 // phpcs:enable
1552 global $langs;
1553
1554 $this->listmeths = array();
1555 $i = 0;
1556
1557 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1558 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1559 if (!empty($id)) {
1560 $sql .= " WHERE em.rowid = ".((int) $id);
1561 }
1562
1563 $resql = $this->db->query($sql);
1564 if ($resql) {
1565 while ($obj = $this->db->fetch_object($resql)) {
1566 $this->listmeths[$i]['rowid'] = $obj->rowid;
1567 $this->listmeths[$i]['code'] = $obj->code;
1568 $label = $langs->trans('ReceptionMethod'.$obj->code);
1569 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1570 $this->listmeths[$i]['description'] = $obj->description;
1571 $this->listmeths[$i]['tracking'] = $obj->tracking;
1572 $this->listmeths[$i]['active'] = $obj->active;
1573 $i++;
1574 }
1575 }
1576 }
1577
1584 public function getUrlTrackingStatus($value = '')
1585 {
1586 if (!empty($this->shipping_method_id)) {
1587 $sql = "SELECT em.code, em.tracking";
1588 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1589 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1590
1591 $resql = $this->db->query($sql);
1592 if ($resql) {
1593 if ($obj = $this->db->fetch_object($resql)) {
1594 $tracking = $obj->tracking;
1595 }
1596 }
1597 }
1598
1599 if (!empty($tracking) && !empty($value)) {
1600 $url = str_replace('{TRACKID}', $value, $tracking);
1601 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1602 } else {
1603 $this->tracking_url = $value;
1604 }
1605 }
1606
1612 public function setClosed()
1613 {
1614 global $conf, $langs, $user;
1615
1616 $error = 0;
1617
1618 // Protection. This avoid to move stock later when we should not
1619 if ($this->statut == Reception::STATUS_CLOSED) {
1620 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1621 return 0;
1622 }
1623
1624 $this->db->begin();
1625
1626 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1627 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1628
1629 $resql = $this->db->query($sql);
1630 if ($resql) {
1631 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1632 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1633 $order = new CommandeFournisseur($this->db);
1634 $order->fetch($this->origin_id);
1635
1636 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1637
1638 $receptions_match_order = 1;
1639 foreach ($order->lines as $line) {
1640 $lineid = $line->id;
1641 $qty = $line->qty;
1642 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1643 $receptions_match_order = 0;
1644 $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';
1645 dol_syslog($text);
1646 break;
1647 }
1648 }
1649 if ($receptions_match_order) {
1650 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');
1651 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1652 }
1653 }
1654
1655 $this->statut = self::STATUS_CLOSED;
1656 $this->status = self::STATUS_CLOSED;
1657
1658 // If stock increment is done on closing
1659 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1660 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1661
1662 $langs->load("agenda");
1663
1664 // Loop on each product line to add a stock movement
1665 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1666 $sql = "SELECT cd.fk_product, cd.subprice,";
1667 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1668 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1669 $sql .= " ed.cost_price";
1670 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1671 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1672 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1673 $sql .= " AND cd.rowid = ed.fk_elementdet";
1674
1675 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1676 $resql = $this->db->query($sql);
1677
1678 if ($resql) {
1679 $cpt = $this->db->num_rows($resql);
1680 for ($i = 0; $i < $cpt; $i++) {
1681 $obj = $this->db->fetch_object($resql);
1682
1683 $qty = $obj->qty;
1684
1685 if ($qty <= 0) {
1686 continue;
1687 }
1688 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1689
1690 $mouvS = new MouvementStock($this->db);
1691 $mouvS->origin = &$this;
1692 $mouvS->setOrigin($this->element, $this->id);
1693
1694 if (empty($obj->batch)) {
1695 // line without batch detail
1696
1697 // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1698 $inventorycode = '';
1699 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1700 if ($result < 0) {
1701 $this->error = $mouvS->error;
1702 $this->errors = $mouvS->errors;
1703 $error++;
1704 break;
1705 }
1706 } else {
1707 // line with batch detail
1708
1709 // 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
1710 $inventorycode = '';
1711 $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);
1712
1713 if ($result < 0) {
1714 $this->error = $mouvS->error;
1715 $this->errors = $mouvS->errors;
1716 $error++;
1717 break;
1718 }
1719 }
1720 }
1721 } else {
1722 $this->error = $this->db->lasterror();
1723 $error++;
1724 }
1725 }
1726
1727 // Call trigger
1728 if (!$error) {
1729 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1730 if ($result < 0) {
1731 $error++;
1732 }
1733 }
1734 } else {
1735 dol_print_error($this->db);
1736 $error++;
1737 }
1738
1739 if (!$error) {
1740 $this->db->commit();
1741 return 1;
1742 } else {
1743 $this->statut = self::STATUS_VALIDATED;
1744 $this->status = self::STATUS_VALIDATED;
1745 $this->db->rollback();
1746 return -1;
1747 }
1748 }
1749
1755 public function setBilled()
1756 {
1757 global $user;
1758 $error = 0;
1759
1760 $this->db->begin();
1761
1762 if ($this->statut == Reception::STATUS_VALIDATED) {
1763 // do not close if already closed
1764 $this->setClosed();
1765 }
1766
1767 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1768 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1769
1770 $resql = $this->db->query($sql);
1771 if ($resql) {
1772 $this->billed = 1;
1773
1774 // Call trigger
1775 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1776 if ($result < 0) {
1777 $this->billed = 0;
1778 $error++;
1779 }
1780 } else {
1781 $error++;
1782 $this->errors[] = $this->db->lasterror;
1783 }
1784
1785 if (empty($error)) {
1786 $this->db->commit();
1787 return 1;
1788 } else {
1789 $this->db->rollback();
1790 return -1;
1791 }
1792 }
1793
1799 public function reOpen()
1800 {
1801 global $conf, $langs, $user;
1802
1803 $error = 0;
1804
1805 $this->db->begin();
1806
1807 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1808 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1809
1810 $resql = $this->db->query($sql);
1811 if ($resql) {
1812 $this->statut = self::STATUS_VALIDATED;
1813 $this->status = self::STATUS_VALIDATED;
1814 $this->billed = 0;
1815
1816 // If stock increment is done on closing
1817 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1818 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1819 $numref = $this->ref;
1820 $langs->load("agenda");
1821
1822 // Loop on each product line to add a stock movement
1823 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1824 $sql = "SELECT ed.fk_product, cd.subprice,";
1825 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1826 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1827 $sql .= " ed.cost_price";
1828 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1829 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1830 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1831 $sql .= " AND cd.rowid = ed.fk_elementdet";
1832
1833 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1834 $resql = $this->db->query($sql);
1835 if ($resql) {
1836 $cpt = $this->db->num_rows($resql);
1837 for ($i = 0; $i < $cpt; $i++) {
1838 $obj = $this->db->fetch_object($resql);
1839
1840 $qty = $obj->qty;
1841
1842 if ($qty <= 0) {
1843 continue;
1844 }
1845
1846 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1847
1848 //var_dump($this->lines[$i]);
1849 $mouvS = new MouvementStock($this->db);
1850 $mouvS->origin = &$this;
1851 $mouvS->setOrigin($this->element, $this->id);
1852
1853 if (empty($obj->batch)) {
1854 // line without batch detail
1855
1856 // 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
1857 $inventorycode = '';
1858 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1859
1860 if ($result < 0) {
1861 $this->error = $mouvS->error;
1862 $this->errors = $mouvS->errors;
1863 $error++;
1864 break;
1865 }
1866 } else {
1867 // line with batch detail
1868
1869 // 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
1870 $inventorycode = '';
1871 $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);
1872 if ($result < 0) {
1873 $this->error = $mouvS->error;
1874 $this->errors = $mouvS->errors;
1875 $error++;
1876 break;
1877 }
1878 }
1879 }
1880 } else {
1881 $this->error = $this->db->lasterror();
1882 $error++;
1883 }
1884 }
1885
1886 if (!$error) {
1887 // Call trigger
1888 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1889 if ($result < 0) {
1890 $error++;
1891 }
1892 }
1893
1894 if (!$error && $this->origin == 'order_supplier') {
1895 $commande = new CommandeFournisseur($this->db);
1896 $commande->fetch($this->origin_id);
1897 $result = $commande->setStatus($user, 4);
1898 if ($result < 0) {
1899 $error++;
1900 $this->error = $commande->error;
1901 $this->errors = $commande->errors;
1902 }
1903 }
1904 } else {
1905 $error++;
1906 $this->errors[] = $this->db->lasterror();
1907 }
1908
1909 if (!$error) {
1910 $this->db->commit();
1911 return 1;
1912 } else {
1913 $this->db->rollback();
1914 return -1;
1915 }
1916 }
1917
1924 public function setDraft($user)
1925 {
1926 // phpcs:enable
1927 global $conf, $langs;
1928
1929 $error = 0;
1930
1931 // Protection
1932 if ($this->statut <= self::STATUS_DRAFT) {
1933 return 0;
1934 }
1935
1936 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1937 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1938 $this->error = 'Permission denied';
1939 return -1;
1940 }
1941
1942 $this->db->begin();
1943
1944 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1945 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1946 $sql .= " WHERE rowid = ".((int) $this->id);
1947
1948 dol_syslog(__METHOD__, LOG_DEBUG);
1949 if ($this->db->query($sql)) {
1950 // If stock increment is done on closing
1951 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1952 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1953
1954 $langs->load("agenda");
1955
1956 // Loop on each product line to add a stock movement
1957 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1958 $sql = "SELECT cd.fk_product, cd.subprice,";
1959 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1960 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1961 $sql .= " ed.cost_price";
1962 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1963 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1964 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1965 $sql .= " AND cd.rowid = ed.fk_elementdet";
1966
1967 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1968 $resql = $this->db->query($sql);
1969 if ($resql) {
1970 $cpt = $this->db->num_rows($resql);
1971 for ($i = 0; $i < $cpt; $i++) {
1972 $obj = $this->db->fetch_object($resql);
1973
1974 $qty = $obj->qty;
1975
1976
1977 if ($qty <= 0) {
1978 continue;
1979 }
1980 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1981
1982 //var_dump($this->lines[$i]);
1983 $mouvS = new MouvementStock($this->db);
1984 $mouvS->origin = &$this;
1985 $mouvS->setOrigin($this->element, $this->id);
1986
1987 if (empty($obj->batch)) {
1988 // line without batch detail
1989
1990 // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1991 $inventorycode = '';
1992 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1993 if ($result < 0) {
1994 $this->error = $mouvS->error;
1995 $this->errors = $mouvS->errors;
1996 $error++;
1997 break;
1998 }
1999 } else {
2000 // line with batch detail
2001
2002 // 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
2003 $inventorycode = '';
2004 $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);
2005 if ($result < 0) {
2006 $this->error = $mouvS->error;
2007 $this->errors = $mouvS->errors;
2008 $error++;
2009 break;
2010 }
2011 }
2012 }
2013 } else {
2014 $this->error = $this->db->lasterror();
2015 $error++;
2016 }
2017 }
2018
2019 if (!$error) {
2020 // Call trigger
2021 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2022 if ($result < 0) {
2023 $error++;
2024 }
2025 }
2026 if ($this->origin == 'order_supplier') {
2027 if (!empty($this->origin) && $this->origin_id > 0) {
2028 $this->fetch_origin();
2029 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2030 // Check if there is no more reception validated.
2031 $this->origin_object->fetchObjectLinked();
2032 $setStatut = 1;
2033 if (!empty($this->origin_object->linkedObjects['reception'])) {
2034 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2035 if ($rcption->statut > 0) {
2036 $setStatut = 0;
2037 break;
2038 }
2039 }
2040 //var_dump($this->$origin->receptions);exit;
2041 if ($setStatut) {
2042 $this->origin_object->setStatut(3); // ordered
2043 }
2044 }
2045 }
2046 }
2047 }
2048
2049 if (!$error) {
2050 $this->statut = self::STATUS_DRAFT;
2051 $this->status = self::STATUS_DRAFT;
2052 $this->db->commit();
2053 return 1;
2054 } else {
2055 $this->db->rollback();
2056 return -1;
2057 }
2058 } else {
2059 $this->error = $this->db->error();
2060 $this->db->rollback();
2061 return -1;
2062 }
2063 }
2064
2075 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2076 {
2077 global $conf, $langs;
2078
2079 $langs->load("receptions");
2080
2081 if (!dol_strlen($modele)) {
2082 $modele = 'squille';
2083
2084 if ($this->model_pdf) {
2085 $modele = $this->model_pdf;
2086 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2087 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2088 }
2089 }
2090
2091 $modelpath = "core/modules/reception/doc/";
2092
2093 $this->fetch_origin();
2094
2095 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2096 }
2097
2106 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2107 {
2108 $tables = array('reception');
2109
2110 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2111 }
2112
2121 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2122 {
2123 $tables = array(
2124 'receptiondet_batch'
2125 );
2126
2127 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2128 }
2129}
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