dolibarr 23.0.4
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-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_DRAFT = 0;
213 const STATUS_VALIDATED = 1;
214 const STATUS_CLOSED = 2;
215
216
222 public function __construct($db)
223 {
224 $this->db = $db;
225
226 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
227 $this->isextrafieldmanaged = 1;
228 }
229
236 public function getNextNumRef($soc)
237 {
238 global $langs, $conf;
239 $langs->load("receptions");
240
241 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
242 $mybool = false;
243
244 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
245 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
246
247 // Include file with class
248 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
249
250 foreach ($dirmodels as $reldir) {
251 $dir = dol_buildpath($reldir."core/modules/reception/");
252
253 // Load file with numbering class (if found)
254 $mybool = ((bool) @include_once $dir.$file) || $mybool;
255 }
256
257 if (!$mybool) {
258 dol_print_error(null, "Failed to include file ".$file);
259 return '';
260 }
261
262 $obj = new $classname();
263 '@phan-var-force ModelNumRefReception $obj';
266 $numref = $obj->getNextValue($soc, $this);
267
268 if ($numref != "") {
269 return $numref;
270 } else {
271 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
272 return "";
273 }
274 } else {
275 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
276 return "";
277 }
278 }
279
287 public function create($user, $notrigger = 0)
288 {
289 global $conf;
290
291 $now = dol_now();
292
293 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
294 $error = 0;
295
296 // Clean parameters
297 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
298 if (empty($this->fk_project)) {
299 $this->fk_project = 0;
300 }
301 if (empty($this->weight_units)) {
302 $this->weight_units = 0;
303 }
304 if (empty($this->size_units)) {
305 $this->size_units = 0;
306 }
307 if (empty($this->date_creation)) {
308 $this->date_creation = $now;
309 }
310
311 $this->entity = setEntity($this);
312
313 $this->user = $user;
314
315 $this->db->begin();
316
317 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
318 $sql .= "ref";
319 $sql .= ", entity";
320 $sql .= ", ref_supplier";
321 $sql .= ", date_creation";
322 $sql .= ", fk_user_author";
323 $sql .= ", date_reception";
324 $sql .= ", date_delivery";
325 $sql .= ", fk_soc";
326 $sql .= ", fk_projet";
327 $sql .= ", fk_shipping_method";
328 $sql .= ", tracking_number";
329 $sql .= ", weight";
330 $sql .= ", size";
331 $sql .= ", width";
332 $sql .= ", height";
333 $sql .= ", weight_units";
334 $sql .= ", size_units";
335 $sql .= ", note_private";
336 $sql .= ", note_public";
337 $sql .= ", model_pdf";
338 $sql .= ", fk_incoterms, location_incoterms";
339 $sql .= ") VALUES (";
340 $sql .= "'(PROV)'";
341 $sql .= ", ".((int) $this->entity);
342 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
343 $sql .= ", '".$this->db->idate($this->date_creation)."'";
344 $sql .= ", ".((int) $user->id);
345 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
346 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
347 $sql .= ", ".($this->socid > 0 ? ((int) $this->socid) : "null");
348 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
349 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
350 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
351 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
352 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
353 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
354 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
355 $sql .= ", ".((int) $this->weight_units);
356 $sql .= ", ".((int) $this->size_units);
357 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
358 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
359 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
360 $sql .= ", ".(int) $this->fk_incoterms;
361 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
362 $sql .= ")";
363
364 dol_syslog(get_class($this)."::create", LOG_DEBUG);
365
366 $resql = $this->db->query($sql);
367
368 if ($resql) {
369 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
370
371 // update ref
372 $initialref = '(PROV'.$this->id.')';
373 if (!empty($this->ref)) {
374 $initialref = $this->ref;
375 }
376
377 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
378 $sql .= " SET ref = '".$this->db->escape($initialref)."'";
379 $sql .= " WHERE rowid = ".((int) $this->id);
380
381 dol_syslog(get_class($this)."::create", LOG_DEBUG);
382 if ($this->db->query($sql)) {
383 // Insert of lines
384 $num = count($this->lines);
385 for ($i = 0; $i < $num; $i++) {
386 $this->lines[$i]->fk_reception = $this->id;
387
388 if ($this->lines[$i]->create($user) <= 0) {
389 $this->setErrorsFromObject($this->lines[$i]);
390 $error++;
391 }
392 }
393
394 if (!$error && $this->id && $this->origin_id) {
395 $ret = $this->add_object_linked();
396 if (!$ret) {
397 $error++;
398 }
399 }
400
401 // Create extrafields
402 if (!$error) {
403 $result = $this->insertExtraFields();
404 if ($result < 0) {
405 $error++;
406 }
407 }
408
409 if (!$error && !$notrigger) {
410 // Call trigger
411 $result = $this->call_trigger('RECEPTION_CREATE', $user);
412 if ($result < 0) {
413 $error++;
414 }
415 // End call triggers
416 }
417
418 if (!$error) {
419 $this->db->commit();
420 return $this->id;
421 } else {
422 foreach ($this->errors as $errmsg) {
423 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
424 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
425 }
426 $this->db->rollback();
427 return -1 * $error;
428 }
429 } else {
430 $error++;
431 $this->error = $this->db->lasterror()." - sql=$sql";
432 $this->db->rollback();
433 return -2;
434 }
435 } else {
436 $error++;
437 $this->error = $this->db->error()." - sql=$sql";
438 $this->db->rollback();
439 return -1;
440 }
441 }
442
443 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
456 public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [], $parent_line_id = 0, $product_id = 0)
457 {
458 //phpcs:enable
459 global $user;
460
461 $receptionline = new ReceptionLineBatch($this->db);
462 $receptionline->fk_reception = $this->id;
463 $receptionline->entrepot_id = $entrepot_id;
464 $receptionline->fk_elementdet = $origin_line_id;
465 $receptionline->element_type = $this->origin;
466 $receptionline->fk_parent = $parent_line_id;
467 $receptionline->fk_product = $product_id;
468 $receptionline->qty = $qty;
469 $receptionline->rang = $rang;
470 $receptionline->array_options = $array_options;
471
472 if (!($receptionline->fk_product > 0)) {
473 $order_line = new CommandeFournisseurLigne($this->db);
474 $order_line->fetch($receptionline->fk_elementdet);
475 $receptionline->fk_product = $order_line->fk_product;
476 }
477
478 if (($lineId = $receptionline->insert($user)) < 0) {
479 $this->errors[] = $receptionline->error;
480 }
481 return $lineId;
482 }
483
492 public function fetch($id, $ref = '', $ref_ext = '')
493 {
494 // Check parameters
495 if (empty($id) && empty($ref) && empty($ref_ext)) {
496 return -1;
497 }
498
499 $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";
500 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
501 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery, e.date_valid";
502 $sql .= ", e.fk_shipping_method, e.tracking_number";
503 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
504 $sql .= ", e.note_private, e.note_public";
505 $sql .= ', e.fk_incoterms, e.location_incoterms';
506 $sql .= ', i.libelle as label_incoterms';
507 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
508 $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'";
509 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
510
511 if ($id) {
512 $sql .= " WHERE e.rowid = ".((int) $id);
513 } else {
514 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
515 if ($ref) {
516 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
517 } elseif ($ref_ext) {
518 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
519 }
520 }
521
522 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
523 $result = $this->db->query($sql);
524 if ($result) {
525 if ($this->db->num_rows($result)) {
526 $obj = $this->db->fetch_object($result);
527
528 $this->id = $obj->rowid;
529 $this->entity = $obj->entity;
530 $this->ref = $obj->ref;
531 $this->socid = $obj->socid;
532 $this->ref_supplier = $obj->ref_supplier;
533 $this->ref_ext = $obj->ref_ext;
534 $this->statut = $obj->status;
535 $this->status = $obj->status;
536 $this->billed = $obj->billed;
537 $this->fk_project = $obj->fk_project;
538 $this->user_author_id = $obj->fk_user_author;
539 $this->date_creation = $this->db->jdate($obj->date_creation);
540 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
541 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
542 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
543 $this->date_valid = $this->db->jdate($obj->date_valid); // Date validation
544 $this->model_pdf = $obj->model_pdf;
545 $this->shipping_method_id = $obj->fk_shipping_method;
546 $this->tracking_number = $obj->tracking_number;
547 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
548 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
549 $this->origin_id = $obj->origin_id;
550
551 $this->trueWeight = $obj->weight;
552 $this->weight_units = $obj->weight_units;
553
554 $this->trueWidth = $obj->width;
555 $this->width_units = $obj->size_units;
556 $this->trueHeight = $obj->height;
557 $this->height_units = $obj->size_units;
558 $this->trueDepth = $obj->size;
559 $this->depth_units = $obj->size_units;
560
561 $this->note_public = $obj->note_public;
562 $this->note_private = $obj->note_private;
563
564 // A denormalized value
565 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
566 $this->size_units = $obj->size_units;
567
568 //Incoterms
569 $this->fk_incoterms = $obj->fk_incoterms;
570 $this->location_incoterms = $obj->location_incoterms;
571 $this->label_incoterms = $obj->label_incoterms;
572
573 $this->db->free($result);
574
575 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
576 //$this->pdf_filename = $file;
577
578 // Tracking url
579 $this->getUrlTrackingStatus($obj->tracking_number);
580
581 /*
582 * Thirdparty
583 */
584 $result = $this->fetch_thirdparty();
585
586
587 // Retrieve all extrafields for reception
588 // fetch optionals attributes and labels
589 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
590 $extrafields = new ExtraFields($this->db);
591 $extrafields->fetch_name_optionals_label($this->table_element, true);
592 $this->fetch_optionals();
593
594 /*
595 * Lines
596 */
597 if (empty($obj->origin_id)) {
598 $result = $this->fetch_lines_free();
599 } else {
600 $result = $this->fetch_lines();
601 }
602 if ($result < 0) {
603 return -3;
604 }
605
606 return 1;
607 } else {
608 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
609 $this->error = 'Reception with id '.$id.' not found';
610 return 0;
611 }
612 } else {
613 $this->error = $this->db->error();
614 return -1;
615 }
616 }
617
625 public function valid($user, $notrigger = 0)
626 {
627 global $conf, $langs;
628
629 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
630
631 dol_syslog(get_class($this)."::valid");
632
633 // Protection
634 if ($this->statut) {
635 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
636 return 0;
637 }
638
639 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
640 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
641 $this->error = 'Permission denied';
642 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
643 return -1;
644 }
645
646 $this->db->begin();
647
648 $error = 0;
649
650 // Define new ref
651 $soc = new Societe($this->db);
652 $soc->fetch($this->socid);
653
654
655 // Define new ref
656 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
657 $numref = $this->getNextNumRef($soc);
658 } else {
659 $numref = (string) $this->ref;
660 }
661
662 $this->newref = dol_sanitizeFileName($numref);
663
664 $now = dol_now();
665
666 // Validate
667 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
668 $sql .= " ref='".$this->db->escape($numref)."'";
669 $sql .= ", fk_statut = 1";
670 $sql .= ", date_valid = '".$this->db->idate($now)."'";
671 $sql .= ", fk_user_valid = ".((int) $user->id);
672 $sql .= " WHERE rowid = ".((int) $this->id);
673 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
674 $resql = $this->db->query($sql);
675 if (!$resql) {
676 $this->error = $this->db->lasterror();
677 $error++;
678 }
679
680 // If stock increment is done on reception (recommended choice)
681 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
682 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
683
684 $langs->load("agenda");
685
686 // Loop on each product line to add a stock movement
687 // TODO in future, reception lines may not be linked to order line
688 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
689 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
690 $sql .= " ed.eatby, ed.sellby, ed.batch,";
691 $sql .= " ed.fk_elementdet, ed.cost_price";
692 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
693 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
694 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
695 $sql .= " AND cd.rowid = ed.fk_elementdet";
696
697 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
698 $resql = $this->db->query($sql);
699 if ($resql) {
700 $cpt = $this->db->num_rows($resql);
701 for ($i = 0; $i < $cpt; $i++) {
702 $obj = $this->db->fetch_object($resql);
703
704 $qty = $obj->qty;
705
706 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
707 continue;
708 }
709
710 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
711
712 //var_dump($this->lines[$i]);
713 $mouvS = new MouvementStock($this->db);
714 $mouvS->origin = &$this;
715 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
716
717 if (empty($obj->batch)) {
718 // line without batch detail
719
720 // 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.
721 $inventorycode = '';
722 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
723
724 if (intval($result) < 0) {
725 $error++;
726 $this->setErrorsFromObject($mouvS);
727 break;
728 }
729 } else {
730 // line with batch detail
731
732 // 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.
733 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
734 $inventorycode = '';
735 $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);
736
737 if (intval($result) < 0) {
738 $error++;
739 $this->setErrorsFromObject($mouvS);
740 break;
741 }
742 }
743 }
744 } else {
745 $this->db->rollback();
746 $this->error = $this->db->error();
747 return -2;
748 }
749 }
750
751 if (!$error && $this->origin_id > 0) {
752 // Change status of purchase order to "reception in process" or "totally received"
753 $status = $this->getStatusDispatch();
754 if ($status < 0) {
755 $error++;
756 } else {
757 $trigger_key = '';
758 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
759 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
760 if ($ret < 0) {
761 $error++;
762 $this->errors = array_merge($this->errors, $this->origin_object->errors);
763 }
764 } else {
765 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
766 if ($ret < 0) {
767 $error++;
768 }
769 }
770 }
771 }
772
773 if (!$error && !$notrigger) {
774 // Call trigger
775 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
776 if ($result < 0) {
777 $error++;
778 }
779 // End call triggers
780 }
781
782 if (!$error) {
783 $this->oldref = $this->ref;
784
785 // Rename directory if dir was a temporary ref
786 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
787 // Now we rename also files into index
788 $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)."'";
789 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
790 $resql = $this->db->query($sql);
791 if (!$resql) {
792 $error++;
793 $this->error = $this->db->lasterror();
794 }
795 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
796 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
797 $resql = $this->db->query($sql);
798 if (!$resql) {
799 $error++;
800 $this->error = $this->db->lasterror();
801 }
802
803 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
804 $oldref = dol_sanitizeFileName($this->ref);
805 $newref = dol_sanitizeFileName($numref);
806 $dirsource = $conf->reception->dir_output.'/'.$oldref;
807 $dirdest = $conf->reception->dir_output.'/'.$newref;
808 if (!$error && file_exists($dirsource)) {
809 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
810
811 if (@rename($dirsource, $dirdest)) {
812 dol_syslog("Rename ok");
813 // Rename docs starting with $oldref with $newref
814 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
815 foreach ($listoffiles as $fileentry) {
816 $dirsource = $fileentry['name'];
817 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
818 $dirsource = $fileentry['path'].'/'.$dirsource;
819 $dirdest = $fileentry['path'].'/'.$dirdest;
820 @rename($dirsource, $dirdest);
821 }
822 }
823 }
824 }
825 }
826
827 // Set new ref and current status
828 if (!$error) {
829 $this->ref = $numref;
830 $this->statut = self::STATUS_VALIDATED;
831 $this->status = self::STATUS_VALIDATED;
832 }
833
834 if (!$error) {
835 $this->db->commit();
836 return 1;
837 } else {
838 foreach ($this->errors as $errmsg) {
839 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
840 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
841 }
842 $this->db->rollback();
843 return -1 * $error;
844 }
845 }
846
852 public function getStatusDispatch()
853 {
854 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
855 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
856
858
859 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
860 if (empty($this->origin_object)) {
861 $this->fetch_origin();
862 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
863 $res = $this->origin_object->fetch_lines();
864 $this->commandeFournisseur = null; // deprecated
865 if ($res < 0) {
866 return $res;
867 }
868 } elseif ($this->origin_object instanceof CommandeFournisseur && empty($this->origin_object->lines)) {
869 $res = $this->origin_object->fetch_lines();
870 $this->commandeFournisseur = $this->origin_object; // deprecated
871 if ($res < 0) {
872 return $res;
873 }
874 }
875 }
876
877 $qty_received = array();
878 $qty_wished = array();
879
880 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
881 $filter = array('t.fk_element' => $this->origin_id);
882 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
883 $filter['t.status'] = 1; // Restrict to lines with status validated
884 }
885
886 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
887 if ($ret < 0) {
888 $this->setErrorsFromObject($supplierorderdispatch);
889 return $ret;
890 } else {
891 // Lines of draft receptions are not received yet
892 $draft_lines = array();
893 $sql = "SELECT rb.rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch as rb";
894 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."reception as r ON r.rowid = rb.fk_reception";
895 $sql .= " WHERE rb.fk_element = ".((int) $this->origin_id);
896 $sql .= " AND r.fk_statut = ".self::STATUS_DRAFT;
897 $resql = $this->db->query($sql);
898 if (!$resql) {
899 $this->error = $this->db->lasterror();
900 return -1;
901 }
902 while ($obj = $this->db->fetch_object($resql)) {
903 $draft_lines[(int) $obj->rowid] = (int) $obj->rowid;
904 }
905 $this->db->free($resql);
906
907 // build array with quantity received by product in all supplier orders (origin)
908 foreach ($supplierorderdispatch->lines as $dispatch_line) {
909 if (isset($draft_lines[(int) $dispatch_line->id])) {
910 continue;
911 }
912 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
913 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
914 } else {
915 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
916 }
917 }
918
919 // qty wished in origin (purchase order, ...)
920 foreach ($this->origin_object->lines as $origin_line) {
921 // exclude lines not qualified for reception
922 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
923 continue;
924 }
925 if (array_key_exists($origin_line->fk_product, $qty_wished)) {
926 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
927 } else {
928 $qty_wished[$origin_line->fk_product] = $origin_line->qty;
929 }
930 }
931
932 // compare array
933 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
934 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
935 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
936
937 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
939 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
940 // set totally received if more products received than ordered
941 $close = 0;
942
943 if (count($diff_array) > 0) {
944 // there are some difference between the two arrays
945 // scan the array of results
946 foreach ($diff_array as $key => $value) {
947 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
948 if ($qty_received[$key] >= $qty_wished[$key]) {
949 $close++;
950 }
951 }
952 }
953
954 if ($close == count($diff_array)) {
955 // all the products are received equal or more than the ordered quantity
957 }
958 }
959 }
960 }
961
962 return $status;
963 }
964
981 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
982 {
983 global $conf, $langs, $user;
984
985 $num = count($this->lines);
986 $line = new CommandeFournisseurDispatch($this->db);
987
988 $line->fk_entrepot = $entrepot_id;
989 $line->fk_commandefourndet = $id;
990 $line->qty = $qty;
991
992 $supplierorderline = new CommandeFournisseurLigne($this->db);
993 $result = $supplierorderline->fetch($id);
994 if ($result <= 0) {
995 $this->setErrorsFromObject($supplierorderline);
996 return -1;
997 }
998
999 $fk_product = 0;
1000 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
1001 $fk_product = $supplierorderline->fk_product;
1002
1003 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
1004 $langs->load("errors");
1005 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
1006 return -1;
1007 }
1008 }
1009
1010 // Check batch is set
1011 $product = new Product($this->db);
1012 $product->fetch($fk_product);
1013 if (isModEnabled('productbatch')) {
1014 $langs->load("errors");
1015 if (!empty($product->status_batch) && empty($batch)) {
1016 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
1017 return -1;
1018 } elseif (empty($product->status_batch) && !empty($batch)) {
1019 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
1020 return -1;
1021 }
1022
1023 // check sell-by / eat-by date is mandatory
1024 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
1025 if (!empty($errorMsgArr)) {
1026 $errorMessage = '<b>' . $product->ref . '</b> : ';
1027 $errorMessage .= '<ul>';
1028 foreach ($errorMsgArr as $errorMsg) {
1029 $errorMessage .= '<li>' . $errorMsg . '</li>';
1030 }
1031 $errorMessage .= '</ul>';
1032 $this->error = $errorMessage;
1033 return -1;
1034 }
1035 }
1036 unset($product);
1037
1038 // extrafields
1039 $line->array_options = $supplierorderline->array_options;
1040 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
1041 foreach ($array_options as $key => $value) {
1042 $line->array_options[$key] = $value;
1043 }
1044 }
1045
1046 $line->fk_product = $fk_product;
1047 $line->fk_commande = $supplierorderline->fk_commande;
1048 $line->fk_user = $user->id;
1049 $line->comment = $comment;
1050 $line->batch = $batch;
1051 $line->eatby = $eatby;
1052 $line->sellby = $sellby;
1053 $line->status = 1;
1054 $line->cost_price = $cost_price;
1055 $line->fk_reception = $this->id;
1056
1057 $this->lines[$num] = $line;
1058
1059 return $num;
1060 }
1061
1074 public function addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options = [])
1075 {
1076 global $mysoc, $langs, $user;
1077
1078 if ($this->status == self::STATUS_DRAFT) {
1079 if (empty($rang)) {
1080 $rang = 0;
1081 }
1082
1083 $qty = (float) price2num($qty);
1084
1085 $this->db->begin();
1086
1087 // Rank to use
1088 $ranktouse = $this->rang;
1089 if ($ranktouse == -1) {
1090 $rangmax = $this->line_max($this->fk_reception);
1091 $ranktouse = $rangmax + 1;
1092 }
1093
1094 // Insert line
1095 $this->line = new ReceptionLineBatch($this->db);
1096 $this->line->fk_reception = $this->id;
1097 $this->line->element_type = $element_type;
1098 $this->line->fk_product = $fk_product;
1099 $this->line->description = $description;
1100
1101 $this->line->qty = (float) $qty;
1102 $this->line->fk_unit = $fk_unit;
1103 $this->line->rang = $ranktouse;
1104
1105 if (is_array($array_options) && count($array_options) > 0) {
1106 $this->line->array_options = $array_options;
1107 }
1108
1109 $result = $this->line->insert($user);
1110 if ($result > 0) {
1111 if (!isset($this->context['createfromclone'])) {
1112 if ($this->fk_reception) {
1113 $this->line_order(true, 'DESC');
1114 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1115 $linecount = count($this->lines);
1116 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1117 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1118 }
1119 }
1120 $this->lines[] = $this->line;
1121 }
1122
1123 $this->db->commit();
1124 return $this->line->id;
1125 } else {
1126 $this->error = $this->line->error;
1127 dol_syslog(get_class($this)."::addlinefree error=".$this->error, LOG_ERR);
1128 $this->db->rollback();
1129 return -2;
1130 }
1131 } else {
1132 dol_syslog(get_class($this)."::addlinefree status of reception must be Draft to allow use of ->addlinefree()", LOG_ERR);
1133 return -3;
1134 }
1135 }
1136
1151 public function updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options = array())
1152 {
1153 global $mysoc, $langs, $user;
1154
1155 if ($this->statut == self::STATUS_DRAFT) {
1156 $this->db->begin();
1157
1158 if (empty($qty)) {
1159 $qty = 0;
1160 }
1161 if (empty($rang)) {
1162 $rang = 0;
1163 }
1164
1165 $qty = (float) $qty;
1166 $description = trim((string) $description);
1167
1168 // Fetch current line from the database and then clone the object and set it in $oldline property
1169 $line = new ReceptionLineBatch($this->db);
1170
1171 $line->fetch($rowid);
1172 $line->fetch_optionals();
1173
1174 if (!empty($line->fk_product)) {
1175 $product = new Product($this->db);
1176 $result = $product->fetch($line->fk_product);
1177 $product_type = $product->type;
1178 }
1179
1180 $staticline = clone $line;
1181
1182 $line->oldline = $staticline;
1183 $this->line = $line;
1184 $this->line->context = $this->context;
1185 $this->line->rang = $rang;
1186 $this->line->fk_reception = $this->id;
1187 $this->line->element_type = $element_type;
1188 $this->line->fk_product = $line->fk_product;
1189 $this->line->qty = $qty;
1190 $this->line->fk_unit = $fk_unit;
1191 $this->line->description = $description;
1192
1193 if (is_array($array_options) && count($array_options) > 0) {
1194 // We replace values in this->line->array_options only for entries defined into $array_options
1195 foreach ($array_options as $key => $value) {
1196 $this->line->array_options[$key] = $array_options[$key];
1197 }
1198 }
1199
1200
1201 $result = $this->line->update($user, $notrigger);
1202 if ($result > 0) {
1203 // Reorder if child line
1204
1205
1206 $this->db->commit();
1207 return $result;
1208 } else {
1209 $this->error = $this->line->error;
1210
1211 $this->db->rollback();
1212 return -1;
1213 }
1214 } else {
1215 $this->error = get_class($this)."::updatelinefree reception status makes operation forbidden";
1216 $this->errors = array('ReceptionStatusMakeOperationForbidden');
1217 return -2;
1218 }
1219 }
1220
1221 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1227 public function fetch_lines_free()
1228 {
1229 // phpcs:enable
1230 global $mysoc;
1231
1232 $this->lines = array();
1233
1234 $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';
1235 $sql .= ' FROM '.MAIN_DB_PREFIX.'receptiondet_batch as rc';
1236 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = rc.fk_product)';
1237 $sql .= ' WHERE rc.fk_reception = '.((int) $this->id);
1238
1239 $sql .= ' ORDER BY rc.rang, rc.rowid';
1240
1241 dol_syslog(get_class($this)."::fetch_lines_free", LOG_DEBUG);
1242 $result = $this->db->query($sql);
1243 if ($result) {
1244 $num = $this->db->num_rows($result);
1245
1246 $i = 0;
1247 while ($i < $num) {
1248 $objp = $this->db->fetch_object($result);
1249
1250 $line = new ReceptionLineBatch($this->db);
1251
1252 $line->rowid = $objp->rowid;
1253 $line->id = $objp->rowid;
1254 $line->fk_reception = $this->id;
1255
1256 $line->description = $objp->description;
1257 $line->qty = $objp->qty;
1258 $line->fk_entrepot = $objp->fk_entrepot;
1259 $line->fk_product = $objp->fk_product;
1260
1261 $line->rang = $objp->rang;
1262
1263
1264 $line->fk_element = $objp->fk_element;
1265 $line->fk_unit = $objp->fk_unit;
1266 $line->fk_elementdet = $objp->fk_elementdet;
1267 $line->fk_element_type = $objp->element_type;
1268 $line->fetch_optionals();
1269
1270
1271
1272 $this->lines[$i] = $line;
1273
1274 $i++;
1275 }
1276
1277 $this->db->free($result);
1278
1279 return 1;
1280 } else {
1281 $this->error = $this->db->error();
1282 return -3;
1283 }
1284 }
1285
1291 public function getLinesArray()
1292 {
1293 return $this->fetch_lines_free();
1294 }
1295
1303 public function update($user = null, $notrigger = 0)
1304 {
1305 global $conf;
1306 $error = 0;
1307
1308 // Clean parameters
1309
1310 if (isset($this->ref)) {
1311 $this->ref = trim($this->ref);
1312 }
1313 if (isset($this->entity)) {
1314 $this->entity = (int) $this->entity;
1315 }
1316 if (isset($this->ref_supplier)) {
1317 $this->ref_supplier = trim($this->ref_supplier);
1318 }
1319 if (isset($this->socid)) {
1320 $this->socid = (int) trim((string) $this->socid);
1321 }
1322 if (isset($this->fk_user_author)) {
1323 $this->fk_user_author = (int) $this->fk_user_author;
1324 }
1325 if (isset($this->fk_user_valid)) {
1326 $this->fk_user_valid = (int) $this->fk_user_valid;
1327 }
1328 if (isset($this->shipping_method_id)) {
1329 $this->shipping_method_id = (int) $this->shipping_method_id;
1330 }
1331 if (isset($this->tracking_number)) {
1332 $this->tracking_number = trim($this->tracking_number);
1333 }
1334 if (isset($this->statut)) {
1335 $this->statut = (int) $this->statut;
1336 }
1337 if (isset($this->trueDepth)) {
1338 $this->trueDepth = price2num($this->trueDepth);
1339 }
1340 if (isset($this->trueWidth)) {
1341 $this->trueWidth = price2num($this->trueWidth);
1342 }
1343 if (isset($this->trueHeight)) {
1344 $this->trueHeight = price2num($this->trueHeight);
1345 }
1346 $this->size_units = (int) $this->size_units;
1347
1348 if (isset($this->trueWeight)) {
1349 $this->weight = price2num($this->trueWeight);
1350 }
1351 $this->weight_units = (int) $this->weight_units;
1352
1353 if (isset($this->note_private)) {
1354 $this->note_private = trim($this->note_private);
1355 }
1356 if (isset($this->note_public)) {
1357 $this->note_public = trim($this->note_public);
1358 }
1359 if (isset($this->model_pdf)) {
1360 $this->model_pdf = trim($this->model_pdf);
1361 }
1362
1363
1364 // Check parameters
1365 // Put here code to add control on parameters values
1366
1367 // Update request
1368 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1369
1370 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1371 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1372 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1373 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1374 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
1375 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1376 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
1377 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1378 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1379 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
1380 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1381 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1382 $sql .= " height=".(($this->trueHeight != '') ? (float) $this->trueHeight : "null").",";
1383 $sql .= " width=".(($this->trueWidth != '') ? (float) $this->trueWidth : "null").",";
1384 $sql .= " size_units=".((int) $this->size_units).",";
1385 $sql .= " size=".(($this->trueDepth != '') ? (float) $this->trueDepth : "null").",";
1386 $sql .= " weight_units=".((int) $this->weight_units).",";
1387 $sql .= " weight=".(($this->trueWeight != '') ? (float) $this->trueWeight : "null").",";
1388 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1389 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1390 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1391 $sql .= " fk_projet=".((isset($this->fk_project) && $this->fk_project > 0) ? ((int) $this->fk_project) : "null").",";
1392 $sql .= " entity = ".((int) $conf->entity);
1393 $sql .= " WHERE rowid=".((int) $this->id);
1394
1395 $this->db->begin();
1396
1397 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1398 $resql = $this->db->query($sql);
1399 if (!$resql) {
1400 $error++;
1401 $this->errors[] = "Error ".$this->db->lasterror();
1402 }
1403
1404 if (!$error) {
1405 if (!$notrigger) {
1406 // Call trigger
1407 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1408 if ($result < 0) {
1409 $error++;
1410 }
1411 // End call triggers
1412 }
1413 }
1414
1415 // Commit or rollback
1416 if ($error) {
1417 foreach ($this->errors as $errmsg) {
1418 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1419 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1420 }
1421 $this->db->rollback();
1422 return -1 * $error;
1423 } else {
1424 $this->db->commit();
1425 return 1;
1426 }
1427 }
1428
1435 public function delete(User $user)
1436 {
1437 global $conf, $langs, $user;
1438 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1439
1440 $error = 0;
1441 $this->error = '';
1442
1443
1444 $this->db->begin();
1445
1446 // Stock control
1447 if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT)
1448 || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED))
1449 ) {
1450 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1451
1452 $langs->load("agenda");
1453
1454 // Loop on each product line to add a stock movement
1455 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1456 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1457 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1458 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1459 $sql .= " AND cd.rowid = ed.fk_elementdet";
1460
1461 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1462 $resql = $this->db->query($sql);
1463 if ($resql) {
1464 $cpt = $this->db->num_rows($resql);
1465 for ($i = 0; $i < $cpt; $i++) {
1466 dol_syslog(get_class($this)."::delete movement index ".$i);
1467 $obj = $this->db->fetch_object($resql);
1468
1469 $mouvS = new MouvementStock($this->db);
1470 // we do not log origin because it will be deleted
1471 $mouvS->origin = null;
1472
1473 $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
1474 if ($result < 0) {
1475 $error++;
1476 $this->error = $mouvS->error;
1477 $this->errors = $mouvS->errors;
1478 }
1479 }
1480 } else {
1481 $error++;
1482 $this->errors[] = "Error ".$this->db->lasterror();
1483 }
1484 }
1485
1486 if (!$error) {
1487 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1488 $ef = $main."_extrafields";
1489
1490 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1491
1492 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1493 $sql .= " WHERE fk_reception = ".((int) $this->id);
1494
1495 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1496 // Delete linked object
1497 $res = $this->deleteObjectLinked();
1498 if ($res < 0) {
1499 $error++;
1500 }
1501
1502 if (!$error) {
1503 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1504 $sql .= " WHERE rowid = ".((int) $this->id);
1505
1506 if ($this->db->query($sql)) {
1507 // Call trigger
1508 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1509 if ($result < 0) {
1510 $error++;
1511 }
1512 // End call triggers
1513
1514 if (!empty($this->origin) && $this->origin_id > 0) {
1515 $this->fetch_origin();
1517 '@phan-var-force CommandeFournisseur $origin_object';
1519 if ($origin_object->status == 4) { // If order source of reception is "partially received"
1520 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1521 $origin_object->loadReceptions();
1522 //var_dump($this->$origin->receptions);exit;
1523 if (count($origin_object->receptions) <= 0) {
1524 $origin_object->setStatut(3); // ordered
1525 }
1526 }
1527 }
1528
1529 if (!$error) {
1530 $this->db->commit();
1531
1532 // We delete PDFs
1533 $ref = dol_sanitizeFileName($this->ref);
1534 if (!empty($conf->reception->dir_output)) {
1535 $dir = $conf->reception->dir_output.'/'.$ref;
1536 $file = $dir.'/'.$ref.'.pdf';
1537 if (file_exists($file)) {
1538 if (!dol_delete_file($file)) {
1539 return 0;
1540 }
1541 }
1542 if (file_exists($dir)) {
1543 if (!dol_delete_dir_recursive($dir)) {
1544 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1545 return 0;
1546 }
1547 }
1548 }
1549
1550 return 1;
1551 } else {
1552 $this->db->rollback();
1553 return -1;
1554 }
1555 } else {
1556 $this->error = $this->db->lasterror()." - sql=$sql";
1557 $this->db->rollback();
1558 return -3;
1559 }
1560 } else {
1561 $this->error = $this->db->lasterror()." - sql=$sql";
1562 $this->db->rollback();
1563 return -2;
1564 }
1565 } else {
1566 $this->error = $this->db->lasterror()." - sql=$sql";
1567 $this->db->rollback();
1568 return -1;
1569 }
1570 } else {
1571 $this->db->rollback();
1572 return -1;
1573 }
1574 }
1575
1583 public function deleteLine($user, $lineid)
1584 {
1585 if ($this->status != self::STATUS_DRAFT) {
1586 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1587 return -2;
1588 }
1589
1590 $line = new ReceptionLineBatch($this->db);
1591 if ($line->fetch($lineid) <= 0) {
1592 $this->error = 'ErrorRecordNotFound';
1593 return -1;
1594 }
1595 if ($line->fk_reception != $this->id) {
1596 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1597 return -1;
1598 }
1599
1600 $this->db->begin();
1601 if ($line->delete($user) > 0) {
1602 $this->db->commit();
1603 return 1;
1604 }
1605 $this->error = $line->error;
1606 $this->db->rollback();
1607 return -1;
1608 }
1609
1610 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1616 public function fetch_lines()
1617 {
1618 // phpcs:enable
1619 $this->lines = array();
1620
1621 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1622
1623 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1624 $sql .= " WHERE fk_reception = ".((int) $this->id);
1625
1626 $resql = $this->db->query($sql);
1627
1628 if (!empty($resql)) {
1629 while ($obj = $this->db->fetch_object($resql)) {
1630 $line = new CommandeFournisseurDispatch($this->db);
1631
1632 $line->fetch($obj->rowid);
1633
1634 // TODO Remove or keep this ?
1635 $line->fetch_product();
1636
1637 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva, date_start, date_end, product_type';
1638 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1639 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1640 $sql_commfourndet .= ' ORDER BY rang';
1641
1642 $resql_commfourndet = $this->db->query($sql_commfourndet);
1643 if (!empty($resql_commfourndet)) {
1644 $obj = $this->db->fetch_object($resql_commfourndet);
1645 $line->qty_asked = $obj->qty;
1646 $line->description = $obj->description;
1647 $line->desc = $obj->description;
1648 $line->tva_tx = $obj->tva_tx;
1649 $line->vat_src_code = $obj->vat_src_code;
1650 $line->localtax1_tx = $obj->localtax1_tx;
1651 $line->localtax2_tx = $obj->localtax2_tx;
1652 $line->subprice = $obj->subprice;
1653 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1654 $line->remise_percent = $obj->remise_percent;
1655 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1656 $line->ref_supplier = $obj->ref;
1657 $line->total_ht = $obj->total_ht;
1658 $line->total_ttc = $obj->total_ttc;
1659 $line->total_tva = $obj->total_tva;
1660 $line->date_start = $this->db->jdate($obj->date_start);
1661 $line->date_end = $this->db->jdate($obj->date_end);
1662 $line->product_type = $obj->product_type;
1663 } else {
1664 $line->qty_asked = 0;
1665 $line->description = '';
1666 $line->desc = '';
1667 $line->label = $obj->label;
1668 }
1669
1670 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1671 $tva = $pu_ht * $line->tva_tx / 100;
1672 $this->total_ht += $pu_ht;
1673 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1674
1675 $this->total_ttc += $pu_ht + $tva;
1676
1677 if (isModEnabled('productbatch') && !empty($line->batch)) {
1678 $detail_batch = new stdClass();
1679 $detail_batch->eatby = $line->eatby;
1680 $detail_batch->sellby = $line->sellby;
1681 $detail_batch->batch = $line->batch;
1682 $detail_batch->qty = $line->qty;
1683
1684 $line->detail_batch[] = $detail_batch;
1685 }
1686
1687 $this->lines[] = $line;
1688 }
1689
1690 return 1;
1691 } else {
1692 return -1;
1693 }
1694 }
1695
1706 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0)
1707 {
1708 global $langs, $hookmanager;
1709
1710 $result = '';
1711 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1712 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1713 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1714
1715 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1716
1717 if ($short) {
1718 return $url;
1719 }
1720
1721 $linkclose = '';
1722 if (empty($notooltip)) {
1723 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1724 $label = $langs->trans("Reception");
1725 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1726 }
1727 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1728 $linkclose .= ' class="classfortooltip"';
1729 }
1730
1731 $linkstart = '<a href="'.$url.'"';
1732 $linkstart .= $linkclose.'>';
1733 $linkend = '</a>';
1734
1735 $result .= $linkstart;
1736 if ($withpicto) {
1737 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1738 }
1739 if ($withpicto != 2) {
1740 $result .= $this->ref;
1741 }
1742
1743 $result .= $linkend;
1744
1745 global $action;
1746 $hookmanager->initHooks(array($this->element . 'dao'));
1747 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1748 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1749 if ($reshook > 0) {
1750 $result = $hookmanager->resPrint;
1751 } else {
1752 $result .= $hookmanager->resPrint;
1753 }
1754 return $result;
1755 }
1756
1763 public function getLibStatut($mode = 0)
1764 {
1765 return $this->LibStatut($this->statut, $mode);
1766 }
1767
1768 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1776 public function LibStatut($status, $mode)
1777 {
1778 // phpcs:enable
1779 global $langs;
1780
1781 // List of long language codes for status
1782 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1783 $this->labelStatus[0] = 'StatusReceptionDraft';
1784 // product to receive if stock increase is on close or already received if stock increase is on validation
1785 $this->labelStatus[1] = 'StatusReceptionValidated';
1786 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1787 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1788 }
1789 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1790 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1791 }
1792 $this->labelStatus[2] = 'StatusReceptionProcessed';
1793
1794 // List of short language codes for status
1795 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1796 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1797 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1798 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1799
1800 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1801 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1802
1803 $statusType = 'status'.$status;
1804 if ($status == self::STATUS_VALIDATED) {
1805 $statusType = 'status4';
1806 }
1807 if ($status == self::STATUS_CLOSED) {
1808 $statusType = 'status6';
1809 }
1810
1811 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1812 }
1813
1821 public function getKanbanView($option = '', $arraydata = null)
1822 {
1823 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1824
1825 $return = '<div class="box-flex-item box-flex-grow-zero">';
1826 $return .= '<div class="info-box info-box-sm">';
1827 $return .= '<div class="info-box-icon bg-infobox-action">';
1828 $return .= img_picto('', 'order');
1829 $return .= '</div>';
1830 $return .= '<div class="info-box-content">';
1831 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1832 if ($selected >= 0) {
1833 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1834 }
1835 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1836 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1837 }
1838 /*if (property_exists($this, 'total_ht')) {
1839 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency()).' '.$langs->trans('HT').'</div>';
1840 }*/
1841 if (method_exists($this, 'getLibStatut')) {
1842 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1843 }
1844 $return .= '</div>';
1845 $return .= '</div>';
1846 $return .= '</div>';
1847
1848 return $return;
1849 }
1850
1858 public function initAsSpecimen()
1859 {
1860 global $langs;
1861
1862 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1863 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1864 $now = dol_now();
1865
1866 dol_syslog(get_class($this)."::initAsSpecimen");
1867
1868 $order = new CommandeFournisseur($this->db);
1869 $order->initAsSpecimen();
1870
1871 // Initialise parameters
1872 $this->id = 0;
1873 $this->ref = 'SPECIMEN';
1874 $this->specimen = 1;
1875 $this->statut = 1;
1876 $this->status = 1;
1877 $this->date = $now;
1878 $this->date_creation = $now;
1879 $this->date_valid = $now;
1880 $this->date_delivery = $now;
1881 $this->date_reception = $now + 24 * 3600;
1882 $this->entrepot_id = 0;
1883 $this->socid = 1;
1884 $this->origin_id = 1;
1885 $this->origin_type = 'supplier_order';
1886 $this->origin_object = $order;
1887 $this->note_private = 'Private note';
1888 $this->note_public = 'Public note';
1889 $this->tracking_number = 'TRACKID-ABC123';
1890 $this->fk_incoterms = 1;
1891 $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)
1892 $xnbp = 0;
1893 while ($xnbp < $nbp) {
1894 $line = new CommandeFournisseurDispatch($this->db);
1895 $line->desc = $langs->trans("Description")." ".$xnbp;
1896 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1897 $line->label = $langs->trans("Description")." ".$xnbp;
1898 $line->qty = 10;
1899
1900 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1901
1902 $this->lines[] = $line;
1903 $xnbp++;
1904 }
1905
1906 return 1;
1907 }
1908
1916 public function setDeliveryDate($user, $delivery_date)
1917 {
1918 // phpcs:enable
1919 if ($user->hasRight('reception', 'creer')) {
1920 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1921 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1922 $sql .= " WHERE rowid = ".((int) $this->id);
1923
1924 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1925 $resql = $this->db->query($sql);
1926 if ($resql) {
1927 $this->date_delivery = $delivery_date;
1928 return 1;
1929 } else {
1930 $this->error = $this->db->error();
1931 return -1;
1932 }
1933 } else {
1934 return -2;
1935 }
1936 }
1937
1945 public function setReceptionDate($user, $reception_date)
1946 {
1947 if ($user->hasRight('reception', 'creer')) {
1948 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1949 $sql .= " SET date_reception = ".($reception_date ? "'".$this->db->idate($reception_date)."'" : 'null');
1950 $sql .= " WHERE rowid = ".((int) $this->id);
1951
1952 dol_syslog(get_class($this)."::setReceptionDate", LOG_DEBUG);
1953 $resql = $this->db->query($sql);
1954 if ($resql) {
1955 $this->date_reception = $reception_date;
1956 return 1;
1957 } else {
1958 $this->error = $this->db->error();
1959 return -1;
1960 }
1961 } else {
1962 return -2;
1963 }
1964 }
1965
1966 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1972 public function fetch_delivery_methods()
1973 {
1974 // phpcs:enable
1975 global $langs;
1976 $this->meths = array();
1977
1978 $sql = "SELECT em.rowid, em.code, em.libelle";
1979 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1980 $sql .= " WHERE em.active = 1";
1981 $sql .= " ORDER BY em.libelle ASC";
1982
1983 $resql = $this->db->query($sql);
1984 if ($resql) {
1985 while ($obj = $this->db->fetch_object($resql)) {
1986 $label = $langs->trans('ReceptionMethod'.$obj->code);
1987 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1988 }
1989 }
1990 }
1991
1992 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1999 public function list_delivery_methods($id = 0)
2000 {
2001 // phpcs:enable
2002 global $langs;
2003
2004 $this->listmeths = array();
2005 $i = 0;
2006
2007 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
2008 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2009 if (!empty($id)) {
2010 $sql .= " WHERE em.rowid = ".((int) $id);
2011 }
2012
2013 $resql = $this->db->query($sql);
2014 if ($resql) {
2015 while ($obj = $this->db->fetch_object($resql)) {
2016 $this->listmeths[$i]['rowid'] = $obj->rowid;
2017 $this->listmeths[$i]['code'] = $obj->code;
2018 $label = $langs->trans('ReceptionMethod'.$obj->code);
2019 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
2020 $this->listmeths[$i]['description'] = $obj->description;
2021 $this->listmeths[$i]['tracking'] = $obj->tracking;
2022 $this->listmeths[$i]['active'] = $obj->active;
2023 $i++;
2024 }
2025 }
2026 }
2027
2034 public function getUrlTrackingStatus($value = '')
2035 {
2036 if (!empty($this->shipping_method_id)) {
2037 $sql = "SELECT em.code, em.tracking";
2038 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2039 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
2040
2041 $resql = $this->db->query($sql);
2042 if ($resql) {
2043 if ($obj = $this->db->fetch_object($resql)) {
2044 $tracking = $obj->tracking;
2045 }
2046 }
2047 }
2048
2049 if (!empty($tracking) && !empty($value)) {
2050 $url = str_replace('{TRACKID}', $value, $tracking);
2051 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
2052 } else {
2053 $this->tracking_url = $value;
2054 }
2055 }
2056
2062 public function setClosed()
2063 {
2064 global $conf, $langs, $user;
2065
2066 $error = 0;
2067
2068 // Protection. This avoid to move stock later when we should not
2069 if ($this->statut == Reception::STATUS_CLOSED) {
2070 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
2071 return 0;
2072 }
2073
2074 $this->db->begin();
2075
2076 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
2077 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2078
2079 $resql = $this->db->query($sql);
2080 if ($resql) {
2081 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
2082 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
2083 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2084
2085 $order = new CommandeFournisseur($this->db);
2086 $order->fetch($this->origin_id);
2087
2088 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
2089
2090 $receptions_match_order = 1;
2091 foreach ($order->lines as $line) {
2092 $lineid = $line->id;
2093 $qty = $line->qty;
2094 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
2095 $receptions_match_order = 0;
2096 $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';
2097 dol_syslog($text);
2098 break;
2099 }
2100 }
2101 if ($receptions_match_order) {
2102 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');
2103 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
2104 }
2105 }
2106
2107 $this->statut = self::STATUS_CLOSED;
2108 $this->status = self::STATUS_CLOSED;
2109
2110 // If stock increment is done on closing
2111 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2112 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2113
2114 $langs->load("agenda");
2115
2116 // Loop on each product line to add a stock movement
2117 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
2118 $sql = "SELECT cd.fk_product, cd.subprice,";
2119 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2120 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2121 $sql .= " ed.fk_elementdet, ed.cost_price";
2122 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2123 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2124 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2125 $sql .= " AND cd.rowid = ed.fk_elementdet";
2126
2127 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2128 $resql = $this->db->query($sql);
2129
2130 if ($resql) {
2131 $cpt = $this->db->num_rows($resql);
2132 for ($i = 0; $i < $cpt; $i++) {
2133 $obj = $this->db->fetch_object($resql);
2134
2135 $qty = $obj->qty;
2136
2137 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2138 continue;
2139 }
2140
2141 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
2142
2143 $mouvS = new MouvementStock($this->db);
2144 $mouvS->origin = &$this;
2145 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2146
2147 if (empty($obj->batch)) {
2148 // line without batch detail
2149
2150 // 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
2151 $inventorycode = '';
2152 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2153 if ($result < 0) {
2154 $this->error = $mouvS->error;
2155 $this->errors = $mouvS->errors;
2156 $error++;
2157 break;
2158 }
2159 } else {
2160 // line with batch detail
2161
2162 // 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
2163 $inventorycode = '';
2164 $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);
2165
2166 if ($result < 0) {
2167 $this->error = $mouvS->error;
2168 $this->errors = $mouvS->errors;
2169 $error++;
2170 break;
2171 }
2172 }
2173 }
2174 } else {
2175 $this->error = $this->db->lasterror();
2176 $error++;
2177 }
2178 }
2179
2180 // Call trigger
2181 if (!$error) {
2182 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
2183 if ($result < 0) {
2184 $error++;
2185 }
2186 }
2187 } else {
2188 dol_print_error($this->db);
2189 $error++;
2190 }
2191
2192 if (!$error) {
2193 $this->db->commit();
2194 return 1;
2195 } else {
2196 $this->statut = self::STATUS_VALIDATED;
2197 $this->status = self::STATUS_VALIDATED;
2198 $this->db->rollback();
2199 return -1;
2200 }
2201 }
2202
2208 public function setBilled()
2209 {
2210 global $user;
2211 $error = 0;
2212
2213 $this->db->begin();
2214
2215 if ($this->statut == Reception::STATUS_VALIDATED) {
2216 // do not close if already closed
2217 $this->setClosed();
2218 }
2219
2220 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
2221 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2222
2223 $resql = $this->db->query($sql);
2224 if ($resql) {
2225 $this->billed = 1;
2226
2227 // Call trigger
2228 $result = $this->call_trigger('RECEPTION_BILLED', $user);
2229 if ($result < 0) {
2230 $this->billed = 0;
2231 $error++;
2232 }
2233 } else {
2234 $error++;
2235 $this->errors[] = $this->db->lasterror;
2236 }
2237
2238 if (empty($error)) {
2239 $this->db->commit();
2240 return 1;
2241 } else {
2242 $this->db->rollback();
2243 return -1;
2244 }
2245 }
2246
2252 public function reOpen()
2253 {
2254 global $langs, $user;
2255
2256 $error = 0;
2257
2258 $this->db->begin();
2259
2260 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
2261 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2262
2263 $resql = $this->db->query($sql);
2264 $rollbackStatus = $this->status;
2265 $rollbackBilled = $this->billed;
2266 if ($resql) {
2267 $this->statut = self::STATUS_VALIDATED;
2268 $this->status = self::STATUS_VALIDATED;
2269 $this->billed = 0;
2270
2271 // If stock increment is done on closing
2272 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2273 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2274 $numref = (string) $this->ref;
2275 $langs->load("agenda");
2276
2277 // Loop on each product line to add a stock movement
2278 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2279 $sql = "SELECT ed.fk_product, cd.subprice,";
2280 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2281 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2282 $sql .= " ed.fk_elementdet, ed.cost_price";
2283 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2284 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2285 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2286 $sql .= " AND cd.rowid = ed.fk_elementdet";
2287
2288 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2289 $resql = $this->db->query($sql);
2290 if ($resql) {
2291 $cpt = $this->db->num_rows($resql);
2292 for ($i = 0; $i < $cpt; $i++) {
2293 $obj = $this->db->fetch_object($resql);
2294
2295 $qty = $obj->qty;
2296
2297 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2298 continue;
2299 }
2300 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2301
2302 //var_dump($this->lines[$i]);
2303 $mouvS = new MouvementStock($this->db);
2304 $mouvS->origin = &$this;
2305 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2306
2307 if (empty($obj->batch)) {
2308 // line without batch detail
2309
2310 // 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
2311 $inventorycode = '';
2312 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
2313
2314 if ($result < 0) {
2315 $this->error = $mouvS->error;
2316 $this->errors = $mouvS->errors;
2317 $error++;
2318 break;
2319 }
2320 } else {
2321 // line with batch detail
2322
2323 // 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
2324 $inventorycode = '';
2325 $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);
2326 if ($result < 0) {
2327 $this->error = $mouvS->error;
2328 $this->errors = $mouvS->errors;
2329 $error++;
2330 break;
2331 }
2332 }
2333 }
2334 } else {
2335 $this->error = $this->db->lasterror();
2336 $error++;
2337 }
2338 }
2339
2340 if (!$error) {
2341 // Call trigger
2342 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
2343 if ($result < 0) {
2344 $error++;
2345 }
2346 }
2347
2348 if (!$error && $this->origin == 'order_supplier') {
2349 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2350
2351 $commande = new CommandeFournisseur($this->db);
2352 $commande->fetch($this->origin_id);
2353 $result = $commande->setStatus($user, 4);
2354 if ($result < 0) {
2355 $error++;
2356 $this->error = $commande->error;
2357 $this->errors = $commande->errors;
2358 }
2359 }
2360 } else {
2361 $error++;
2362 $this->errors[] = $this->db->lasterror();
2363 }
2364
2365 if (!$error) {
2366 $this->db->commit();
2367 return 1;
2368 } else {
2369 $this->statut = $this->status = $rollbackStatus;
2370 $this->billed = $rollbackBilled;
2371 $this->db->rollback();
2372 return -1;
2373 }
2374 }
2375
2382 public function setDraft($user)
2383 {
2384 // phpcs:enable
2385 global $conf, $langs;
2386
2387 $error = 0;
2388
2389 // Protection
2390 if ($this->statut <= self::STATUS_DRAFT) {
2391 return 0;
2392 }
2393
2394 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
2395 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
2396 $this->error = 'Permission denied';
2397 return -1;
2398 }
2399
2400 $this->db->begin();
2401
2402 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2403 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2404 $sql .= " WHERE rowid = ".((int) $this->id);
2405
2406 dol_syslog(__METHOD__, LOG_DEBUG);
2407 if ($this->db->query($sql)) {
2408 // If stock increment is done on closing
2409 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2410 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2411
2412 $langs->load("agenda");
2413
2414 // Loop on each product line to add a stock movement
2415 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2416 $sql = "SELECT cd.fk_product, cd.subprice,";
2417 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2418 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2419 $sql .= " ed.fk_elementdet, ed.cost_price";
2420 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2421 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2422 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2423 $sql .= " AND cd.rowid = ed.fk_elementdet";
2424
2425 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2426 $resql = $this->db->query($sql);
2427 if ($resql) {
2428 $cpt = $this->db->num_rows($resql);
2429 for ($i = 0; $i < $cpt; $i++) {
2430 $obj = $this->db->fetch_object($resql);
2431
2432 $qty = $obj->qty;
2433
2434 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2435 continue;
2436 }
2437
2438 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2439
2440 //var_dump($this->lines[$i]);
2441 $mouvS = new MouvementStock($this->db);
2442 $mouvS->origin = &$this;
2443 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2444
2445 if (empty($obj->batch)) {
2446 // line without batch detail
2447
2448 // 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
2449 $inventorycode = '';
2450 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2451 if ($result < 0) {
2452 $this->error = $mouvS->error;
2453 $this->errors = $mouvS->errors;
2454 $error++;
2455 break;
2456 }
2457 } else {
2458 // line with batch detail
2459
2460 // 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
2461 $inventorycode = '';
2462 $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);
2463 if ($result < 0) {
2464 $this->error = $mouvS->error;
2465 $this->errors = $mouvS->errors;
2466 $error++;
2467 break;
2468 }
2469 }
2470 }
2471 } else {
2472 $this->error = $this->db->lasterror();
2473 $error++;
2474 }
2475 }
2476
2477 if (!$error) {
2478 // Call trigger
2479 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2480 if ($result < 0) {
2481 $error++;
2482 }
2483 }
2484 if ($this->origin == 'order_supplier') {
2485 if (!empty($this->origin) && $this->origin_id > 0) {
2486 $this->fetch_origin();
2487 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2488 // Check if there is no more reception validated.
2489 $this->origin_object->fetchObjectLinked();
2490 $setStatut = 1;
2491 if (!empty($this->origin_object->linkedObjects['reception'])) {
2492 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2493 if ($rcption->statut > 0) {
2494 $setStatut = 0;
2495 break;
2496 }
2497 }
2498 //var_dump($this->$origin->receptions);exit;
2499 if ($setStatut) {
2500 $this->origin_object->setStatut(3); // ordered
2501 }
2502 }
2503 }
2504 }
2505 }
2506
2507 if (!$error) {
2508 $this->statut = self::STATUS_DRAFT;
2509 $this->status = self::STATUS_DRAFT;
2510 $this->db->commit();
2511 return 1;
2512 } else {
2513 $this->db->rollback();
2514 return -1;
2515 }
2516 } else {
2517 $this->error = $this->db->error();
2518 $this->db->rollback();
2519 return -1;
2520 }
2521 }
2522
2533 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2534 {
2535 global $conf, $langs;
2536
2537 $langs->load("receptions");
2538
2539 if (!dol_strlen($modele)) {
2540 $modele = 'squille';
2541
2542 if ($this->model_pdf) {
2543 $modele = $this->model_pdf;
2544 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2545 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2546 }
2547 }
2548
2549 $modelpath = "core/modules/reception/doc/";
2550
2551 $this->fetch_origin();
2552
2553 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2554 }
2555
2564 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2565 {
2566 $tables = array('reception');
2567
2568 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2569 }
2570
2579 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2580 {
2581 $tables = array(
2582 'receptiondet_batch'
2583 );
2584
2585 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2586 }
2587}
$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.
deleteLine($user, $lineid)
Delete a line of the reception.
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:171
global $mysoc
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.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left 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 PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:469
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:136