dolibarr 24.0.0-beta
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-2025 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
16 * Copyright (C) 2025 Nick Fragoulis
17 * Copyright (C) 2026 Mathieu Moulin <mathieu@iprospective.fr>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
40require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
42require_once DOL_DOCUMENT_ROOT.'/reception/class/receptionlinebatch.class.php';
43if (isModEnabled("propal")) {
44 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
45}
46if (isModEnabled('order')) {
47 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
48}
49
50
55{
56 use CommonIncoterm;
57
62 public $TRIGGER_PREFIX = 'RECEPTION'; // to be overridden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
63
67 public $code = "";
68
72 public $element = "reception";
73
77 public $fk_element = "fk_reception";
78
82 public $table_element = "reception";
83
87 public $table_element_line = "receptiondet_batch";
88
92 public $picto = 'dollyrevert';
93
97 public $socid;
101 public $ref_supplier;
102
106 public $entrepot_id;
110 public $tracking_number;
114 public $tracking_url;
118 public $billed;
119
123 public $weight;
127 public $trueWeight;
131 public $weight_units;
135 public $trueWidth;
139 public $width_units;
143 public $trueHeight;
147 public $height_units;
151 public $trueDepth;
155 public $depth_units;
159 public $trueSize;
163 public $size_units;
167 public $user_author_id;
168
172 public $date_delivery;
173
179 public $date;
180
184 public $date_reception;
185
189 public $date_valid;
190
194 public $meths;
198 public $listmeths; // List of carriers
199
203 public $lines = array();
204
205
210 public $detail_batch;
211
212 const STATUS_CANCELED = -1;
213 const STATUS_DRAFT = 0;
214 const STATUS_VALIDATED = 1;
215 const STATUS_CLOSED = 2;
216
217
223 public function __construct($db)
224 {
225 $this->db = $db;
226
227 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
228 $this->isextrafieldmanaged = 1;
229 }
230
237 public function getNextNumRef($soc)
238 {
239 global $langs, $conf;
240 $langs->load("receptions");
241
242 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
243 $mybool = false;
244
245 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
246 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
247
248 // Include file with class
249 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
250
251 foreach ($dirmodels as $reldir) {
252 $dir = dol_buildpath($reldir."core/modules/reception/");
253
254 // Load file with numbering class (if found)
255 $mybool = ((bool) @include_once $dir.$file) || $mybool;
256 }
257
258 if (!$mybool) {
259 dol_print_error(null, "Failed to include file ".$file);
260 return '';
261 }
262
263 $obj = new $classname();
264 '@phan-var-force ModelNumRefReception $obj';
267 $numref = $obj->getNextValue($soc, $this);
268
269 if ($numref != "") {
270 return $numref;
271 } else {
272 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
273 return "";
274 }
275 } else {
276 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
277 return "";
278 }
279 }
280
288 public function create($user, $notrigger = 0)
289 {
290 global $conf;
291
292 $now = dol_now();
293
294 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
295 $error = 0;
296
297 // Clean parameters
298 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
299 if (empty($this->fk_project)) {
300 $this->fk_project = 0;
301 }
302 if (empty($this->weight_units)) {
303 $this->weight_units = 0;
304 }
305 if (empty($this->size_units)) {
306 $this->size_units = 0;
307 }
308 if (empty($this->date_creation)) {
309 $this->date_creation = $now;
310 }
311
312 $this->entity = setEntity($this);
313
314 $this->user = $user;
315
316 $this->db->begin();
317
318 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
319 $sql .= "ref";
320 $sql .= ", entity";
321 $sql .= ", ref_supplier";
322 $sql .= ", date_creation";
323 $sql .= ", fk_user_author";
324 $sql .= ", date_reception";
325 $sql .= ", date_delivery";
326 $sql .= ", fk_soc";
327 $sql .= ", fk_projet";
328 $sql .= ", fk_shipping_method";
329 $sql .= ", tracking_number";
330 $sql .= ", weight";
331 $sql .= ", size";
332 $sql .= ", width";
333 $sql .= ", height";
334 $sql .= ", weight_units";
335 $sql .= ", size_units";
336 $sql .= ", note_private";
337 $sql .= ", note_public";
338 $sql .= ", model_pdf";
339 $sql .= ", fk_incoterms, location_incoterms";
340 $sql .= ") VALUES (";
341 $sql .= "'(PROV)'";
342 $sql .= ", ".((int) $this->entity);
343 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
344 $sql .= ", '".$this->db->idate($this->date_creation)."'";
345 $sql .= ", ".((int) $user->id);
346 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
347 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
348 $sql .= ", ".($this->socid > 0 ? ((int) $this->socid) : "null");
349 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
350 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
351 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
352 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
353 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
354 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
355 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
356 $sql .= ", ".((int) $this->weight_units);
357 $sql .= ", ".((int) $this->size_units);
358 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
359 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
360 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
361 $sql .= ", ".(int) $this->fk_incoterms;
362 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
363 $sql .= ")";
364
365 dol_syslog(get_class($this)."::create", LOG_DEBUG);
366
367 $resql = $this->db->query($sql);
368
369 if ($resql) {
370 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
371
372 // update ref
373 $initialref = '(PROV'.$this->id.')';
374 if (!empty($this->ref)) {
375 $initialref = $this->ref;
376 }
377
378 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
379 $sql .= " SET ref = '".$this->db->escape($initialref)."'";
380 $sql .= " WHERE rowid = ".((int) $this->id);
381
382 dol_syslog(get_class($this)."::create", LOG_DEBUG);
383 if ($this->db->query($sql)) {
384 // Insert of lines
385 $num = count($this->lines);
386 for ($i = 0; $i < $num; $i++) {
387 $this->lines[$i]->fk_reception = $this->id;
388
389 if ($this->lines[$i]->create($user) <= 0) {
390 $this->setErrorsFromObject($this->lines[$i]);
391 $error++;
392 }
393 }
394
395 if (!$error && $this->id && $this->origin_id) {
396 $ret = $this->add_object_linked();
397 if (!$ret) {
398 $error++;
399 }
400 }
401
402 // Create extrafields
403 if (!$error) {
404 $result = $this->insertExtraFields();
405 if ($result < 0) {
406 $error++;
407 }
408 }
409
410 if (!$error && !$notrigger) {
411 // Call trigger
412 $result = $this->call_trigger('RECEPTION_CREATE', $user);
413 if ($result < 0) {
414 $error++;
415 }
416 // End call triggers
417 }
418
419 if (!$error) {
420 $this->db->commit();
421 return $this->id;
422 } else {
423 foreach ($this->errors as $errmsg) {
424 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
425 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
426 }
427 $this->db->rollback();
428 return -1 * $error;
429 }
430 } else {
431 $error++;
432 $this->error = $this->db->lasterror()." - sql=$sql";
433 $this->db->rollback();
434 return -2;
435 }
436 } else {
437 $error++;
438 $this->error = $this->db->error()." - sql=$sql";
439 $this->db->rollback();
440 return -1;
441 }
442 }
443
444 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
457 public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [], $parent_line_id = 0, $product_id = 0)
458 {
459 //phpcs:enable
460 global $user;
461
462 $receptionline = new ReceptionLineBatch($this->db);
463 $receptionline->fk_reception = $this->id;
464 $receptionline->entrepot_id = $entrepot_id;
465 $receptionline->fk_elementdet = $origin_line_id;
466 $receptionline->element_type = $this->origin;
467 $receptionline->fk_parent = $parent_line_id;
468 $receptionline->fk_product = $product_id;
469 $receptionline->qty = $qty;
470 $receptionline->rang = $rang;
471 $receptionline->array_options = $array_options;
472
473 if (!($receptionline->fk_product > 0)) {
474 $order_line = new CommandeFournisseurLigne($this->db);
475 $order_line->fetch($receptionline->fk_elementdet);
476 $receptionline->fk_product = $order_line->fk_product;
477 }
478
479 if (($lineId = $receptionline->insert($user)) < 0) {
480 $this->errors[] = $receptionline->error;
481 }
482 return $lineId;
483 }
484
493 public function fetch($id, $ref = '', $ref_ext = '')
494 {
495 // Check parameters
496 if (empty($id) && empty($ref) && empty($ref_ext)) {
497 return -1;
498 }
499
500 $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.fk_projet as fk_project, e.billed";
501 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
502 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery, e.date_valid";
503 $sql .= ", e.fk_shipping_method, e.tracking_number";
504 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
505 $sql .= ", e.note_private, e.note_public";
506 $sql .= ', e.fk_incoterms, e.location_incoterms';
507 $sql .= ', i.libelle as label_incoterms';
508 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
509 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."' AND el.sourcetype = 'order_supplier'";
510 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
511
512 if ($id) {
513 $sql .= " WHERE e.rowid = ".((int) $id);
514 } else {
515 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
516 if ($ref) {
517 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
518 } elseif ($ref_ext) {
519 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
520 }
521 }
522
523 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
524 $result = $this->db->query($sql);
525 if ($result) {
526 if ($this->db->num_rows($result)) {
527 $obj = $this->db->fetch_object($result);
528
529 $this->id = $obj->rowid;
530 $this->entity = $obj->entity;
531 $this->ref = $obj->ref;
532 $this->socid = $obj->socid;
533 $this->ref_supplier = $obj->ref_supplier;
534 $this->ref_ext = $obj->ref_ext;
535 $this->statut = $obj->status;
536 $this->status = $obj->status;
537 $this->billed = $obj->billed;
538 $this->fk_project = $obj->fk_project;
539 $this->user_author_id = $obj->fk_user_author;
540 $this->date_creation = $this->db->jdate($obj->date_creation);
541 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
542 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
543 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
544 $this->date_valid = $this->db->jdate($obj->date_valid); // Date validation
545 $this->model_pdf = $obj->model_pdf;
546 $this->shipping_method_id = $obj->fk_shipping_method;
547 $this->tracking_number = $obj->tracking_number;
548 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
549 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
550 $this->origin_id = $obj->origin_id;
551
552 $this->trueWeight = $obj->weight;
553 $this->weight_units = $obj->weight_units;
554
555 $this->trueWidth = $obj->width;
556 $this->width_units = $obj->size_units;
557 $this->trueHeight = $obj->height;
558 $this->height_units = $obj->size_units;
559 $this->trueDepth = $obj->size;
560 $this->depth_units = $obj->size_units;
561
562 $this->note_public = $obj->note_public;
563 $this->note_private = $obj->note_private;
564
565 // A denormalized value
566 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
567 $this->size_units = $obj->size_units;
568
569 //Incoterms
570 $this->fk_incoterms = $obj->fk_incoterms;
571 $this->location_incoterms = $obj->location_incoterms;
572 $this->label_incoterms = $obj->label_incoterms;
573
574 $this->db->free($result);
575
576 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
577 //$this->pdf_filename = $file;
578
579 // Tracking url
580 $this->getUrlTrackingStatus($obj->tracking_number);
581
582 /*
583 * Thirdparty
584 */
585 $result = $this->fetch_thirdparty();
586
587
588 // Retrieve all extrafields for reception
589 // fetch optionals attributes and labels
590 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
591 $extrafields = new ExtraFields($this->db);
592 $extrafields->fetch_name_optionals_label($this->table_element, true);
593 $this->fetch_optionals();
594
595 /*
596 * Lines
597 */
598 if (empty($obj->origin_id)) {
599 $result = $this->fetch_lines_free();
600 } else {
601 $result = $this->fetch_lines();
602 }
603 if ($result < 0) {
604 return -3;
605 }
606
607 return 1;
608 } else {
609 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
610 $this->error = 'Reception with id '.$id.' not found';
611 return 0;
612 }
613 } else {
614 $this->error = $this->db->error();
615 return -1;
616 }
617 }
618
626 public function valid($user, $notrigger = 0)
627 {
628 global $conf, $langs;
629
630 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
631
632 dol_syslog(get_class($this)."::valid");
633
634 // Protection
635 if ($this->statut) {
636 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
637 return 0;
638 }
639
640 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
641 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
642 $this->error = 'Permission denied';
643 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
644 return -1;
645 }
646
647 $this->db->begin();
648
649 $error = 0;
650
651 // Define new ref
652 $soc = new Societe($this->db);
653 $soc->fetch($this->socid);
654
655
656 // Define new ref
657 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
658 $numref = $this->getNextNumRef($soc);
659 } else {
660 $numref = (string) $this->ref;
661 }
662
663 $this->newref = dol_sanitizeFileName($numref);
664
665 $now = dol_now();
666
667 // Validate
668 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
669 $sql .= " ref='".$this->db->escape($numref)."'";
670 $sql .= ", fk_statut = 1";
671 $sql .= ", date_valid = '".$this->db->idate($now)."'";
672 $sql .= ", fk_user_valid = ".((int) $user->id);
673 $sql .= " WHERE rowid = ".((int) $this->id);
674 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
675 $resql = $this->db->query($sql);
676 if (!$resql) {
677 $this->error = $this->db->lasterror();
678 $error++;
679 }
680
681 // If stock increment is done on reception (recommended choice)
682 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
683 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
684
685 $langs->load("agenda");
686
687 // Loop on each product line to add a stock movement
688 // TODO in future, reception lines may not be linked to order line
689 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
690 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
691 $sql .= " ed.eatby, ed.sellby, ed.batch,";
692 $sql .= " ed.fk_elementdet, ed.cost_price";
693 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
694 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
695 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
696 $sql .= " AND cd.rowid = ed.fk_elementdet";
697
698 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
699 $resql = $this->db->query($sql);
700 if ($resql) {
701 $cpt = $this->db->num_rows($resql);
702 for ($i = 0; $i < $cpt; $i++) {
703 $obj = $this->db->fetch_object($resql);
704
705 $qty = $obj->qty;
706
707 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
708 continue;
709 }
710
711 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
712
713 //var_dump($this->lines[$i]);
714 $mouvS = new MouvementStock($this->db);
715 $mouvS->origin = &$this;
716 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
717
718 if (empty($obj->batch)) {
719 // line without batch detail
720
721 // 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.
722 $inventorycode = '';
723 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
724
725 if (intval($result) < 0) {
726 $error++;
727 $this->setErrorsFromObject($mouvS);
728 break;
729 }
730 } else {
731 // line with batch detail
732
733 // 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.
734 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
735 $inventorycode = '';
736 $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);
737
738 if (intval($result) < 0) {
739 $error++;
740 $this->setErrorsFromObject($mouvS);
741 break;
742 }
743 }
744 }
745 } else {
746 $this->db->rollback();
747 $this->error = $this->db->error();
748 return -2;
749 }
750 }
751
752 if (!$error && $this->origin_id > 0) {
753 // Change status of purchase order to "reception in process" or "totally received"
754 $status = $this->getStatusDispatch();
755 if ($status < 0) {
756 $error++;
757 } else {
758 $trigger_key = '';
759 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
760 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
761 if ($ret < 0) {
762 $error++;
763 $this->errors = array_merge($this->errors, $this->origin_object->errors);
764 }
765 } else {
766 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
767 if ($ret < 0) {
768 $error++;
769 }
770 }
771 }
772 }
773
774 if (!$error && !$notrigger) {
775 // Call trigger
776 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
777 if ($result < 0) {
778 $error++;
779 }
780 // End call triggers
781 }
782
783 if (!$error) {
784 $this->oldref = $this->ref;
785
786 // Rename directory if dir was a temporary ref
787 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
788 // Now we rename also files into index
789 $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)."'";
790 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
791 $resql = $this->db->query($sql);
792 if (!$resql) {
793 $error++;
794 $this->error = $this->db->lasterror();
795 }
796 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
797 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
798 $resql = $this->db->query($sql);
799 if (!$resql) {
800 $error++;
801 $this->error = $this->db->lasterror();
802 }
803
804 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
805 $oldref = dol_sanitizeFileName($this->ref);
806 $newref = dol_sanitizeFileName($numref);
807 $dirsource = $conf->reception->dir_output.'/'.$oldref;
808 $dirdest = $conf->reception->dir_output.'/'.$newref;
809 if (!$error && file_exists($dirsource)) {
810 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
811
812 if (@rename($dirsource, $dirdest)) {
813 dol_syslog("Rename ok");
814 // Rename docs starting with $oldref with $newref
815 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
816 foreach ($listoffiles as $fileentry) {
817 $dirsource = $fileentry['name'];
818 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
819 $dirsource = $fileentry['path'].'/'.$dirsource;
820 $dirdest = $fileentry['path'].'/'.$dirdest;
821 @rename($dirsource, $dirdest);
822 }
823 }
824 }
825 }
826 }
827
828 // Set new ref and current status
829 if (!$error) {
830 $this->ref = $numref;
831 $this->statut = self::STATUS_VALIDATED;
832 $this->status = self::STATUS_VALIDATED;
833 }
834
835 if (!$error) {
836 $this->db->commit();
837 return 1;
838 } else {
839 foreach ($this->errors as $errmsg) {
840 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
841 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
842 }
843 $this->db->rollback();
844 return -1 * $error;
845 }
846 }
847
853 public function getStatusDispatch()
854 {
855 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
856 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
857
859
860 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
861 if (empty($this->origin_object)) {
862 $this->fetch_origin();
863 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
864 $res = $this->origin_object->fetch_lines();
865 $this->commandeFournisseur = null; // deprecated
866 if ($res < 0) {
867 return $res;
868 }
869 } elseif ($this->origin_object instanceof CommandeFournisseur && empty($this->origin_object->lines)) {
870 $res = $this->origin_object->fetch_lines();
871 $this->commandeFournisseur = $this->origin_object; // deprecated
872 if ($res < 0) {
873 return $res;
874 }
875 }
876 }
877
878 $qty_received = array();
879 $qty_wished = array();
880
881 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
882 $filter = array('t.fk_element' => $this->origin_id);
883 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
884 $filter['t.status'] = 1; // Restrict to lines with status validated
885 }
886
887 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
888 if ($ret < 0) {
889 $this->setErrorsFromObject($supplierorderdispatch);
890 return $ret;
891 } else {
892 // build array with quantity received by product in all supplier orders (origin)
893 foreach ($supplierorderdispatch->lines as $dispatch_line) {
894 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
895 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
896 } else {
897 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
898 }
899 }
900
901 // qty wished in origin (purchase order, ...)
902 foreach ($this->origin_object->lines as $origin_line) {
903 // exclude lines not qualified for reception
904 if (($origin_line->product_type > 0 && (!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') || empty($origin_line->stockable_product))) || $origin_line->product_type > 1) {
905 continue;
906 }
907 if (array_key_exists($origin_line->fk_product, $qty_wished)) {
908 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
909 } else {
910 $qty_wished[$origin_line->fk_product] = $origin_line->qty;
911 }
912 }
913
914 // compare array
915 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
916 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
917 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
918
919 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
921 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
922 // set totally received if more products received than ordered
923 $close = 0;
924
925 if (count($diff_array) > 0) {
926 // there are some difference between the two arrays
927 // scan the array of results
928 foreach ($diff_array as $key => $value) {
929 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
930 if ($qty_received[$key] >= $qty_wished[$key]) {
931 $close++;
932 }
933 }
934 }
935
936 if ($close == count($diff_array)) {
937 // all the products are received equal or more than the ordered quantity
939 }
940 }
941 }
942 }
943
944 return $status;
945 }
946
963 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
964 {
965 global $conf, $langs, $user;
966
967 $num = count($this->lines);
968 $line = new CommandeFournisseurDispatch($this->db);
969
970 $line->fk_entrepot = $entrepot_id;
971 $line->fk_commandefourndet = $id;
972 $line->qty = $qty;
973
974 $supplierorderline = new CommandeFournisseurLigne($this->db);
975 $result = $supplierorderline->fetch($id);
976 if ($result <= 0) {
977 $this->setErrorsFromObject($supplierorderline);
978 return -1;
979 }
980
981 $fk_product = 0;
982 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
983 $fk_product = $supplierorderline->fk_product;
984
985 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
986 $langs->load("errors");
987 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
988 return -1;
989 }
990 }
991
992 // Check batch is set
993 $product = new Product($this->db);
994 $product->fetch($fk_product);
995 if (isModEnabled('productbatch')) {
996 $langs->load("errors");
997 if (!empty($product->status_batch) && empty($batch)) {
998 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
999 return -1;
1000 } elseif (empty($product->status_batch) && !empty($batch)) {
1001 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
1002 return -1;
1003 }
1004
1005 // check sell-by / eat-by date is mandatory
1006 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
1007 if (!empty($errorMsgArr)) {
1008 $errorMessage = '<b>' . $product->ref . '</b> : ';
1009 $errorMessage .= '<ul>';
1010 foreach ($errorMsgArr as $errorMsg) {
1011 $errorMessage .= '<li>' . $errorMsg . '</li>';
1012 }
1013 $errorMessage .= '</ul>';
1014 $this->error = $errorMessage;
1015 return -1;
1016 }
1017 }
1018 unset($product);
1019
1020 // extrafields
1021 $line->array_options = $supplierorderline->array_options;
1022 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
1023 foreach ($array_options as $key => $value) {
1024 $line->array_options[$key] = $value;
1025 }
1026 }
1027
1028 $line->fk_product = $fk_product;
1029 $line->fk_commande = $supplierorderline->fk_commande;
1030 $line->fk_user = $user->id;
1031 $line->comment = $comment;
1032 $line->batch = $batch;
1033 $line->eatby = $eatby;
1034 $line->sellby = $sellby;
1035 $line->status = 1;
1036 $line->cost_price = $cost_price;
1037 $line->fk_reception = $this->id;
1038
1039 $this->lines[$num] = $line;
1040
1041 return $num;
1042 }
1043
1056 public function addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options = [])
1057 {
1058 global $mysoc, $langs, $user;
1059
1060 if ($this->status == self::STATUS_DRAFT) {
1061 if (empty($rang)) {
1062 $rang = 0;
1063 }
1064
1065 $qty = (float) price2num($qty);
1066
1067 $this->db->begin();
1068
1069 // Rank to use
1070 $ranktouse = $this->rang;
1071 if ($ranktouse == -1) {
1072 $rangmax = $this->line_max($this->fk_reception);
1073 $ranktouse = $rangmax + 1;
1074 }
1075
1076 // Insert line
1077 $this->line = new ReceptionLineBatch($this->db);
1078 $this->line->fk_reception = $this->id;
1079 $this->line->element_type = $element_type;
1080 $this->line->fk_product = $fk_product;
1081 $this->line->description = $description;
1082
1083 $this->line->qty = (float) $qty;
1084 $this->line->fk_unit = $fk_unit;
1085 $this->line->rang = $ranktouse;
1086
1087 if (is_array($array_options) && count($array_options) > 0) {
1088 $this->line->array_options = $array_options;
1089 }
1090
1091 $result = $this->line->insert($user);
1092 if ($result > 0) {
1093 if (!isset($this->context['createfromclone'])) {
1094 if ($this->fk_reception) {
1095 $this->line_order(true, 'DESC');
1096 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1097 $linecount = count($this->lines);
1098 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1099 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1100 }
1101 }
1102 $this->lines[] = $this->line;
1103 }
1104
1105 $this->db->commit();
1106 return $this->line->id;
1107 } else {
1108 $this->error = $this->line->error;
1109 dol_syslog(get_class($this)."::addlinefree error=".$this->error, LOG_ERR);
1110 $this->db->rollback();
1111 return -2;
1112 }
1113 } else {
1114 dol_syslog(get_class($this)."::addlinefree status of reception must be Draft to allow use of ->addlinefree()", LOG_ERR);
1115 return -3;
1116 }
1117 }
1118
1133 public function updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options = array())
1134 {
1135 global $mysoc, $langs, $user;
1136
1137 if ($this->statut == self::STATUS_DRAFT) {
1138 $this->db->begin();
1139
1140 if (empty($qty)) {
1141 $qty = 0;
1142 }
1143 if (empty($rang)) {
1144 $rang = 0;
1145 }
1146
1147 $qty = (float) $qty;
1148 $description = trim($description);
1149
1150 // Fetch current line from the database and then clone the object and set it in $oldline property
1151 $line = new ReceptionLineBatch($this->db);
1152
1153 $line->fetch($rowid);
1154 $line->fetch_optionals();
1155
1156 if (!empty($line->fk_product)) {
1157 $product = new Product($this->db);
1158 $result = $product->fetch($line->fk_product);
1159 $product_type = $product->type;
1160 }
1161
1162 $staticline = clone $line;
1163
1164 $line->oldline = $staticline;
1165 $this->line = $line;
1166 $this->line->context = $this->context;
1167 $this->line->rang = $rang;
1168 $this->line->fk_reception = $this->id;
1169 $this->line->element_type = $element_type;
1170 $this->line->fk_product = $line->fk_product;
1171 $this->line->qty = $qty;
1172 $this->line->fk_unit = $fk_unit;
1173 $this->line->description = $description;
1174
1175 if (is_array($array_options) && count($array_options) > 0) {
1176 // We replace values in this->line->array_options only for entries defined into $array_options
1177 foreach ($array_options as $key => $value) {
1178 $this->line->array_options[$key] = $array_options[$key];
1179 }
1180 }
1181
1182
1183 $result = $this->line->update($user, $notrigger);
1184 if ($result > 0) {
1185 // Reorder if child line
1186
1187
1188 $this->db->commit();
1189 return $result;
1190 } else {
1191 $this->error = $this->line->error;
1192
1193 $this->db->rollback();
1194 return -1;
1195 }
1196 } else {
1197 $this->error = get_class($this)."::updatelinefree reception status makes operation forbidden";
1198 $this->errors = array('ReceptionStatusMakeOperationForbidden');
1199 return -2;
1200 }
1201 }
1202
1203 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1209 public function fetch_lines_free()
1210 {
1211 // phpcs:enable
1212 global $mysoc;
1213
1214 $this->lines = array();
1215
1216 $sql = 'SELECT rc.rowid, rc.fk_reception, rc.fk_entrepot, rc.fk_product, rc.fk_unit, rc.description, rc.fk_elementdet, rc.fk_element, rc.element_type, rc.qty, rc.rang';
1217 $sql .= ' FROM '.MAIN_DB_PREFIX.'receptiondet_batch as rc';
1218 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = rc.fk_product)';
1219 $sql .= ' WHERE rc.fk_reception = '.((int) $this->id);
1220
1221 $sql .= ' ORDER BY rc.rang, rc.rowid';
1222
1223 dol_syslog(get_class($this)."::fetch_lines_free", LOG_DEBUG);
1224 $result = $this->db->query($sql);
1225 if ($result) {
1226 $num = $this->db->num_rows($result);
1227
1228 $i = 0;
1229 while ($i < $num) {
1230 $objp = $this->db->fetch_object($result);
1231
1232 $line = new ReceptionLineBatch($this->db);
1233
1234 $line->rowid = $objp->rowid;
1235 $line->id = $objp->rowid;
1236 $line->fk_reception = $this->id;
1237
1238 $line->description = $objp->description;
1239 $line->qty = $objp->qty;
1240 $line->fk_entrepot = $objp->fk_entrepot;
1241 $line->fk_product = $objp->fk_product;
1242
1243 $line->rang = $objp->rang;
1244
1245
1246 $line->fk_element = $objp->fk_element;
1247 $line->fk_unit = $objp->fk_unit;
1248 $line->fk_elementdet = $objp->fk_elementdet;
1249 $line->fk_element_type = $objp->element_type;
1250 $line->fetch_optionals();
1251
1252
1253
1254 $this->lines[$i] = $line;
1255
1256 $i++;
1257 }
1258
1259 $this->db->free($result);
1260
1261 return 1;
1262 } else {
1263 $this->error = $this->db->error();
1264 return -3;
1265 }
1266 }
1267
1273 public function getLinesArray()
1274 {
1275 return $this->fetch_lines_free();
1276 }
1277
1285 public function update($user = null, $notrigger = 0)
1286 {
1287 global $conf;
1288 $error = 0;
1289
1290 // Clean parameters
1291
1292 if (isset($this->ref)) {
1293 $this->ref = trim($this->ref);
1294 }
1295 if (isset($this->entity)) {
1296 $this->entity = (int) $this->entity;
1297 }
1298 if (isset($this->ref_supplier)) {
1299 $this->ref_supplier = trim($this->ref_supplier);
1300 }
1301 if (isset($this->socid)) {
1302 $this->socid = (int) trim((string) $this->socid);
1303 }
1304 if (isset($this->fk_user_author)) {
1305 $this->fk_user_author = (int) $this->fk_user_author;
1306 }
1307 if (isset($this->fk_user_valid)) {
1308 $this->fk_user_valid = (int) $this->fk_user_valid;
1309 }
1310 if (isset($this->shipping_method_id)) {
1311 $this->shipping_method_id = (int) $this->shipping_method_id;
1312 }
1313 if (isset($this->tracking_number)) {
1314 $this->tracking_number = trim($this->tracking_number);
1315 }
1316 if (isset($this->statut)) {
1317 $this->statut = (int) $this->statut;
1318 }
1319 if (isset($this->trueDepth)) {
1320 $this->trueDepth = price2num($this->trueDepth);
1321 }
1322 if (isset($this->trueWidth)) {
1323 $this->trueWidth = price2num($this->trueWidth);
1324 }
1325 if (isset($this->trueHeight)) {
1326 $this->trueHeight = price2num($this->trueHeight);
1327 }
1328 $this->size_units = (int) $this->size_units;
1329
1330 if (isset($this->trueWeight)) {
1331 $this->weight = price2num($this->trueWeight);
1332 }
1333 $this->weight_units = (int) $this->weight_units;
1334
1335 if (isset($this->note_private)) {
1336 $this->note_private = trim($this->note_private);
1337 }
1338 if (isset($this->note_public)) {
1339 $this->note_public = trim($this->note_public);
1340 }
1341 if (isset($this->model_pdf)) {
1342 $this->model_pdf = trim($this->model_pdf);
1343 }
1344
1345
1346 // Check parameters
1347 // Put here code to add control on parameters values
1348
1349 // Update request
1350 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1351
1352 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1353 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1354 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1355 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1356 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
1357 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1358 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
1359 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1360 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1361 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
1362 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1363 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1364 $sql .= " height=".(($this->trueHeight != '') ? (float) $this->trueHeight : "null").",";
1365 $sql .= " width=".(($this->trueWidth != '') ? (float) $this->trueWidth : "null").",";
1366 $sql .= " size_units=".((int) $this->size_units).",";
1367 $sql .= " size=".(($this->trueDepth != '') ? (float) $this->trueDepth : "null").",";
1368 $sql .= " weight_units=".((int) $this->weight_units).",";
1369 $sql .= " weight=".(($this->trueWeight != '') ? (float) $this->trueWeight : "null").",";
1370 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1371 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1372 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1373 $sql .= " fk_projet=".((isset($this->fk_project) && $this->fk_project > 0) ? ((int) $this->fk_project) : "null").",";
1374 $sql .= " entity = ".((int) $conf->entity);
1375 $sql .= " WHERE rowid=".((int) $this->id);
1376
1377 $this->db->begin();
1378
1379 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1380 $resql = $this->db->query($sql);
1381 if (!$resql) {
1382 $error++;
1383 $this->errors[] = "Error ".$this->db->lasterror();
1384 }
1385
1386 if (!$error) {
1387 if (!$notrigger) {
1388 // Call trigger
1389 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1390 if ($result < 0) {
1391 $error++;
1392 }
1393 // End call triggers
1394 }
1395 }
1396
1397 // Commit or rollback
1398 if ($error) {
1399 foreach ($this->errors as $errmsg) {
1400 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1401 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1402 }
1403 $this->db->rollback();
1404 return -1 * $error;
1405 } else {
1406 $this->db->commit();
1407 return 1;
1408 }
1409 }
1410
1417 public function delete(User $user)
1418 {
1419 global $conf, $langs, $user;
1420 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1421
1422 $error = 0;
1423 $this->error = '';
1424
1425
1426 $this->db->begin();
1427
1428 // Stock control
1429 if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT)
1430 || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED))
1431 ) {
1432 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1433
1434 $langs->load("agenda");
1435
1436 // Loop on each product line to add a stock movement
1437 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1438 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1439 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1440 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1441 $sql .= " AND cd.rowid = ed.fk_elementdet";
1442
1443 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1444 $resql = $this->db->query($sql);
1445 if ($resql) {
1446 $cpt = $this->db->num_rows($resql);
1447 for ($i = 0; $i < $cpt; $i++) {
1448 dol_syslog(get_class($this)."::delete movement index ".$i);
1449 $obj = $this->db->fetch_object($resql);
1450
1451 $mouvS = new MouvementStock($this->db);
1452 // we do not log origin because it will be deleted
1453 $mouvS->origin = null;
1454
1455 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ReceptionDeletedInDolibarr", $this->ref), '', $obj->eatby ? $this->db->jdate($obj->eatby) : null, $obj->sellby ? $this->db->jdate($obj->sellby) : null, $obj->batch); // Price is set to 0, because we don't want to see WAP changed
1456 if ($result < 0) {
1457 $error++;
1458 $this->error = $mouvS->error;
1459 $this->errors = $mouvS->errors;
1460 }
1461 }
1462 } else {
1463 $error++;
1464 $this->errors[] = "Error ".$this->db->lasterror();
1465 }
1466 }
1467
1468 if (!$error) {
1469 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1470 $ef = $main."_extrafields";
1471
1472 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1473
1474 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1475 $sql .= " WHERE fk_reception = ".((int) $this->id);
1476
1477 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1478 // Delete linked object
1479 $res = $this->deleteObjectLinked();
1480 if ($res < 0) {
1481 $error++;
1482 }
1483
1484 if (!$error) {
1485 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1486 $sql .= " WHERE rowid = ".((int) $this->id);
1487
1488 if ($this->db->query($sql)) {
1489 // Call trigger
1490 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1491 if ($result < 0) {
1492 $error++;
1493 }
1494 // End call triggers
1495
1496 if (!empty($this->origin) && $this->origin_id > 0) {
1497 $this->fetch_origin();
1499 '@phan-var-force CommandeFournisseur $origin_object';
1501 if ($origin_object->status == 4) { // If order source of reception is "partially received"
1502 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1503 $origin_object->loadReceptions();
1504 //var_dump($this->$origin->receptions);exit;
1505 if (count($origin_object->receptions) <= 0) {
1506 $origin_object->setStatut(3); // CommandeFournisseur ordered
1507 }
1508 }
1509 }
1510
1511 if (!$error) {
1512 $this->db->commit();
1513
1514 // We delete PDFs
1515 $ref = dol_sanitizeFileName($this->ref);
1516 if (!empty($conf->reception->dir_output)) {
1517 $dir = $conf->reception->dir_output.'/'.$ref;
1518 $file = $dir.'/'.$ref.'.pdf';
1519 if (file_exists($file)) {
1520 if (!dol_delete_file($file)) {
1521 return 0;
1522 }
1523 }
1524 if (file_exists($dir)) {
1525 if (!dol_delete_dir_recursive($dir)) {
1526 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1527 return 0;
1528 }
1529 }
1530 }
1531
1532 return 1;
1533 } else {
1534 $this->db->rollback();
1535 return -1;
1536 }
1537 } else {
1538 $this->error = $this->db->lasterror()." - sql=$sql";
1539 $this->db->rollback();
1540 return -3;
1541 }
1542 } else {
1543 $this->error = $this->db->lasterror()." - sql=$sql";
1544 $this->db->rollback();
1545 return -2;
1546 }
1547 } else {
1548 $this->error = $this->db->lasterror()." - sql=$sql";
1549 $this->db->rollback();
1550 return -1;
1551 }
1552 } else {
1553 $this->db->rollback();
1554 return -1;
1555 }
1556 }
1557
1558 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1564 public function fetch_lines()
1565 {
1566 // phpcs:enable
1567 $this->lines = array();
1568
1569 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1570
1571 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1572 $sql .= " WHERE fk_reception = ".((int) $this->id);
1573
1574 $resql = $this->db->query($sql);
1575
1576 if (!empty($resql)) {
1577 while ($obj = $this->db->fetch_object($resql)) {
1578 $line = new CommandeFournisseurDispatch($this->db);
1579
1580 $line->fetch($obj->rowid);
1581
1582 // TODO Remove or keep this ?
1583 $line->fetch_product();
1584
1585 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1586 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1587 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1588 $sql_commfourndet .= ' ORDER BY rang';
1589
1590 $resql_commfourndet = $this->db->query($sql_commfourndet);
1591 if (!empty($resql_commfourndet)) {
1592 $obj = $this->db->fetch_object($resql_commfourndet);
1593 $line->qty_asked = $obj->qty;
1594 $line->description = $obj->description;
1595 $line->desc = $obj->description;
1596 $line->tva_tx = $obj->tva_tx;
1597 $line->vat_src_code = $obj->vat_src_code;
1598 $line->subprice = $obj->subprice;
1599 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1600 $line->remise_percent = $obj->remise_percent;
1601 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1602 $line->ref_supplier = $obj->ref;
1603 $line->total_ht = $obj->total_ht;
1604 $line->total_ttc = $obj->total_ttc;
1605 $line->total_tva = $obj->total_tva;
1606 } else {
1607 $line->qty_asked = 0;
1608 $line->description = '';
1609 $line->desc = '';
1610 $line->label = $obj->label;
1611 }
1612
1613 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1614 $tva = $pu_ht * $line->tva_tx / 100;
1615 $this->total_ht += $pu_ht;
1616 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1617
1618 $this->total_ttc += $pu_ht + $tva;
1619
1620 if (isModEnabled('productbatch') && !empty($line->batch)) {
1621 $detail_batch = new stdClass();
1622 $detail_batch->eatby = $line->eatby;
1623 $detail_batch->sellby = $line->sellby;
1624 $detail_batch->batch = $line->batch;
1625 $detail_batch->qty = $line->qty;
1626
1627 $line->detail_batch[] = $detail_batch;
1628 }
1629
1630 $this->lines[] = $line;
1631 }
1632
1633 return 1;
1634 } else {
1635 return -1;
1636 }
1637 }
1638
1649 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0)
1650 {
1651 global $langs, $hookmanager;
1652
1653 $result = '';
1654 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1655 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1656 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1657
1658 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1659
1660 if ($short) {
1661 return $url;
1662 }
1663
1664 $linkclose = '';
1665 if (empty($notooltip)) {
1666 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1667 $label = $langs->trans("Reception");
1668 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1669 }
1670 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1671 $linkclose .= ' class="classfortooltip"';
1672 }
1673
1674 $linkstart = '<a href="'.$url.'"';
1675 $linkstart .= $linkclose.'>';
1676 $linkend = '</a>';
1677
1678 $result .= $linkstart;
1679 if ($withpicto) {
1680 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1681 }
1682 if ($withpicto != 2) {
1683 $result .= $this->ref;
1684 }
1685
1686 $result .= $linkend;
1687
1688 global $action;
1689 $hookmanager->initHooks(array($this->element . 'dao'));
1690 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1691 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1692 if ($reshook > 0) {
1693 $result = $hookmanager->resPrint;
1694 } else {
1695 $result .= $hookmanager->resPrint;
1696 }
1697 return $result;
1698 }
1699
1706 public function getLibStatut($mode = 0)
1707 {
1708 return $this->LibStatut($this->statut, $mode);
1709 }
1710
1711 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1719 public function LibStatut($status, $mode)
1720 {
1721 // phpcs:enable
1722 global $langs;
1723
1724 // List of long language codes for status
1725 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1726 $this->labelStatus[0] = 'StatusReceptionDraft';
1727 // product to receive if stock increase is on close or already received if stock increase is on validation
1728 $this->labelStatus[1] = 'StatusReceptionValidated';
1729 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1730 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1731 }
1732 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1733 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1734 }
1735 $this->labelStatus[2] = 'StatusReceptionProcessed';
1736
1737 // List of short language codes for status
1738 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1739 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1740 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1741 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1742
1743 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1744 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1745
1746 $statusType = 'status'.$status;
1747 if ($status == self::STATUS_VALIDATED) {
1748 $statusType = 'status4';
1749 }
1750 if ($status == self::STATUS_CLOSED) {
1751 $statusType = 'status6';
1752 }
1753
1754 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1755 }
1756
1764 public function getKanbanView($option = '', $arraydata = null)
1765 {
1766 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1767
1768 $return = '<div class="box-flex-item box-flex-grow-zero">';
1769 $return .= '<div class="info-box info-box-sm">';
1770 $return .= '<div class="info-box-icon bg-infobox-action">';
1771 $return .= img_picto('', 'order');
1772 $return .= '</div>';
1773 $return .= '<div class="info-box-content">';
1774 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1775 if ($selected >= 0) {
1776 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1777 }
1778 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1779 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1780 }
1781 /*if (property_exists($this, 'total_ht')) {
1782 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency()).' '.$langs->trans('HT').'</div>';
1783 }*/
1784 if (method_exists($this, 'getLibStatut')) {
1785 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1786 }
1787 $return .= '</div>';
1788 $return .= '</div>';
1789 $return .= '</div>';
1790
1791 return $return;
1792 }
1793
1801 public function initAsSpecimen()
1802 {
1803 global $langs;
1804
1805 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1806 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1807 $now = dol_now();
1808
1809 dol_syslog(get_class($this)."::initAsSpecimen");
1810
1811 $order = new CommandeFournisseur($this->db);
1812 $order->initAsSpecimen();
1813
1814 // Initialise parameters
1815 $this->id = 0;
1816 $this->ref = 'SPECIMEN';
1817 $this->specimen = 1;
1818 $this->statut = 1;
1819 $this->status = 1;
1820 $this->date = $now;
1821 $this->date_creation = $now;
1822 $this->date_valid = $now;
1823 $this->date_delivery = $now;
1824 $this->date_reception = $now + 24 * 3600;
1825 $this->entrepot_id = 0;
1826 $this->socid = 1;
1827 $this->origin_id = 1;
1828 $this->origin_type = 'supplier_order';
1829 $this->origin_object = $order;
1830 $this->note_private = 'Private note';
1831 $this->note_public = 'Public note';
1832 $this->tracking_number = 'TRACKID-ABC123';
1833 $this->fk_incoterms = 1;
1834 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
1835 $xnbp = 0;
1836 while ($xnbp < $nbp) {
1837 $line = new CommandeFournisseurDispatch($this->db);
1838 $line->desc = $langs->trans("Description")." ".$xnbp;
1839 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1840 $line->label = $langs->trans("Description")." ".$xnbp;
1841 $line->qty = 10;
1842
1843 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1844
1845 $this->lines[] = $line;
1846 $xnbp++;
1847 }
1848
1849 return 1;
1850 }
1851
1859 public function setDeliveryDate($user, $delivery_date)
1860 {
1861 // phpcs:enable
1862 if ($user->hasRight('reception', 'creer')) {
1863 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1864 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1865 $sql .= " WHERE rowid = ".((int) $this->id);
1866
1867 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1868 $resql = $this->db->query($sql);
1869 if ($resql) {
1870 $this->date_delivery = $delivery_date;
1871 return 1;
1872 } else {
1873 $this->error = $this->db->error();
1874 return -1;
1875 }
1876 } else {
1877 return -2;
1878 }
1879 }
1880
1888 public function setReceptionDate($user, $reception_date)
1889 {
1890 if ($user->hasRight('reception', 'creer')) {
1891 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1892 $sql .= " SET date_reception = ".($reception_date ? "'".$this->db->idate($reception_date)."'" : 'null');
1893 $sql .= " WHERE rowid = ".((int) $this->id);
1894
1895 dol_syslog(get_class($this)."::setReceptionDate", LOG_DEBUG);
1896 $resql = $this->db->query($sql);
1897 if ($resql) {
1898 $this->date_reception = $reception_date;
1899 return 1;
1900 } else {
1901 $this->error = $this->db->error();
1902 return -1;
1903 }
1904 } else {
1905 return -2;
1906 }
1907 }
1908
1909 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1915 public function fetch_delivery_methods()
1916 {
1917 // phpcs:enable
1918 global $langs;
1919 $this->meths = array();
1920
1921 $sql = "SELECT em.rowid, em.code, em.libelle";
1922 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1923 $sql .= " WHERE em.active = 1";
1924 $sql .= " ORDER BY em.libelle ASC";
1925
1926 $resql = $this->db->query($sql);
1927 if ($resql) {
1928 while ($obj = $this->db->fetch_object($resql)) {
1929 $label = $langs->trans('ReceptionMethod'.$obj->code);
1930 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1931 }
1932 }
1933 }
1934
1935 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1942 public function list_delivery_methods($id = 0)
1943 {
1944 // phpcs:enable
1945 global $langs;
1946
1947 $this->listmeths = array();
1948 $i = 0;
1949
1950 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1951 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1952 if (!empty($id)) {
1953 $sql .= " WHERE em.rowid = ".((int) $id);
1954 }
1955
1956 $resql = $this->db->query($sql);
1957 if ($resql) {
1958 while ($obj = $this->db->fetch_object($resql)) {
1959 $this->listmeths[$i]['rowid'] = $obj->rowid;
1960 $this->listmeths[$i]['code'] = $obj->code;
1961 $label = $langs->trans('ReceptionMethod'.$obj->code);
1962 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1963 $this->listmeths[$i]['description'] = $obj->description;
1964 $this->listmeths[$i]['tracking'] = $obj->tracking;
1965 $this->listmeths[$i]['active'] = $obj->active;
1966 $i++;
1967 }
1968 }
1969 }
1970
1977 public function getUrlTrackingStatus($value = '')
1978 {
1979 if (!empty($this->shipping_method_id)) {
1980 $sql = "SELECT em.code, em.tracking";
1981 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1982 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1983
1984 $resql = $this->db->query($sql);
1985 if ($resql) {
1986 if ($obj = $this->db->fetch_object($resql)) {
1987 $tracking = $obj->tracking;
1988 }
1989 }
1990 }
1991
1992 if (!empty($tracking) && !empty($value)) {
1993 $url = str_replace('{TRACKID}', $value, $tracking);
1994 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1995 } else {
1996 $this->tracking_url = $value;
1997 }
1998 }
1999
2005 public function setClosed()
2006 {
2007 global $conf, $langs, $user;
2008
2009 $error = 0;
2010
2011 // Protection. This avoid to move stock later when we should not
2012 if ($this->statut == Reception::STATUS_CLOSED) {
2013 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
2014 return 0;
2015 }
2016
2017 $this->db->begin();
2018
2019 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
2020 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2021
2022 $resql = $this->db->query($sql);
2023 if ($resql) {
2024 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
2025 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
2026 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2027
2028 $order = new CommandeFournisseur($this->db);
2029 $order->fetch($this->origin_id);
2030
2031 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
2032
2033 $receptions_match_order = 1;
2034 foreach ($order->lines as $line) {
2035 $lineid = $line->id;
2036 $qty = $line->qty;
2037 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
2038 $receptions_match_order = 0;
2039 $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';
2040 dol_syslog($text);
2041 break;
2042 }
2043 }
2044 if ($receptions_match_order) {
2045 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');
2046 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
2047 }
2048 }
2049
2050 $this->statut = self::STATUS_CLOSED;
2051 $this->status = self::STATUS_CLOSED;
2052
2053 // If stock increment is done on closing
2054 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2055 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2056
2057 $langs->load("agenda");
2058
2059 // Loop on each product line to add a stock movement
2060 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
2061 $sql = "SELECT cd.fk_product, cd.subprice,";
2062 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2063 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2064 $sql .= " ed.fk_elementdet, ed.cost_price";
2065 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2066 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2067 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2068 $sql .= " AND cd.rowid = ed.fk_elementdet";
2069
2070 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2071 $resql = $this->db->query($sql);
2072
2073 if ($resql) {
2074 $cpt = $this->db->num_rows($resql);
2075 for ($i = 0; $i < $cpt; $i++) {
2076 $obj = $this->db->fetch_object($resql);
2077
2078 $qty = $obj->qty;
2079
2080 if ($qty <= 0) {
2081 continue;
2082 }
2083
2084 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
2085
2086 $mouvS = new MouvementStock($this->db);
2087 $mouvS->origin = &$this;
2088 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2089
2090 if (empty($obj->batch)) {
2091 // line without batch detail
2092
2093 // 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
2094 $inventorycode = '';
2095 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2096 if ($result < 0) {
2097 $this->error = $mouvS->error;
2098 $this->errors = $mouvS->errors;
2099 $error++;
2100 break;
2101 }
2102 } else {
2103 // line with batch detail
2104
2105 // 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
2106 $inventorycode = '';
2107 $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);
2108
2109 if ($result < 0) {
2110 $this->error = $mouvS->error;
2111 $this->errors = $mouvS->errors;
2112 $error++;
2113 break;
2114 }
2115 }
2116 }
2117 } else {
2118 $this->error = $this->db->lasterror();
2119 $error++;
2120 }
2121 }
2122
2123 // Call trigger
2124 if (!$error) {
2125 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
2126 if ($result < 0) {
2127 $error++;
2128 }
2129 }
2130 } else {
2131 dol_print_error($this->db);
2132 $error++;
2133 }
2134
2135 if (!$error) {
2136 $this->db->commit();
2137 return 1;
2138 } else {
2139 $this->statut = self::STATUS_VALIDATED;
2140 $this->status = self::STATUS_VALIDATED;
2141 $this->db->rollback();
2142 return -1;
2143 }
2144 }
2145
2151 public function setBilled()
2152 {
2153 global $user;
2154 $error = 0;
2155
2156 $this->db->begin();
2157
2158 if ($this->statut == Reception::STATUS_VALIDATED) {
2159 // do not close if already closed
2160 $this->setClosed();
2161 }
2162
2163 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
2164 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2165
2166 $resql = $this->db->query($sql);
2167 if ($resql) {
2168 $this->billed = 1;
2169
2170 // Call trigger
2171 $result = $this->call_trigger('RECEPTION_BILLED', $user);
2172 if ($result < 0) {
2173 $this->billed = 0;
2174 $error++;
2175 }
2176 } else {
2177 $error++;
2178 $this->errors[] = $this->db->lasterror;
2179 }
2180
2181 if (empty($error)) {
2182 $this->db->commit();
2183 return 1;
2184 } else {
2185 $this->db->rollback();
2186 return -1;
2187 }
2188 }
2189
2195 public function reOpen()
2196 {
2197 global $langs, $user;
2198
2199 $error = 0;
2200
2201 $this->db->begin();
2202
2203 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
2204 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2205
2206 $resql = $this->db->query($sql);
2207 $rollbackStatus = $this->status;
2208 $rollbackBilled = $this->billed;
2209 if ($resql) {
2210 $this->statut = self::STATUS_VALIDATED;
2211 $this->status = self::STATUS_VALIDATED;
2212 $this->billed = 0;
2213
2214 // If stock increment is done on closing
2215 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2216 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2217 $numref = (string) $this->ref;
2218 $langs->load("agenda");
2219
2220 // Loop on each product line to add a stock movement
2221 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2222 $sql = "SELECT ed.fk_product, cd.subprice,";
2223 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2224 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2225 $sql .= " ed.fk_elementdet, ed.cost_price";
2226 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2227 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2228 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2229 $sql .= " AND cd.rowid = ed.fk_elementdet";
2230
2231 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2232 $resql = $this->db->query($sql);
2233 if ($resql) {
2234 $cpt = $this->db->num_rows($resql);
2235 for ($i = 0; $i < $cpt; $i++) {
2236 $obj = $this->db->fetch_object($resql);
2237
2238 $qty = $obj->qty;
2239
2240 if ($qty <= 0) {
2241 continue;
2242 }
2243 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2244
2245 //var_dump($this->lines[$i]);
2246 $mouvS = new MouvementStock($this->db);
2247 $mouvS->origin = &$this;
2248 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2249
2250 if (empty($obj->batch)) {
2251 // line without batch detail
2252
2253 // 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
2254 $inventorycode = '';
2255 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
2256
2257 if ($result < 0) {
2258 $this->error = $mouvS->error;
2259 $this->errors = $mouvS->errors;
2260 $error++;
2261 break;
2262 }
2263 } else {
2264 // line with batch detail
2265
2266 // 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
2267 $inventorycode = '';
2268 $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);
2269 if ($result < 0) {
2270 $this->error = $mouvS->error;
2271 $this->errors = $mouvS->errors;
2272 $error++;
2273 break;
2274 }
2275 }
2276 }
2277 } else {
2278 $this->error = $this->db->lasterror();
2279 $error++;
2280 }
2281 }
2282
2283 if (!$error) {
2284 // Call trigger
2285 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
2286 if ($result < 0) {
2287 $error++;
2288 }
2289 }
2290
2291 if (!$error && $this->origin == 'order_supplier') {
2292 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2293
2294 $commande = new CommandeFournisseur($this->db);
2295 $commande->fetch($this->origin_id);
2296 $result = $commande->setStatus($user, 4);
2297 if ($result < 0) {
2298 $error++;
2299 $this->error = $commande->error;
2300 $this->errors = $commande->errors;
2301 }
2302 }
2303 } else {
2304 $error++;
2305 $this->errors[] = $this->db->lasterror();
2306 }
2307
2308 if (!$error) {
2309 $this->db->commit();
2310 return 1;
2311 } else {
2312 $this->statut = $this->status = $rollbackStatus;
2313 $this->billed = $rollbackBilled;
2314 $this->db->rollback();
2315 return -1;
2316 }
2317 }
2318
2325 public function setDraft($user)
2326 {
2327 // phpcs:enable
2328 global $conf, $langs;
2329
2330 $error = 0;
2331
2332 // Protection
2333 if ($this->statut <= self::STATUS_DRAFT) {
2334 return 0;
2335 }
2336
2337 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
2338 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
2339 $this->error = 'Permission denied';
2340 return -1;
2341 }
2342
2343 $this->db->begin();
2344
2345 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2346 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2347 $sql .= " WHERE rowid = ".((int) $this->id);
2348
2349 dol_syslog(__METHOD__, LOG_DEBUG);
2350 if ($this->db->query($sql)) {
2351 // If stock increment is done on closing
2352 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2353 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2354
2355 $langs->load("agenda");
2356
2357 // Loop on each product line to add a stock movement
2358 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2359 $sql = "SELECT cd.fk_product, cd.subprice,";
2360 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2361 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2362 $sql .= " ed.fk_elementdet, ed.cost_price";
2363 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2364 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2365 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2366 $sql .= " AND cd.rowid = ed.fk_elementdet";
2367
2368 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2369 $resql = $this->db->query($sql);
2370 if ($resql) {
2371 $cpt = $this->db->num_rows($resql);
2372 for ($i = 0; $i < $cpt; $i++) {
2373 $obj = $this->db->fetch_object($resql);
2374
2375 $qty = $obj->qty;
2376
2377 if ($qty <= 0) {
2378 continue;
2379 }
2380
2381 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2382
2383 //var_dump($this->lines[$i]);
2384 $mouvS = new MouvementStock($this->db);
2385 $mouvS->origin = &$this;
2386 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2387
2388 if (empty($obj->batch)) {
2389 // line without batch detail
2390
2391 // 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
2392 $inventorycode = '';
2393 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2394 if ($result < 0) {
2395 $this->error = $mouvS->error;
2396 $this->errors = $mouvS->errors;
2397 $error++;
2398 break;
2399 }
2400 } else {
2401 // line with batch detail
2402
2403 // 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
2404 $inventorycode = '';
2405 $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);
2406 if ($result < 0) {
2407 $this->error = $mouvS->error;
2408 $this->errors = $mouvS->errors;
2409 $error++;
2410 break;
2411 }
2412 }
2413 }
2414 } else {
2415 $this->error = $this->db->lasterror();
2416 $error++;
2417 }
2418 }
2419
2420 if (!$error) {
2421 // Call trigger
2422 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2423 if ($result < 0) {
2424 $error++;
2425 }
2426 }
2427 if ($this->origin == 'order_supplier') {
2428 if (!empty($this->origin) && $this->origin_id > 0) {
2429 $this->fetch_origin();
2430 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2431 // Check if there is no more reception validated.
2432 $this->origin_object->fetchObjectLinked();
2433 $setStatut = 1;
2434 if (!empty($this->origin_object->linkedObjects['reception'])) {
2435 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2436 if ($rcption->statut > 0) {
2437 $setStatut = 0;
2438 break;
2439 }
2440 }
2441 //var_dump($this->$origin->receptions);exit;
2442 if ($setStatut) {
2443 $this->origin_object->setStatut(3); // CommandeFournisseur ordered
2444 }
2445 }
2446 }
2447 }
2448 }
2449
2450 if (!$error) {
2451 $this->statut = self::STATUS_DRAFT;
2452 $this->status = self::STATUS_DRAFT;
2453 $this->db->commit();
2454 return 1;
2455 } else {
2456 $this->db->rollback();
2457 return -1;
2458 }
2459 } else {
2460 $this->error = $this->db->error();
2461 $this->db->rollback();
2462 return -1;
2463 }
2464 }
2465
2476 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2477 {
2478 global $conf, $langs;
2479
2480 $langs->load("receptions");
2481
2482 if (!dol_strlen($modele)) {
2483 $modele = 'squille';
2484
2485 if ($this->model_pdf) {
2486 $modele = $this->model_pdf;
2487 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2488 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2489 }
2490 }
2491
2492 $modelpath = "core/modules/reception/doc/";
2493
2494 $this->fetch_origin();
2495
2496 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2497 }
2498
2507 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2508 {
2509 $tables = array('reception');
2510
2511 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2512 }
2513
2522 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2523 {
2524 $tables = array(
2525 'receptiondet_batch'
2526 );
2527
2528 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2529 }
2530}
$object ref
Definition info.php:90
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...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='')
Set status of an object.
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.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
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.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
fetch_origin()
Read linked origin object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
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.
updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options=array())
Update a simple reception line.
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.
getLinesArray()
Create an array of reception lines.
fetch_lines_free()
Load lines of simple reception.
update($user=null, $notrigger=0)
Update database.
setClosed()
Classify the reception as closed (this records also the stock movement)
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.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0)
Return clickable link of object (with eventually picto)
create_line($entrepot_id, $origin_line_id, $qty, $rang=0, $array_options=[], $parent_line_id=0, $product_id=0)
Create a reception line.
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.
addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options=[])
Add a simple reception line.
initAsSpecimen()
Initialise an instance with random values.
setReceptionDate($user, $reception_date)
Set the reception date.
fetch_lines()
Load lines.
create($user, $notrigger=0)
Create reception.
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 table commandefournisseurdispatch.
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:168
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
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_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
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:64
dol_now($mode='gmt')
Return date for now.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
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_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.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:487
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:134