dolibarr 21.0.0-alpha
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
37require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
38require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
40if (isModEnabled("propal")) {
41 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
42}
43if (isModEnabled('order')) {
44 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
45}
46
47
52{
53 use CommonIncoterm;
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
80 public $socid;
84 public $ref_supplier;
85
89 public $entrepot_id;
93 public $tracking_number;
97 public $tracking_url;
101 public $billed;
105 public $model_pdf;
106
110 public $weight;
114 public $trueWeight;
118 public $weight_units;
122 public $trueWidth;
126 public $width_units;
130 public $trueHeight;
134 public $height_units;
138 public $trueDepth;
142 public $depth_units;
146 public $trueSize;
150 public $size_units;
154 public $user_author_id;
155
159 public $date_delivery;
160
166 public $date;
167
171 public $date_reception;
172
176 public $date_valid;
177
181 public $meths;
185 public $listmeths; // List of carriers
186
190 public $lines = array();
191
192
197 public $detail_batch;
198
199 const STATUS_DRAFT = 0;
200 const STATUS_VALIDATED = 1;
201 const STATUS_CLOSED = 2;
202
203
209 public function __construct($db)
210 {
211 $this->db = $db;
212
213 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
214 }
215
222 public function getNextNumRef($soc)
223 {
224 global $langs, $conf;
225 $langs->load("receptions");
226
227 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
228 $mybool = false;
229
230 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
231 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
232
233 // Include file with class
234 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
235
236 foreach ($dirmodels as $reldir) {
237 $dir = dol_buildpath($reldir."core/modules/reception/");
238
239 // Load file with numbering class (if found)
240 $mybool = ((bool) @include_once $dir.$file) || $mybool;
241 }
242
243 if (!$mybool) {
244 dol_print_error(null, "Failed to include file ".$file);
245 return '';
246 }
247
248 $obj = new $classname();
249 '@phan-var-force ModelNumRefReception $obj';
250
251 $numref = $obj->getNextValue($soc, $this);
252
253 if ($numref != "") {
254 return $numref;
255 } else {
256 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
257 return "";
258 }
259 } else {
260 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
261 return "";
262 }
263 }
264
272 public function create($user, $notrigger = 0)
273 {
274 global $conf;
275
276 $now = dol_now();
277
278 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
279 $error = 0;
280
281 // Clean parameters
282 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
283 if (empty($this->fk_project)) {
284 $this->fk_project = 0;
285 }
286 if (empty($this->weight_units)) {
287 $this->weight_units = 0;
288 }
289 if (empty($this->size_units)) {
290 $this->size_units = 0;
291 }
292
293 $this->user = $user;
294
295 $this->db->begin();
296
297 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
298 $sql .= "ref";
299 $sql .= ", entity";
300 $sql .= ", ref_supplier";
301 $sql .= ", date_creation";
302 $sql .= ", fk_user_author";
303 $sql .= ", date_reception";
304 $sql .= ", date_delivery";
305 $sql .= ", fk_soc";
306 $sql .= ", fk_projet";
307 $sql .= ", fk_shipping_method";
308 $sql .= ", tracking_number";
309 $sql .= ", weight";
310 $sql .= ", size";
311 $sql .= ", width";
312 $sql .= ", height";
313 $sql .= ", weight_units";
314 $sql .= ", size_units";
315 $sql .= ", note_private";
316 $sql .= ", note_public";
317 $sql .= ", model_pdf";
318 $sql .= ", fk_incoterms, location_incoterms";
319 $sql .= ") VALUES (";
320 $sql .= "'(PROV)'";
321 $sql .= ", ".((int) $conf->entity);
322 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
323 $sql .= ", '".$this->db->idate($now)."'";
324 $sql .= ", ".((int) $user->id);
325 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
326 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
327 $sql .= ", ".((int) $this->socid);
328 $sql .= ", ".((int) $this->fk_project);
329 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
330 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
331 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
332 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
333 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
334 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
335 $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((float) $this->weight_units));
336 $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((float) $this->size_units));
337 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
338 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
339 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
340 $sql .= ", ".(int) $this->fk_incoterms;
341 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
342 $sql .= ")";
343
344 dol_syslog(get_class($this)."::create", LOG_DEBUG);
345
346 $resql = $this->db->query($sql);
347
348 if ($resql) {
349 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
350
351 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
352 $sql .= " SET ref = '(PROV".((int) $this->id).")'";
353 $sql .= " WHERE rowid = ".((int) $this->id);
354
355 dol_syslog(get_class($this)."::create", LOG_DEBUG);
356 if ($this->db->query($sql)) {
357 // Insert of lines
358 $num = count($this->lines);
359 for ($i = 0; $i < $num; $i++) {
360 $this->lines[$i]->fk_reception = $this->id;
361
362 if (!$this->lines[$i]->create($user) > 0) {
363 $error++;
364 }
365 }
366
367 if (!$error && $this->id && $this->origin_id) {
368 $ret = $this->add_object_linked();
369 if (!$ret) {
370 $error++;
371 }
372 }
373
374 // Create extrafields
375 if (!$error) {
376 $result = $this->insertExtraFields();
377 if ($result < 0) {
378 $error++;
379 }
380 }
381
382 if (!$error && !$notrigger) {
383 // Call trigger
384 $result = $this->call_trigger('RECEPTION_CREATE', $user);
385 if ($result < 0) {
386 $error++;
387 }
388 // End call triggers
389 }
390
391 if (!$error) {
392 $this->db->commit();
393 return $this->id;
394 } else {
395 foreach ($this->errors as $errmsg) {
396 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
397 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
398 }
399 $this->db->rollback();
400 return -1 * $error;
401 }
402 } else {
403 $error++;
404 $this->error = $this->db->lasterror()." - sql=$sql";
405 $this->db->rollback();
406 return -2;
407 }
408 } else {
409 $error++;
410 $this->error = $this->db->error()." - sql=$sql";
411 $this->db->rollback();
412 return -1;
413 }
414 }
415
416
417
426 public function fetch($id, $ref = '', $ref_ext = '')
427 {
428 // Check parameters
429 if (empty($id) && empty($ref) && empty($ref_ext)) {
430 return -1;
431 }
432
433 $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";
434 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
435 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery";
436 $sql .= ", e.fk_shipping_method, e.tracking_number";
437 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
438 $sql .= ", e.note_private, e.note_public";
439 $sql .= ', e.fk_incoterms, e.location_incoterms';
440 $sql .= ', i.libelle as label_incoterms';
441 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
442 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
443 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
444 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
445 if ($id) {
446 $sql .= " AND e.rowid = ".((int) $id);
447 }
448 if ($ref) {
449 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
450 }
451 if ($ref_ext) {
452 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
453 }
454
455 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
456 $result = $this->db->query($sql);
457 if ($result) {
458 if ($this->db->num_rows($result)) {
459 $obj = $this->db->fetch_object($result);
460
461 $this->id = $obj->rowid;
462 $this->entity = $obj->entity;
463 $this->ref = $obj->ref;
464 $this->socid = $obj->socid;
465 $this->ref_supplier = $obj->ref_supplier;
466 $this->ref_ext = $obj->ref_ext;
467 $this->statut = $obj->status;
468 $this->status = $obj->status;
469 $this->billed = $obj->billed;
470
471 $this->user_author_id = $obj->fk_user_author;
472 $this->date_creation = $this->db->jdate($obj->date_creation);
473 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
474 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
475 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
476 $this->model_pdf = $obj->model_pdf;
477 $this->shipping_method_id = $obj->fk_shipping_method;
478 $this->tracking_number = $obj->tracking_number;
479 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
480 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
481 $this->origin_id = $obj->origin_id;
482
483 $this->trueWeight = $obj->weight;
484 $this->weight_units = $obj->weight_units;
485
486 $this->trueWidth = $obj->width;
487 $this->width_units = $obj->size_units;
488 $this->trueHeight = $obj->height;
489 $this->height_units = $obj->size_units;
490 $this->trueDepth = $obj->size;
491 $this->depth_units = $obj->size_units;
492
493 $this->note_public = $obj->note_public;
494 $this->note_private = $obj->note_private;
495
496 // A denormalized value
497 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
498 $this->size_units = $obj->size_units;
499
500 //Incoterms
501 $this->fk_incoterms = $obj->fk_incoterms;
502 $this->location_incoterms = $obj->location_incoterms;
503 $this->label_incoterms = $obj->label_incoterms;
504
505 $this->db->free($result);
506
507 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
508 //$this->pdf_filename = $file;
509
510 // Tracking url
511 $this->getUrlTrackingStatus($obj->tracking_number);
512
513 /*
514 * Thirdparty
515 */
516 $result = $this->fetch_thirdparty();
517
518
519 // Retrieve all extrafields for reception
520 // fetch optionals attributes and labels
521 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
522 $extrafields = new ExtraFields($this->db);
523 $extrafields->fetch_name_optionals_label($this->table_element, true);
524 $this->fetch_optionals();
525
526 /*
527 * Lines
528 */
529 $result = $this->fetch_lines();
530 if ($result < 0) {
531 return -3;
532 }
533
534 return 1;
535 } else {
536 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
537 $this->error = 'Reception with id '.$id.' not found';
538 return 0;
539 }
540 } else {
541 $this->error = $this->db->error();
542 return -1;
543 }
544 }
545
553 public function valid($user, $notrigger = 0)
554 {
555 global $conf, $langs;
556
557 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
558
559 dol_syslog(get_class($this)."::valid");
560
561 // Protection
562 if ($this->statut) {
563 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
564 return 0;
565 }
566
567 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
568 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
569 $this->error = 'Permission denied';
570 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
571 return -1;
572 }
573
574 $this->db->begin();
575
576 $error = 0;
577
578 // Define new ref
579 $soc = new Societe($this->db);
580 $soc->fetch($this->socid);
581
582
583 // Define new ref
584 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
585 $numref = $this->getNextNumRef($soc);
586 } else {
587 $numref = $this->ref;
588 }
589
590 $this->newref = dol_sanitizeFileName($numref);
591
592 $now = dol_now();
593
594 // Validate
595 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
596 $sql .= " ref='".$this->db->escape($numref)."'";
597 $sql .= ", fk_statut = 1";
598 $sql .= ", date_valid = '".$this->db->idate($now)."'";
599 $sql .= ", fk_user_valid = ".((int) $user->id);
600 $sql .= " WHERE rowid = ".((int) $this->id);
601 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
602 $resql = $this->db->query($sql);
603 if (!$resql) {
604 $this->error = $this->db->lasterror();
605 $error++;
606 }
607
608 // If stock increment is done on reception (recommended choice)
609 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
610 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
611
612 $langs->load("agenda");
613
614 // Loop on each product line to add a stock movement
615 // TODO in future, reception lines may not be linked to order line
616 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
617 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
618 $sql .= " ed.eatby, ed.sellby, ed.batch,";
619 $sql .= " ed.cost_price";
620 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
621 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
622 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
623 $sql .= " AND cd.rowid = ed.fk_elementdet";
624
625 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
626 $resql = $this->db->query($sql);
627 if ($resql) {
628 $cpt = $this->db->num_rows($resql);
629 for ($i = 0; $i < $cpt; $i++) {
630 $obj = $this->db->fetch_object($resql);
631
632 $qty = $obj->qty;
633
634 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
635 continue;
636 }
637 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
638
639 //var_dump($this->lines[$i]);
640 $mouvS = new MouvementStock($this->db);
641 $mouvS->origin = &$this;
642 $mouvS->setOrigin($this->element, $this->id);
643
644 if (empty($obj->batch)) {
645 // line without batch detail
646
647 // 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.
648 $inventorycode = '';
649 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
650
651 if (intval($result) < 0) {
652 $error++;
653 $this->errors[] = $mouvS->error;
654 $this->errors = array_merge($this->errors, $mouvS->errors);
655 break;
656 }
657 } else {
658 // line with batch detail
659
660 // 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.
661 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
662 $inventorycode = '';
663 $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);
664
665 if (intval($result) < 0) {
666 $error++;
667 $this->errors[] = $mouvS->error;
668 $this->errors = array_merge($this->errors, $mouvS->errors);
669 break;
670 }
671 }
672 }
673 } else {
674 $this->db->rollback();
675 $this->error = $this->db->error();
676 return -2;
677 }
678 }
679
680 if (!$error) {
681 // Change status of purchase order to "reception in process" or "totally received"
682 $status = $this->getStatusDispatch();
683 if ($status < 0) {
684 $error++;
685 } else {
686 $trigger_key = '';
687 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
688 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
689 if ($ret < 0) {
690 $error++;
691 $this->errors = array_merge($this->errors, $this->origin_object->errors);
692 }
693 } else {
694 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
695 if ($ret < 0) {
696 $error++;
697 }
698 }
699 }
700 }
701
702 if (!$error && !$notrigger) {
703 // Call trigger
704 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
705 if ($result < 0) {
706 $error++;
707 }
708 // End call triggers
709 }
710
711 if (!$error) {
712 $this->oldref = $this->ref;
713
714 // Rename directory if dir was a temporary ref
715 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
716 // Now we rename also files into index
717 $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)."'";
718 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
719 $resql = $this->db->query($sql);
720 if (!$resql) {
721 $error++;
722 $this->error = $this->db->lasterror();
723 }
724 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
725 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
726 $resql = $this->db->query($sql);
727 if (!$resql) {
728 $error++;
729 $this->error = $this->db->lasterror();
730 }
731
732 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
733 $oldref = dol_sanitizeFileName($this->ref);
734 $newref = dol_sanitizeFileName($numref);
735 $dirsource = $conf->reception->dir_output.'/'.$oldref;
736 $dirdest = $conf->reception->dir_output.'/'.$newref;
737 if (!$error && file_exists($dirsource)) {
738 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
739
740 if (@rename($dirsource, $dirdest)) {
741 dol_syslog("Rename ok");
742 // Rename docs starting with $oldref with $newref
743 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
744 foreach ($listoffiles as $fileentry) {
745 $dirsource = $fileentry['name'];
746 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
747 $dirsource = $fileentry['path'].'/'.$dirsource;
748 $dirdest = $fileentry['path'].'/'.$dirdest;
749 @rename($dirsource, $dirdest);
750 }
751 }
752 }
753 }
754 }
755
756 // Set new ref and current status
757 if (!$error) {
758 $this->ref = $numref;
759 $this->statut = self::STATUS_VALIDATED;
760 $this->status = self::STATUS_VALIDATED;
761 }
762
763 if (!$error) {
764 $this->db->commit();
765 return 1;
766 } else {
767 foreach ($this->errors as $errmsg) {
768 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
769 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
770 }
771 $this->db->rollback();
772 return -1 * $error;
773 }
774 }
775
781 public function getStatusDispatch()
782 {
783 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
784 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
785
787
788 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
789 if (empty($this->origin_object)) {
790 $this->fetch_origin();
791 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
792 $res = $this->origin_object->fetch_lines();
793 if ($this->origin_object instanceof CommandeFournisseur) {
794 $this->commandeFournisseur = $this->origin_object; // deprecated
795 } else {
796 $this->commandeFournisseur = null; // deprecated
797 }
798 if ($res < 0) {
799 return $res;
800 }
801 }
802 }
803
804 $qty_received = array();
805 $qty_wished = array();
806
807 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
808 $filter = array('t.fk_element' => $this->origin_id);
809 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
810 $filter['t.status'] = 1; // Restrict to lines with status validated
811 }
812
813 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
814 if ($ret < 0) {
815 $this->error = $supplierorderdispatch->error;
816 $this->errors = $supplierorderdispatch->errors;
817 return $ret;
818 } else {
819 // build array with quantity received by product in all supplier orders (origin)
820 foreach ($supplierorderdispatch->lines as $dispatch_line) {
821 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
822 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
823 } else {
824 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
825 }
826 }
827
828 // qty wished in origin (purchase order, ...)
829 foreach ($this->origin_object->lines as $origin_line) {
830 // exclude lines not qualified for reception
831 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
832 continue;
833 }
834
835 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
836 }
837
838 // compare array
839 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
840 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
841 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
842
843 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
845 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
846 // set totally received if more products received than ordered
847 $close = 0;
848
849 if (count($diff_array) > 0) {
850 // there are some difference between the two arrays
851 // scan the array of results
852 foreach ($diff_array as $key => $value) {
853 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
854 if ($qty_received[$key] >= $qty_wished[$key]) {
855 $close++;
856 }
857 }
858 }
859
860 if ($close == count($diff_array)) {
861 // all the products are received equal or more than the ordered quantity
863 }
864 }
865 }
866 }
867
868 return $status;
869 }
870
887 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
888 {
889 global $conf, $langs, $user;
890
891 $num = count($this->lines);
892 $line = new CommandeFournisseurDispatch($this->db);
893
894 $line->fk_entrepot = $entrepot_id;
895 $line->fk_commandefourndet = $id;
896 $line->qty = $qty;
897
898 $supplierorderline = new CommandeFournisseurLigne($this->db);
899 $result = $supplierorderline->fetch($id);
900 if ($result <= 0) {
901 $this->error = $supplierorderline->error;
902 $this->errors = $supplierorderline->errors;
903 return -1;
904 }
905
906 $fk_product = 0;
907 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
908 $fk_product = $supplierorderline->fk_product;
909
910 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
911 $langs->load("errors");
912 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
913 return -1;
914 }
915 }
916
917 // Check batch is set
918 $product = new Product($this->db);
919 $product->fetch($fk_product);
920 if (isModEnabled('productbatch')) {
921 $langs->load("errors");
922 if (!empty($product->status_batch) && empty($batch)) {
923 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
924 return -1;
925 } elseif (empty($product->status_batch) && !empty($batch)) {
926 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
927 return -1;
928 }
929
930 // check sell-by / eat-by date is mandatory
931 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
932 if (!empty($errorMsgArr)) {
933 $errorMessage = '<b>' . $product->ref . '</b> : ';
934 $errorMessage .= '<ul>';
935 foreach ($errorMsgArr as $errorMsg) {
936 $errorMessage .= '<li>' . $errorMsg . '</li>';
937 }
938 $errorMessage .= '</ul>';
939 $this->error = $errorMessage;
940 return -1;
941 }
942 }
943 unset($product);
944
945 // extrafields
946 $line->array_options = $supplierorderline->array_options;
947 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
948 foreach ($array_options as $key => $value) {
949 $line->array_options[$key] = $value;
950 }
951 }
952
953 $line->fk_product = $fk_product;
954 $line->fk_commande = $supplierorderline->fk_commande;
955 $line->fk_user = $user->id;
956 $line->comment = $comment;
957 $line->batch = $batch;
958 $line->eatby = $eatby;
959 $line->sellby = $sellby;
960 $line->status = 1;
961 $line->cost_price = $cost_price;
962 $line->fk_reception = $this->id;
963
964 $this->lines[$num] = $line;
965
966 return $num;
967 }
968
969
977 public function update($user = null, $notrigger = 0)
978 {
979 global $conf;
980 $error = 0;
981
982 // Clean parameters
983
984 if (isset($this->ref)) {
985 $this->ref = trim($this->ref);
986 }
987 if (isset($this->entity)) {
988 $this->entity = (int) $this->entity;
989 }
990 if (isset($this->ref_supplier)) {
991 $this->ref_supplier = trim($this->ref_supplier);
992 }
993 if (isset($this->socid)) {
994 $this->socid = (int) trim((string) $this->socid);
995 }
996 if (isset($this->fk_user_author)) {
997 $this->fk_user_author = (int) $this->fk_user_author;
998 }
999 if (isset($this->fk_user_valid)) {
1000 $this->fk_user_valid = (int) $this->fk_user_valid;
1001 }
1002 if (isset($this->shipping_method_id)) {
1003 $this->shipping_method_id = (int) $this->shipping_method_id;
1004 }
1005 if (isset($this->tracking_number)) {
1006 $this->tracking_number = trim($this->tracking_number);
1007 }
1008 if (isset($this->statut)) {
1009 $this->statut = (int) $this->statut;
1010 }
1011 if (isset($this->trueDepth)) {
1012 $this->trueDepth = (float) trim((string) $this->trueDepth);
1013 }
1014 if (isset($this->trueWidth)) {
1015 $this->trueWidth = (float) trim((string) $this->trueWidth);
1016 }
1017 if (isset($this->trueHeight)) {
1018 $this->trueHeight = (float) trim((string) $this->trueHeight);
1019 }
1020 if (isset($this->size_units)) {
1021 $this->size_units = trim((string) $this->size_units);
1022 }
1023 if (isset($this->weight_units)) {
1024 $this->weight_units = (float) trim((string) $this->weight_units);
1025 }
1026 if (isset($this->trueWeight)) {
1027 $this->weight = (float) trim((string) $this->trueWeight);
1028 }
1029 if (isset($this->note_private)) {
1030 $this->note_private = trim($this->note_private);
1031 }
1032 if (isset($this->note_public)) {
1033 $this->note_public = trim($this->note_public);
1034 }
1035 if (isset($this->model_pdf)) {
1036 $this->model_pdf = trim($this->model_pdf);
1037 }
1038
1039
1040 // Check parameters
1041 // Put here code to add control on parameters values
1042
1043 // Update request
1044 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1045
1046 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1047 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1048 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1049 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1050 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
1051 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1052 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
1053 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1054 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1055 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
1056 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1057 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1058 $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
1059 $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
1060 $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
1061 $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
1062 $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
1063 $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
1064 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1065 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1066 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1067 $sql .= " entity = ".((int) $conf->entity);
1068 $sql .= " WHERE rowid=".((int) $this->id);
1069
1070 $this->db->begin();
1071
1072 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1073 $resql = $this->db->query($sql);
1074 if (!$resql) {
1075 $error++;
1076 $this->errors[] = "Error ".$this->db->lasterror();
1077 }
1078
1079 if (!$error) {
1080 if (!$notrigger) {
1081 // Call trigger
1082 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1083 if ($result < 0) {
1084 $error++;
1085 }
1086 // End call triggers
1087 }
1088 }
1089
1090 // Commit or rollback
1091 if ($error) {
1092 foreach ($this->errors as $errmsg) {
1093 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1094 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1095 }
1096 $this->db->rollback();
1097 return -1 * $error;
1098 } else {
1099 $this->db->commit();
1100 return 1;
1101 }
1102 }
1103
1110 public function delete(User $user)
1111 {
1112 global $conf, $langs, $user;
1113 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1114
1115 $error = 0;
1116 $this->error = '';
1117
1118
1119 $this->db->begin();
1120
1121 // Stock control
1122 if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > 0) {
1123 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1124
1125 $langs->load("agenda");
1126
1127 // Loop on each product line to add a stock movement
1128 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1129 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1130 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1131 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1132 $sql .= " AND cd.rowid = ed.fk_elementdet";
1133
1134 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1135 $resql = $this->db->query($sql);
1136 if ($resql) {
1137 $cpt = $this->db->num_rows($resql);
1138 for ($i = 0; $i < $cpt; $i++) {
1139 dol_syslog(get_class($this)."::delete movement index ".$i);
1140 $obj = $this->db->fetch_object($resql);
1141
1142 $mouvS = new MouvementStock($this->db);
1143 // we do not log origin because it will be deleted
1144 $mouvS->origin = null;
1145
1146 $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
1147 if ($result < 0) {
1148 $error++;
1149 $this->error = $mouvS->error;
1150 $this->errors = $mouvS->errors;
1151 }
1152 }
1153 } else {
1154 $error++;
1155 $this->errors[] = "Error ".$this->db->lasterror();
1156 }
1157 }
1158
1159 if (!$error) {
1160 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1161 $ef = $main."_extrafields";
1162
1163 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1164
1165 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1166 $sql .= " WHERE fk_reception = ".((int) $this->id);
1167
1168 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1169 // Delete linked object
1170 $res = $this->deleteObjectLinked();
1171 if ($res < 0) {
1172 $error++;
1173 }
1174
1175 if (!$error) {
1176 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1177 $sql .= " WHERE rowid = ".((int) $this->id);
1178
1179 if ($this->db->query($sql)) {
1180 // Call trigger
1181 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1182 if ($result < 0) {
1183 $error++;
1184 }
1185 // End call triggers
1186
1187 if (!empty($this->origin) && $this->origin_id > 0) {
1188 $this->fetch_origin();
1190 '@phan-var-force CommandeFournisseur $origin_object';
1191 if ($origin_object->statut == 4) { // If order source of reception is "partially received"
1192 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1193 $origin_object->loadReceptions();
1194 //var_dump($this->$origin->receptions);exit;
1195 if (count($origin_object->receptions) <= 0) {
1196 $origin_object->setStatut(3); // ordered
1197 }
1198 }
1199 }
1200
1201 if (!$error) {
1202 $this->db->commit();
1203
1204 // We delete PDFs
1205 $ref = dol_sanitizeFileName($this->ref);
1206 if (!empty($conf->reception->dir_output)) {
1207 $dir = $conf->reception->dir_output.'/'.$ref;
1208 $file = $dir.'/'.$ref.'.pdf';
1209 if (file_exists($file)) {
1210 if (!dol_delete_file($file)) {
1211 return 0;
1212 }
1213 }
1214 if (file_exists($dir)) {
1215 if (!dol_delete_dir_recursive($dir)) {
1216 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1217 return 0;
1218 }
1219 }
1220 }
1221
1222 return 1;
1223 } else {
1224 $this->db->rollback();
1225 return -1;
1226 }
1227 } else {
1228 $this->error = $this->db->lasterror()." - sql=$sql";
1229 $this->db->rollback();
1230 return -3;
1231 }
1232 } else {
1233 $this->error = $this->db->lasterror()." - sql=$sql";
1234 $this->db->rollback();
1235 return -2;
1236 }
1237 } else {
1238 $this->error = $this->db->lasterror()." - sql=$sql";
1239 $this->db->rollback();
1240 return -1;
1241 }
1242 } else {
1243 $this->db->rollback();
1244 return -1;
1245 }
1246 }
1247
1248 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1254 public function fetch_lines()
1255 {
1256 // phpcs:enable
1257 $this->lines = array();
1258
1259 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1260
1261 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1262 $sql .= " WHERE fk_reception = ".((int) $this->id);
1263
1264 $resql = $this->db->query($sql);
1265
1266 if (!empty($resql)) {
1267 while ($obj = $this->db->fetch_object($resql)) {
1268 $line = new CommandeFournisseurDispatch($this->db);
1269
1270 $line->fetch($obj->rowid);
1271
1272 // TODO Remove or keep this ?
1273 $line->fetch_product();
1274
1275 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1276 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1277 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1278 $sql_commfourndet .= ' ORDER BY rang';
1279
1280 $resql_commfourndet = $this->db->query($sql_commfourndet);
1281 if (!empty($resql_commfourndet)) {
1282 $obj = $this->db->fetch_object($resql_commfourndet);
1283 $line->qty_asked = $obj->qty;
1284 $line->description = $obj->description;
1285 $line->desc = $obj->description;
1286 $line->tva_tx = $obj->tva_tx;
1287 $line->vat_src_code = $obj->vat_src_code;
1288 $line->subprice = $obj->subprice;
1289 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1290 $line->remise_percent = $obj->remise_percent;
1291 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1292 $line->ref_supplier = $obj->ref;
1293 $line->total_ht = $obj->total_ht;
1294 $line->total_ttc = $obj->total_ttc;
1295 $line->total_tva = $obj->total_tva;
1296 } else {
1297 $line->qty_asked = 0;
1298 $line->description = '';
1299 $line->desc = '';
1300 $line->label = $obj->label;
1301 }
1302
1303 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1304 $tva = $pu_ht * $line->tva_tx / 100;
1305 $this->total_ht += $pu_ht;
1306 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1307
1308 $this->total_ttc += $pu_ht + $tva;
1309
1310 if (isModEnabled('productbatch') && !empty($line->batch)) {
1311 $detail_batch = new stdClass();
1312 $detail_batch->eatby = $line->eatby;
1313 $detail_batch->sellby = $line->sellby;
1314 $detail_batch->batch = $line->batch;
1315 $detail_batch->qty = $line->qty;
1316
1317 $line->detail_batch[] = $detail_batch;
1318 }
1319
1320 $this->lines[] = $line;
1321 }
1322
1323 return 1;
1324 } else {
1325 return -1;
1326 }
1327 }
1328
1339 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1340 {
1341 global $langs, $hookmanager;
1342
1343 $result = '';
1344 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1345 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1346 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1347
1348 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1349
1350 if ($short) {
1351 return $url;
1352 }
1353
1354 $linkclose = '';
1355 if (empty($notooltip)) {
1356 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1357 $label = $langs->trans("Reception");
1358 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1359 }
1360 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1361 $linkclose .= ' class="classfortooltip"';
1362 }
1363
1364 $linkstart = '<a href="'.$url.'"';
1365 $linkstart .= $linkclose.'>';
1366 $linkend = '</a>';
1367
1368 $result .= $linkstart;
1369 if ($withpicto) {
1370 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1371 }
1372 if ($withpicto != 2) {
1373 $result .= $this->ref;
1374 }
1375
1376 $result .= $linkend;
1377
1378 global $action;
1379 $hookmanager->initHooks(array($this->element . 'dao'));
1380 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1381 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1382 if ($reshook > 0) {
1383 $result = $hookmanager->resPrint;
1384 } else {
1385 $result .= $hookmanager->resPrint;
1386 }
1387 return $result;
1388 }
1389
1396 public function getLibStatut($mode = 0)
1397 {
1398 return $this->LibStatut($this->statut, $mode);
1399 }
1400
1401 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1409 public function LibStatut($status, $mode)
1410 {
1411 // phpcs:enable
1412 global $langs;
1413
1414 // List of long language codes for status
1415 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1416 $this->labelStatus[0] = 'StatusReceptionDraft';
1417 // product to receive if stock increase is on close or already received if stock increase is on validation
1418 $this->labelStatus[1] = 'StatusReceptionValidated';
1419 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1420 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1421 }
1422 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1423 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1424 }
1425 $this->labelStatus[2] = 'StatusReceptionProcessed';
1426
1427 // List of short language codes for status
1428 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1429 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1430 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1431 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1432
1433 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1434 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1435
1436 $statusType = 'status'.$status;
1437 if ($status == self::STATUS_VALIDATED) {
1438 $statusType = 'status4';
1439 }
1440 if ($status == self::STATUS_CLOSED) {
1441 $statusType = 'status6';
1442 }
1443
1444 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1445 }
1446
1454 public function getKanbanView($option = '', $arraydata = null)
1455 {
1456 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1457
1458 $return = '<div class="box-flex-item box-flex-grow-zero">';
1459 $return .= '<div class="info-box info-box-sm">';
1460 $return .= '<div class="info-box-icon bg-infobox-action">';
1461 $return .= img_picto('', 'order');
1462 $return .= '</div>';
1463 $return .= '<div class="info-box-content">';
1464 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1465 if ($selected >= 0) {
1466 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1467 }
1468 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1469 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1470 }
1471 /*if (property_exists($this, 'total_ht')) {
1472 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1473 }*/
1474 if (method_exists($this, 'getLibStatut')) {
1475 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1476 }
1477 $return .= '</div>';
1478 $return .= '</div>';
1479 $return .= '</div>';
1480
1481 return $return;
1482 }
1483
1491 public function initAsSpecimen()
1492 {
1493 global $langs;
1494
1495 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1496 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1497 $now = dol_now();
1498
1499 dol_syslog(get_class($this)."::initAsSpecimen");
1500
1501 $order = new CommandeFournisseur($this->db);
1502 $order->initAsSpecimen();
1503
1504 // Initialise parameters
1505 $this->id = 0;
1506 $this->ref = 'SPECIMEN';
1507 $this->specimen = 1;
1508 $this->statut = 1;
1509 $this->status = 1;
1510 $this->date = $now;
1511 $this->date_creation = $now;
1512 $this->date_valid = $now;
1513 $this->date_delivery = $now;
1514 $this->date_reception = $now + 24 * 3600;
1515
1516 $this->entrepot_id = 0;
1517 $this->socid = 1;
1518
1519 $this->origin_id = 1;
1520 $this->origin_type = 'supplier_order';
1521 $this->origin_object = $order;
1522
1523 $this->note_private = 'Private note';
1524 $this->note_public = 'Public note';
1525
1526 $this->tracking_number = 'TRACKID-ABC123';
1527
1528 $this->fk_incoterms = 1;
1529
1530 $nbp = 5;
1531 $xnbp = 0;
1532 while ($xnbp < $nbp) {
1533 $line = new CommandeFournisseurDispatch($this->db);
1534 $line->desc = $langs->trans("Description")." ".$xnbp;
1535 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1536 $line->label = $langs->trans("Description")." ".$xnbp;
1537 $line->qty = 10;
1538
1539 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1540
1541 $this->lines[] = $line;
1542 $xnbp++;
1543 }
1544
1545 return 1;
1546 }
1547
1555 public function setDeliveryDate($user, $delivery_date)
1556 {
1557 // phpcs:enable
1558 if ($user->hasRight('reception', 'creer')) {
1559 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1560 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1561 $sql .= " WHERE rowid = ".((int) $this->id);
1562
1563 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1564 $resql = $this->db->query($sql);
1565 if ($resql) {
1566 $this->date_delivery = $delivery_date;
1567 return 1;
1568 } else {
1569 $this->error = $this->db->error();
1570 return -1;
1571 }
1572 } else {
1573 return -2;
1574 }
1575 }
1576
1577 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583 public function fetch_delivery_methods()
1584 {
1585 // phpcs:enable
1586 global $langs;
1587 $this->meths = array();
1588
1589 $sql = "SELECT em.rowid, em.code, em.libelle";
1590 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1591 $sql .= " WHERE em.active = 1";
1592 $sql .= " ORDER BY em.libelle ASC";
1593
1594 $resql = $this->db->query($sql);
1595 if ($resql) {
1596 while ($obj = $this->db->fetch_object($resql)) {
1597 $label = $langs->trans('ReceptionMethod'.$obj->code);
1598 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1599 }
1600 }
1601 }
1602
1603 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1610 public function list_delivery_methods($id = 0)
1611 {
1612 // phpcs:enable
1613 global $langs;
1614
1615 $this->listmeths = array();
1616 $i = 0;
1617
1618 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1619 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1620 if (!empty($id)) {
1621 $sql .= " WHERE em.rowid = ".((int) $id);
1622 }
1623
1624 $resql = $this->db->query($sql);
1625 if ($resql) {
1626 while ($obj = $this->db->fetch_object($resql)) {
1627 $this->listmeths[$i]['rowid'] = $obj->rowid;
1628 $this->listmeths[$i]['code'] = $obj->code;
1629 $label = $langs->trans('ReceptionMethod'.$obj->code);
1630 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1631 $this->listmeths[$i]['description'] = $obj->description;
1632 $this->listmeths[$i]['tracking'] = $obj->tracking;
1633 $this->listmeths[$i]['active'] = $obj->active;
1634 $i++;
1635 }
1636 }
1637 }
1638
1645 public function getUrlTrackingStatus($value = '')
1646 {
1647 if (!empty($this->shipping_method_id)) {
1648 $sql = "SELECT em.code, em.tracking";
1649 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1650 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1651
1652 $resql = $this->db->query($sql);
1653 if ($resql) {
1654 if ($obj = $this->db->fetch_object($resql)) {
1655 $tracking = $obj->tracking;
1656 }
1657 }
1658 }
1659
1660 if (!empty($tracking) && !empty($value)) {
1661 $url = str_replace('{TRACKID}', $value, $tracking);
1662 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1663 } else {
1664 $this->tracking_url = $value;
1665 }
1666 }
1667
1673 public function setClosed()
1674 {
1675 global $conf, $langs, $user;
1676
1677 $error = 0;
1678
1679 // Protection. This avoid to move stock later when we should not
1680 if ($this->statut == Reception::STATUS_CLOSED) {
1681 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1682 return 0;
1683 }
1684
1685 $this->db->begin();
1686
1687 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1688 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1689
1690 $resql = $this->db->query($sql);
1691 if ($resql) {
1692 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1693 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1694 $order = new CommandeFournisseur($this->db);
1695 $order->fetch($this->origin_id);
1696
1697 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1698
1699 $receptions_match_order = 1;
1700 foreach ($order->lines as $line) {
1701 $lineid = $line->id;
1702 $qty = $line->qty;
1703 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1704 $receptions_match_order = 0;
1705 $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';
1706 dol_syslog($text);
1707 break;
1708 }
1709 }
1710 if ($receptions_match_order) {
1711 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');
1712 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1713 }
1714 }
1715
1716 $this->statut = self::STATUS_CLOSED;
1717 $this->status = self::STATUS_CLOSED;
1718
1719 // If stock increment is done on closing
1720 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1721 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1722
1723 $langs->load("agenda");
1724
1725 // Loop on each product line to add a stock movement
1726 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1727 $sql = "SELECT cd.fk_product, cd.subprice,";
1728 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1729 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1730 $sql .= " ed.cost_price";
1731 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1732 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1733 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1734 $sql .= " AND cd.rowid = ed.fk_elementdet";
1735
1736 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1737 $resql = $this->db->query($sql);
1738
1739 if ($resql) {
1740 $cpt = $this->db->num_rows($resql);
1741 for ($i = 0; $i < $cpt; $i++) {
1742 $obj = $this->db->fetch_object($resql);
1743
1744 $qty = $obj->qty;
1745
1746 if ($qty <= 0) {
1747 continue;
1748 }
1749 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1750
1751 $mouvS = new MouvementStock($this->db);
1752 $mouvS->origin = &$this;
1753 $mouvS->setOrigin($this->element, $this->id);
1754
1755 if (empty($obj->batch)) {
1756 // line without batch detail
1757
1758 // 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
1759 $inventorycode = '';
1760 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1761 if ($result < 0) {
1762 $this->error = $mouvS->error;
1763 $this->errors = $mouvS->errors;
1764 $error++;
1765 break;
1766 }
1767 } else {
1768 // line with batch detail
1769
1770 // 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
1771 $inventorycode = '';
1772 $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);
1773
1774 if ($result < 0) {
1775 $this->error = $mouvS->error;
1776 $this->errors = $mouvS->errors;
1777 $error++;
1778 break;
1779 }
1780 }
1781 }
1782 } else {
1783 $this->error = $this->db->lasterror();
1784 $error++;
1785 }
1786 }
1787
1788 // Call trigger
1789 if (!$error) {
1790 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1791 if ($result < 0) {
1792 $error++;
1793 }
1794 }
1795 } else {
1796 dol_print_error($this->db);
1797 $error++;
1798 }
1799
1800 if (!$error) {
1801 $this->db->commit();
1802 return 1;
1803 } else {
1804 $this->statut = self::STATUS_VALIDATED;
1805 $this->status = self::STATUS_VALIDATED;
1806 $this->db->rollback();
1807 return -1;
1808 }
1809 }
1810
1816 public function setBilled()
1817 {
1818 global $user;
1819 $error = 0;
1820
1821 $this->db->begin();
1822
1823 if ($this->statut == Reception::STATUS_VALIDATED) {
1824 // do not close if already closed
1825 $this->setClosed();
1826 }
1827
1828 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1829 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1830
1831 $resql = $this->db->query($sql);
1832 if ($resql) {
1833 $this->billed = 1;
1834
1835 // Call trigger
1836 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1837 if ($result < 0) {
1838 $this->billed = 0;
1839 $error++;
1840 }
1841 } else {
1842 $error++;
1843 $this->errors[] = $this->db->lasterror;
1844 }
1845
1846 if (empty($error)) {
1847 $this->db->commit();
1848 return 1;
1849 } else {
1850 $this->db->rollback();
1851 return -1;
1852 }
1853 }
1854
1860 public function reOpen()
1861 {
1862 global $conf, $langs, $user;
1863
1864 $error = 0;
1865
1866 $this->db->begin();
1867
1868 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1869 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1870
1871 $resql = $this->db->query($sql);
1872 if ($resql) {
1873 $this->statut = self::STATUS_VALIDATED;
1874 $this->status = self::STATUS_VALIDATED;
1875 $this->billed = 0;
1876
1877 // If stock increment is done on closing
1878 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1879 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1880 $numref = $this->ref;
1881 $langs->load("agenda");
1882
1883 // Loop on each product line to add a stock movement
1884 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1885 $sql = "SELECT ed.fk_product, cd.subprice,";
1886 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1887 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1888 $sql .= " ed.cost_price";
1889 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1890 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1891 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1892 $sql .= " AND cd.rowid = ed.fk_elementdet";
1893
1894 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1895 $resql = $this->db->query($sql);
1896 if ($resql) {
1897 $cpt = $this->db->num_rows($resql);
1898 for ($i = 0; $i < $cpt; $i++) {
1899 $obj = $this->db->fetch_object($resql);
1900
1901 $qty = $obj->qty;
1902
1903 if ($qty <= 0) {
1904 continue;
1905 }
1906
1907 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1908
1909 //var_dump($this->lines[$i]);
1910 $mouvS = new MouvementStock($this->db);
1911 $mouvS->origin = &$this;
1912 $mouvS->setOrigin($this->element, $this->id);
1913
1914 if (empty($obj->batch)) {
1915 // line without batch detail
1916
1917 // 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
1918 $inventorycode = '';
1919 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1920
1921 if ($result < 0) {
1922 $this->error = $mouvS->error;
1923 $this->errors = $mouvS->errors;
1924 $error++;
1925 break;
1926 }
1927 } else {
1928 // line with batch detail
1929
1930 // 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
1931 $inventorycode = '';
1932 $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);
1933 if ($result < 0) {
1934 $this->error = $mouvS->error;
1935 $this->errors = $mouvS->errors;
1936 $error++;
1937 break;
1938 }
1939 }
1940 }
1941 } else {
1942 $this->error = $this->db->lasterror();
1943 $error++;
1944 }
1945 }
1946
1947 if (!$error) {
1948 // Call trigger
1949 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1950 if ($result < 0) {
1951 $error++;
1952 }
1953 }
1954
1955 if (!$error && $this->origin == 'order_supplier') {
1956 $commande = new CommandeFournisseur($this->db);
1957 $commande->fetch($this->origin_id);
1958 $result = $commande->setStatus($user, 4);
1959 if ($result < 0) {
1960 $error++;
1961 $this->error = $commande->error;
1962 $this->errors = $commande->errors;
1963 }
1964 }
1965 } else {
1966 $error++;
1967 $this->errors[] = $this->db->lasterror();
1968 }
1969
1970 if (!$error) {
1971 $this->db->commit();
1972 return 1;
1973 } else {
1974 $this->db->rollback();
1975 return -1;
1976 }
1977 }
1978
1985 public function setDraft($user)
1986 {
1987 // phpcs:enable
1988 global $conf, $langs;
1989
1990 $error = 0;
1991
1992 // Protection
1993 if ($this->statut <= self::STATUS_DRAFT) {
1994 return 0;
1995 }
1996
1997 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1998 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1999 $this->error = 'Permission denied';
2000 return -1;
2001 }
2002
2003 $this->db->begin();
2004
2005 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2006 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2007 $sql .= " WHERE rowid = ".((int) $this->id);
2008
2009 dol_syslog(__METHOD__, LOG_DEBUG);
2010 if ($this->db->query($sql)) {
2011 // If stock increment is done on closing
2012 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2013 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2014
2015 $langs->load("agenda");
2016
2017 // Loop on each product line to add a stock movement
2018 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2019 $sql = "SELECT cd.fk_product, cd.subprice,";
2020 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2021 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2022 $sql .= " ed.cost_price";
2023 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2024 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2025 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2026 $sql .= " AND cd.rowid = ed.fk_elementdet";
2027
2028 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2029 $resql = $this->db->query($sql);
2030 if ($resql) {
2031 $cpt = $this->db->num_rows($resql);
2032 for ($i = 0; $i < $cpt; $i++) {
2033 $obj = $this->db->fetch_object($resql);
2034
2035 $qty = $obj->qty;
2036
2037
2038 if ($qty <= 0) {
2039 continue;
2040 }
2041 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2042
2043 //var_dump($this->lines[$i]);
2044 $mouvS = new MouvementStock($this->db);
2045 $mouvS->origin = &$this;
2046 $mouvS->setOrigin($this->element, $this->id);
2047
2048 if (empty($obj->batch)) {
2049 // line without batch detail
2050
2051 // 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
2052 $inventorycode = '';
2053 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2054 if ($result < 0) {
2055 $this->error = $mouvS->error;
2056 $this->errors = $mouvS->errors;
2057 $error++;
2058 break;
2059 }
2060 } else {
2061 // line with batch detail
2062
2063 // 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
2064 $inventorycode = '';
2065 $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);
2066 if ($result < 0) {
2067 $this->error = $mouvS->error;
2068 $this->errors = $mouvS->errors;
2069 $error++;
2070 break;
2071 }
2072 }
2073 }
2074 } else {
2075 $this->error = $this->db->lasterror();
2076 $error++;
2077 }
2078 }
2079
2080 if (!$error) {
2081 // Call trigger
2082 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2083 if ($result < 0) {
2084 $error++;
2085 }
2086 }
2087 if ($this->origin == 'order_supplier') {
2088 if (!empty($this->origin) && $this->origin_id > 0) {
2089 $this->fetch_origin();
2090 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2091 // Check if there is no more reception validated.
2092 $this->origin_object->fetchObjectLinked();
2093 $setStatut = 1;
2094 if (!empty($this->origin_object->linkedObjects['reception'])) {
2095 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2096 if ($rcption->statut > 0) {
2097 $setStatut = 0;
2098 break;
2099 }
2100 }
2101 //var_dump($this->$origin->receptions);exit;
2102 if ($setStatut) {
2103 $this->origin_object->setStatut(3); // ordered
2104 }
2105 }
2106 }
2107 }
2108 }
2109
2110 if (!$error) {
2111 $this->statut = self::STATUS_DRAFT;
2112 $this->status = self::STATUS_DRAFT;
2113 $this->db->commit();
2114 return 1;
2115 } else {
2116 $this->db->rollback();
2117 return -1;
2118 }
2119 } else {
2120 $this->error = $this->db->error();
2121 $this->db->rollback();
2122 return -1;
2123 }
2124 }
2125
2136 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2137 {
2138 global $conf, $langs;
2139
2140 $langs->load("receptions");
2141
2142 if (!dol_strlen($modele)) {
2143 $modele = 'squille';
2144
2145 if ($this->model_pdf) {
2146 $modele = $this->model_pdf;
2147 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2148 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2149 }
2150 }
2151
2152 $modelpath = "core/modules/reception/doc/";
2153
2154 $this->fetch_origin();
2155
2156 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2157 }
2158
2167 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2168 {
2169 $tables = array('reception');
2170
2171 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2172 }
2173
2182 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2183 {
2184 $tables = array(
2185 'receptiondet_batch'
2186 );
2187
2188 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2189 }
2190}
$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.
Class to manage line orders.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=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 clickable 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 clickable 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.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:162
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 a 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