dolibarr 24.0.2
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-2026 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 = ".((int) $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 // Lines of draft receptions are not received yet
893 $draft_lines = array();
894 $sql = "SELECT rb.rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch as rb";
895 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."reception as r ON r.rowid = rb.fk_reception";
896 $sql .= " WHERE rb.fk_element = ".((int) $this->origin_id);
897 $sql .= " AND r.fk_statut = ".self::STATUS_DRAFT;
898 $resql = $this->db->query($sql);
899 if (!$resql) {
900 $this->error = $this->db->lasterror();
901 return -1;
902 }
903 while ($obj = $this->db->fetch_object($resql)) {
904 $draft_lines[(int) $obj->rowid] = (int) $obj->rowid;
905 }
906 $this->db->free($resql);
907
908 // build array with quantity received by product in all supplier orders (origin)
909 foreach ($supplierorderdispatch->lines as $dispatch_line) {
910 if (isset($draft_lines[(int) $dispatch_line->id])) {
911 continue;
912 }
913 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
914 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
915 } else {
916 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
917 }
918 }
919
920 // qty wished in origin (purchase order, ...)
921 foreach ($this->origin_object->lines as $origin_line) {
922 // exclude lines not qualified for reception
923 if (($origin_line->product_type > 0 && (!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') || empty($origin_line->stockable_product))) || $origin_line->product_type > 1) {
924 continue;
925 }
926 if (array_key_exists($origin_line->fk_product, $qty_wished)) {
927 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
928 } else {
929 $qty_wished[$origin_line->fk_product] = $origin_line->qty;
930 }
931 }
932
933 // compare array
934 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
935 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
936 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
937
938 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
940 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
941 // set totally received if more products received than ordered
942 $close = 0;
943
944 if (count($diff_array) > 0) {
945 // there are some difference between the two arrays
946 // scan the array of results
947 foreach ($diff_array as $key => $value) {
948 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
949 if ($qty_received[$key] >= $qty_wished[$key]) {
950 $close++;
951 }
952 }
953 }
954
955 if ($close == count($diff_array)) {
956 // all the products are received equal or more than the ordered quantity
958 }
959 }
960 }
961 }
962
963 return $status;
964 }
965
982 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
983 {
984 global $conf, $langs, $user;
985
986 $num = count($this->lines);
987 $line = new CommandeFournisseurDispatch($this->db);
988
989 $line->fk_entrepot = $entrepot_id;
990 $line->fk_commandefourndet = $id;
991 $line->qty = $qty;
992
993 $supplierorderline = new CommandeFournisseurLigne($this->db);
994 $result = $supplierorderline->fetch($id);
995 if ($result <= 0) {
996 $this->setErrorsFromObject($supplierorderline);
997 return -1;
998 }
999
1000 $fk_product = 0;
1001 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
1002 $fk_product = $supplierorderline->fk_product;
1003
1004 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
1005 $langs->load("errors");
1006 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
1007 return -1;
1008 }
1009 }
1010
1011 // Check batch is set
1012 $product = new Product($this->db);
1013 $product->fetch($fk_product);
1014 if (isModEnabled('productbatch')) {
1015 $langs->load("errors");
1016 if (!empty($product->status_batch) && empty($batch)) {
1017 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
1018 return -1;
1019 } elseif (empty($product->status_batch) && !empty($batch)) {
1020 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
1021 return -1;
1022 }
1023
1024 // check sell-by / eat-by date is mandatory
1025 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
1026 if (!empty($errorMsgArr)) {
1027 $errorMessage = '<b>' . $product->ref . '</b> : ';
1028 $errorMessage .= '<ul>';
1029 foreach ($errorMsgArr as $errorMsg) {
1030 $errorMessage .= '<li>' . $errorMsg . '</li>';
1031 }
1032 $errorMessage .= '</ul>';
1033 $this->error = $errorMessage;
1034 return -1;
1035 }
1036 }
1037 unset($product);
1038
1039 // extrafields
1040 $line->array_options = $supplierorderline->array_options;
1041 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
1042 foreach ($array_options as $key => $value) {
1043 $line->array_options[$key] = $value;
1044 }
1045 }
1046
1047 $line->fk_product = $fk_product;
1048 $line->fk_commande = $supplierorderline->fk_commande;
1049 $line->fk_user = $user->id;
1050 $line->comment = $comment;
1051 $line->batch = $batch;
1052 $line->eatby = $eatby;
1053 $line->sellby = $sellby;
1054 $line->status = 1;
1055 $line->cost_price = $cost_price;
1056 $line->fk_reception = $this->id;
1057
1058 $this->lines[$num] = $line;
1059
1060 return $num;
1061 }
1062
1075 public function addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options = [])
1076 {
1077 global $mysoc, $langs, $user;
1078
1079 if ($this->status == self::STATUS_DRAFT) {
1080 if (empty($rang)) {
1081 $rang = 0;
1082 }
1083
1084 $qty = (float) price2num($qty);
1085
1086 $this->db->begin();
1087
1088 // Rank to use
1089 $ranktouse = $this->rang;
1090 if ($ranktouse == -1) {
1091 $rangmax = $this->line_max($this->fk_reception);
1092 $ranktouse = $rangmax + 1;
1093 }
1094
1095 // Insert line
1096 $this->line = new ReceptionLineBatch($this->db);
1097 $this->line->fk_reception = $this->id;
1098 $this->line->element_type = $element_type;
1099 $this->line->fk_product = $fk_product;
1100 $this->line->description = $description;
1101
1102 $this->line->qty = (float) $qty;
1103 $this->line->fk_unit = $fk_unit;
1104 $this->line->rang = $ranktouse;
1105
1106 if (is_array($array_options) && count($array_options) > 0) {
1107 $this->line->array_options = $array_options;
1108 }
1109
1110 $result = $this->line->insert($user);
1111 if ($result > 0) {
1112 if (!isset($this->context['createfromclone'])) {
1113 if ($this->fk_reception) {
1114 $this->line_order(true, 'DESC');
1115 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1116 $linecount = count($this->lines);
1117 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1118 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1119 }
1120 }
1121 $this->lines[] = $this->line;
1122 }
1123
1124 $this->db->commit();
1125 return $this->line->id;
1126 } else {
1127 $this->error = $this->line->error;
1128 dol_syslog(get_class($this)."::addlinefree error=".$this->error, LOG_ERR);
1129 $this->db->rollback();
1130 return -2;
1131 }
1132 } else {
1133 dol_syslog(get_class($this)."::addlinefree status of reception must be Draft to allow use of ->addlinefree()", LOG_ERR);
1134 return -3;
1135 }
1136 }
1137
1152 public function updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options = array())
1153 {
1154 global $mysoc, $langs, $user;
1155
1156 if ($this->statut == self::STATUS_DRAFT) {
1157 $this->db->begin();
1158
1159 if (empty($qty)) {
1160 $qty = 0;
1161 }
1162 if (empty($rang)) {
1163 $rang = 0;
1164 }
1165
1166 $qty = (float) $qty;
1167 $description = trim((string) $description);
1168
1169 // Fetch current line from the database and then clone the object and set it in $oldline property
1170 $line = new ReceptionLineBatch($this->db);
1171
1172 $line->fetch($rowid);
1173 $line->fetch_optionals();
1174
1175 if (!empty($line->fk_product)) {
1176 $product = new Product($this->db);
1177 $result = $product->fetch($line->fk_product);
1178 $product_type = $product->type;
1179 }
1180
1181 $staticline = clone $line;
1182
1183 $line->oldline = $staticline;
1184 $this->line = $line;
1185 $this->line->context = $this->context;
1186 $this->line->rang = $rang;
1187 $this->line->fk_reception = $this->id;
1188 $this->line->element_type = $element_type;
1189 $this->line->fk_product = $line->fk_product;
1190 $this->line->qty = $qty;
1191 $this->line->fk_unit = $fk_unit;
1192 $this->line->description = $description;
1193
1194 if (is_array($array_options) && count($array_options) > 0) {
1195 // We replace values in this->line->array_options only for entries defined into $array_options
1196 foreach ($array_options as $key => $value) {
1197 $this->line->array_options[$key] = $array_options[$key];
1198 }
1199 }
1200
1201
1202 $result = $this->line->update($user, $notrigger);
1203 if ($result > 0) {
1204 // Reorder if child line
1205
1206
1207 $this->db->commit();
1208 return $result;
1209 } else {
1210 $this->error = $this->line->error;
1211
1212 $this->db->rollback();
1213 return -1;
1214 }
1215 } else {
1216 $this->error = get_class($this)."::updatelinefree reception status makes operation forbidden";
1217 $this->errors = array('ReceptionStatusMakeOperationForbidden');
1218 return -2;
1219 }
1220 }
1221
1222 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1228 public function fetch_lines_free()
1229 {
1230 // phpcs:enable
1231 $this->lines = array();
1232
1233 $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';
1234 $sql .= ' FROM '.MAIN_DB_PREFIX.'receptiondet_batch as rc';
1235 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = rc.fk_product)';
1236 $sql .= ' WHERE rc.fk_reception = '.((int) $this->id);
1237
1238 $sql .= ' ORDER BY rc.rang, rc.rowid';
1239
1240 dol_syslog(get_class($this)."::fetch_lines_free", LOG_DEBUG);
1241 $result = $this->db->query($sql);
1242 if ($result) {
1243 $num = $this->db->num_rows($result);
1244
1245 $i = 0;
1246 while ($i < $num) {
1247 $objp = $this->db->fetch_object($result);
1248
1249 $line = new ReceptionLineBatch($this->db);
1250
1251 $line->rowid = $objp->rowid;
1252 $line->id = $objp->rowid;
1253 $line->fk_reception = $this->id;
1254
1255 $line->description = $objp->description;
1256 $line->qty = $objp->qty;
1257 $line->fk_entrepot = $objp->fk_entrepot;
1258 $line->fk_product = $objp->fk_product;
1259
1260 $line->rang = $objp->rang;
1261
1262
1263 $line->fk_element = $objp->fk_element;
1264 $line->fk_unit = $objp->fk_unit;
1265 $line->fk_elementdet = $objp->fk_elementdet;
1266 $line->fk_element_type = $objp->element_type;
1267 $line->fetch_optionals();
1268
1269 $this->lines[$i] = $line;
1270
1271 $i++;
1272 }
1273
1274 $this->db->free($result);
1275
1276 return 1;
1277 } else {
1278 $this->error = $this->db->error();
1279 return -3;
1280 }
1281 }
1282
1288 public function getLinesArray()
1289 {
1290 return $this->fetch_lines_free();
1291 }
1292
1300 public function update($user = null, $notrigger = 0)
1301 {
1302 global $conf;
1303 $error = 0;
1304
1305 // Clean parameters
1306
1307 if (isset($this->ref)) {
1308 $this->ref = trim($this->ref);
1309 }
1310 if (isset($this->entity)) {
1311 $this->entity = (int) $this->entity;
1312 }
1313 if (isset($this->ref_supplier)) {
1314 $this->ref_supplier = trim($this->ref_supplier);
1315 }
1316 if (isset($this->socid)) {
1317 $this->socid = (int) trim((string) $this->socid);
1318 }
1319 if (isset($this->fk_user_author)) {
1320 $this->fk_user_author = (int) $this->fk_user_author;
1321 }
1322 if (isset($this->fk_user_valid)) {
1323 $this->fk_user_valid = (int) $this->fk_user_valid;
1324 }
1325 if (isset($this->shipping_method_id)) {
1326 $this->shipping_method_id = (int) $this->shipping_method_id;
1327 }
1328 if (isset($this->tracking_number)) {
1329 $this->tracking_number = trim($this->tracking_number);
1330 }
1331 if (isset($this->statut)) {
1332 $this->statut = (int) $this->statut;
1333 }
1334 if (isset($this->trueDepth)) {
1335 $this->trueDepth = price2num($this->trueDepth);
1336 }
1337 if (isset($this->trueWidth)) {
1338 $this->trueWidth = price2num($this->trueWidth);
1339 }
1340 if (isset($this->trueHeight)) {
1341 $this->trueHeight = price2num($this->trueHeight);
1342 }
1343 $this->size_units = (int) $this->size_units;
1344
1345 if (isset($this->trueWeight)) {
1346 $this->weight = price2num($this->trueWeight);
1347 }
1348 $this->weight_units = (int) $this->weight_units;
1349
1350 if (isset($this->note_private)) {
1351 $this->note_private = trim($this->note_private);
1352 }
1353 if (isset($this->note_public)) {
1354 $this->note_public = trim($this->note_public);
1355 }
1356 if (isset($this->model_pdf)) {
1357 $this->model_pdf = trim($this->model_pdf);
1358 }
1359
1360
1361 // Check parameters
1362 // Put here code to add control on parameters values
1363
1364 // Update request
1365 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1366
1367 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1368 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1369 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1370 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1371 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? ((int) $this->fk_user_author) : "null").",";
1372 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1373 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1374 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1375 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1376 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? ((int) $this->shipping_method_id) : "null").",";
1377 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1378 $sql .= " fk_statut=".(isset($this->statut) ? ((int) $this->statut) : "null").",";
1379 $sql .= " height=".(($this->trueHeight != '') ? (float) $this->trueHeight : "null").",";
1380 $sql .= " width=".(($this->trueWidth != '') ? (float) $this->trueWidth : "null").",";
1381 $sql .= " size_units=".((int) $this->size_units).",";
1382 $sql .= " size=".(($this->trueDepth != '') ? (float) $this->trueDepth : "null").",";
1383 $sql .= " weight_units=".((int) $this->weight_units).",";
1384 $sql .= " weight=".(($this->trueWeight != '') ? (float) $this->trueWeight : "null").",";
1385 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1386 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1387 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1388 $sql .= " fk_projet=".((isset($this->fk_project) && $this->fk_project > 0) ? ((int) $this->fk_project) : "null").",";
1389 $sql .= " entity = ".((int) $conf->entity);
1390 $sql .= " WHERE rowid=".((int) $this->id);
1391
1392 $this->db->begin();
1393
1394 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1395 $resql = $this->db->query($sql);
1396 if (!$resql) {
1397 $error++;
1398 $this->errors[] = "Error ".$this->db->lasterror();
1399 }
1400
1401 if (!$error) {
1402 if (!$notrigger) {
1403 // Call trigger
1404 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1405 if ($result < 0) {
1406 $error++;
1407 }
1408 // End call triggers
1409 }
1410 }
1411
1412 // Commit or rollback
1413 if ($error) {
1414 foreach ($this->errors as $errmsg) {
1415 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1416 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1417 }
1418 $this->db->rollback();
1419 return -1 * $error;
1420 } else {
1421 $this->db->commit();
1422 return 1;
1423 }
1424 }
1425
1432 public function delete(User $user)
1433 {
1434 global $conf, $langs, $user;
1435 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1436
1437 $error = 0;
1438 $this->error = '';
1439
1440
1441 $this->db->begin();
1442
1443 // Stock control
1444 if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT)
1445 || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED))
1446 ) {
1447 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1448
1449 $langs->load("agenda");
1450
1451 // Loop on each product line to add a stock movement
1452 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1453 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1454 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1455 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1456 $sql .= " AND cd.rowid = ed.fk_elementdet";
1457
1458 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1459 $resql = $this->db->query($sql);
1460 if ($resql) {
1461 $cpt = $this->db->num_rows($resql);
1462 for ($i = 0; $i < $cpt; $i++) {
1463 dol_syslog(get_class($this)."::delete movement index ".$i);
1464 $obj = $this->db->fetch_object($resql);
1465
1466 $mouvS = new MouvementStock($this->db);
1467 // we do not log origin because it will be deleted
1468 $mouvS->origin = null;
1469
1470 $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
1471 if ($result < 0) {
1472 $error++;
1473 $this->error = $mouvS->error;
1474 $this->errors = $mouvS->errors;
1475 }
1476 }
1477 } else {
1478 $error++;
1479 $this->errors[] = "Error ".$this->db->lasterror();
1480 }
1481 }
1482
1483 if (!$error) {
1484 $sanitized_main = MAIN_DB_PREFIX.'receptiondet_batch';
1485 $sanitized_ef = $sanitized_main."_extrafields";
1486
1487 $sqlef = "DELETE FROM ".$sanitized_ef." WHERE fk_object IN (SELECT rowid FROM ".$sanitized_main." WHERE fk_reception = ".((int) $this->id).")";
1488
1489 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1490 $sql .= " WHERE fk_reception = ".((int) $this->id);
1491
1492 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1493 // Delete linked object
1494 $res = $this->deleteObjectLinked();
1495 if ($res < 0) {
1496 $error++;
1497 }
1498
1499 if (!$error) {
1500 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1501 $sql .= " WHERE rowid = ".((int) $this->id);
1502
1503 if ($this->db->query($sql)) {
1504 // Call trigger
1505 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1506 if ($result < 0) {
1507 $error++;
1508 }
1509 // End call triggers
1510
1511 if (!empty($this->origin) && $this->origin_id > 0) {
1512 $this->fetch_origin();
1514 '@phan-var-force CommandeFournisseur $origin_object';
1516 if ($origin_object->status == 4) { // If order source of reception is "partially received"
1517 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1518 $origin_object->loadReceptions();
1519 //var_dump($this->$origin->receptions);exit;
1520 if (count($origin_object->receptions) <= 0) {
1521 $origin_object->setStatut(3); // CommandeFournisseur ordered
1522 }
1523 }
1524 }
1525
1526 if (!$error) {
1527 $this->db->commit();
1528
1529 // We delete PDFs
1530 $ref = dol_sanitizeFileName($this->ref);
1531 if (!empty($conf->reception->dir_output)) {
1532 $dir = $conf->reception->dir_output.'/'.$ref;
1533 $file = $dir.'/'.$ref.'.pdf';
1534 if (file_exists($file)) {
1535 if (!dol_delete_file($file)) {
1536 return 0;
1537 }
1538 }
1539 if (file_exists($dir)) {
1540 if (!dol_delete_dir_recursive($dir)) {
1541 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1542 return 0;
1543 }
1544 }
1545 }
1546
1547 return 1;
1548 } else {
1549 $this->db->rollback();
1550 return -1;
1551 }
1552 } else {
1553 $this->error = $this->db->lasterror()." - sql=$sql";
1554 $this->db->rollback();
1555 return -3;
1556 }
1557 } else {
1558 $this->error = $this->db->lasterror()." - sql=$sql";
1559 $this->db->rollback();
1560 return -2;
1561 }
1562 } else {
1563 $this->error = $this->db->lasterror()." - sql=$sql";
1564 $this->db->rollback();
1565 return -1;
1566 }
1567 } else {
1568 $this->db->rollback();
1569 return -1;
1570 }
1571 }
1572
1580 public function deleteLine($user, $lineid)
1581 {
1582 if ($this->status != self::STATUS_DRAFT) {
1583 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1584 return -2;
1585 }
1586
1587 $line = new ReceptionLineBatch($this->db);
1588 if ($line->fetch($lineid) <= 0) {
1589 $this->error = 'ErrorRecordNotFound';
1590 return -1;
1591 }
1592 if ($line->fk_reception != $this->id) {
1593 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1594 return -1;
1595 }
1596
1597 $this->db->begin();
1598 if ($line->delete($user) > 0) {
1599 $this->db->commit();
1600 return 1;
1601 }
1602 $this->error = $line->error;
1603 $this->db->rollback();
1604 return -1;
1605 }
1606
1607 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1613 public function fetch_lines()
1614 {
1615 // phpcs:enable
1616 $this->lines = array();
1617
1618 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1619
1620 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1621 $sql .= " WHERE fk_reception = ".((int) $this->id);
1622
1623 $resql = $this->db->query($sql);
1624
1625 if (!empty($resql)) {
1626 while ($obj = $this->db->fetch_object($resql)) {
1627 $line = new CommandeFournisseurDispatch($this->db);
1628
1629 $line->fetch($obj->rowid);
1630
1631 // TODO Remove or keep this ?
1632 $line->fetch_product();
1633
1634 $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';
1635 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1636 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1637 $sql_commfourndet .= ' ORDER BY rang';
1638
1639 $resql_commfourndet = $this->db->query($sql_commfourndet);
1640 if (!empty($resql_commfourndet)) {
1641 $obj = $this->db->fetch_object($resql_commfourndet);
1642 $line->qty_asked = $obj->qty;
1643 $line->description = $obj->description;
1644 $line->desc = $obj->description;
1645 $line->tva_tx = $obj->tva_tx;
1646 $line->vat_src_code = $obj->vat_src_code;
1647 $line->localtax1_tx = $obj->localtax1_tx;
1648 $line->localtax2_tx = $obj->localtax2_tx;
1649 $line->subprice = $obj->subprice;
1650 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1651 $line->remise_percent = $obj->remise_percent;
1652 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1653 $line->ref_supplier = $obj->ref;
1654 $line->total_ht = $obj->total_ht;
1655 $line->total_ttc = $obj->total_ttc;
1656 $line->total_tva = $obj->total_tva;
1657 $line->date_start = $this->db->jdate($obj->date_start);
1658 $line->date_end = $this->db->jdate($obj->date_end);
1659 $line->product_type = $obj->product_type;
1660 } else {
1661 $line->qty_asked = 0;
1662 $line->description = '';
1663 $line->desc = '';
1664 $line->label = $obj->label;
1665 }
1666
1667 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1668 $tva = $pu_ht * $line->tva_tx / 100;
1669 $this->total_ht += $pu_ht;
1670 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1671
1672 $this->total_ttc += $pu_ht + $tva;
1673
1674 if (isModEnabled('productbatch') && !empty($line->batch)) {
1675 $detail_batch = new stdClass();
1676 $detail_batch->eatby = $line->eatby;
1677 $detail_batch->sellby = $line->sellby;
1678 $detail_batch->batch = $line->batch;
1679 $detail_batch->qty = $line->qty;
1680
1681 $line->detail_batch[] = $detail_batch;
1682 }
1683
1684 $this->lines[] = $line;
1685 }
1686
1687 return 1;
1688 } else {
1689 return -1;
1690 }
1691 }
1692
1703 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0)
1704 {
1705 global $langs, $hookmanager;
1706
1707 $result = '';
1708 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1709 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1710 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1711
1712 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1713
1714 if ($short) {
1715 return $url;
1716 }
1717
1718 $linkclose = '';
1719 if (empty($notooltip)) {
1720 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1721 $label = $langs->trans("Reception");
1722 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1723 }
1724 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1725 $linkclose .= ' class="classfortooltip"';
1726 }
1727
1728 $linkstart = '<a href="'.$url.'"';
1729 $linkstart .= $linkclose.'>';
1730 $linkend = '</a>';
1731
1732 $result .= $linkstart;
1733 if ($withpicto) {
1734 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1735 }
1736 if ($withpicto != 2) {
1737 $result .= $this->ref;
1738 }
1739
1740 $result .= $linkend;
1741
1742 global $action;
1743 $hookmanager->initHooks(array($this->element . 'dao'));
1744 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1745 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1746 if ($reshook > 0) {
1747 $result = $hookmanager->resPrint;
1748 } else {
1749 $result .= $hookmanager->resPrint;
1750 }
1751 return $result;
1752 }
1753
1760 public function getLibStatut($mode = 0)
1761 {
1762 return $this->LibStatut($this->statut, $mode);
1763 }
1764
1765 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1773 public function LibStatut($status, $mode)
1774 {
1775 // phpcs:enable
1776 global $langs;
1777
1778 // List of long language codes for status
1779 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1780 $this->labelStatus[0] = 'StatusReceptionDraft';
1781 // product to receive if stock increase is on close or already received if stock increase is on validation
1782 $this->labelStatus[1] = 'StatusReceptionValidated';
1783 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1784 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1785 }
1786 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1787 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1788 }
1789 $this->labelStatus[2] = 'StatusReceptionProcessed';
1790
1791 // List of short language codes for status
1792 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1793 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1794 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1795 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1796
1797 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1798 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1799
1800 $statusType = 'status'.$status;
1801 if ($status == self::STATUS_VALIDATED) {
1802 $statusType = 'status4';
1803 }
1804 if ($status == self::STATUS_CLOSED) {
1805 $statusType = 'status6';
1806 }
1807
1808 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1809 }
1810
1818 public function getKanbanView($option = '', $arraydata = null)
1819 {
1820 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1821
1822 $return = '<div class="box-flex-item box-flex-grow-zero">';
1823 $return .= '<div class="info-box info-box-sm">';
1824 $return .= '<div class="info-box-icon bg-infobox-action">';
1825 $return .= img_picto('', 'order');
1826 $return .= '</div>';
1827 $return .= '<div class="info-box-content">';
1828 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1829 if ($selected >= 0) {
1830 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1831 }
1832 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1833 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1834 }
1835 /*if (property_exists($this, 'total_ht')) {
1836 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency()).' '.$langs->trans('HT').'</div>';
1837 }*/
1838 if (method_exists($this, 'getLibStatut')) {
1839 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1840 }
1841 $return .= '</div>';
1842 $return .= '</div>';
1843 $return .= '</div>';
1844
1845 return $return;
1846 }
1847
1855 public function initAsSpecimen()
1856 {
1857 global $langs;
1858
1859 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1860 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1861 $now = dol_now();
1862
1863 dol_syslog(get_class($this)."::initAsSpecimen");
1864
1865 $order = new CommandeFournisseur($this->db);
1866 $order->initAsSpecimen();
1867
1868 // Initialise parameters
1869 $this->id = 0;
1870 $this->ref = 'SPECIMEN';
1871 $this->specimen = 1;
1872 $this->statut = 1;
1873 $this->status = 1;
1874 $this->date = $now;
1875 $this->date_creation = $now;
1876 $this->date_valid = $now;
1877 $this->date_delivery = $now;
1878 $this->date_reception = $now + 24 * 3600;
1879 $this->entrepot_id = 0;
1880 $this->socid = 1;
1881 $this->origin_id = 1;
1882 $this->origin_type = 'supplier_order';
1883 $this->origin_object = $order;
1884 $this->note_private = 'Private note';
1885 $this->note_public = 'Public note';
1886 $this->tracking_number = 'TRACKID-ABC123';
1887 $this->fk_incoterms = 1;
1888 $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)
1889 $xnbp = 0;
1890 while ($xnbp < $nbp) {
1891 $line = new CommandeFournisseurDispatch($this->db);
1892 $line->desc = $langs->trans("Description")." ".$xnbp;
1893 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1894 $line->label = $langs->trans("Description")." ".$xnbp;
1895 $line->qty = 10;
1896
1897 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1898
1899 $this->lines[] = $line;
1900 $xnbp++;
1901 }
1902
1903 return 1;
1904 }
1905
1913 public function setDeliveryDate($user, $delivery_date)
1914 {
1915 // phpcs:enable
1916 if ($user->hasRight('reception', 'creer')) {
1917 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1918 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1919 $sql .= " WHERE rowid = ".((int) $this->id);
1920
1921 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1922 $resql = $this->db->query($sql);
1923 if ($resql) {
1924 $this->date_delivery = $delivery_date;
1925 return 1;
1926 } else {
1927 $this->error = $this->db->error();
1928 return -1;
1929 }
1930 } else {
1931 return -2;
1932 }
1933 }
1934
1942 public function setReceptionDate($user, $reception_date)
1943 {
1944 if ($user->hasRight('reception', 'creer')) {
1945 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1946 $sql .= " SET date_reception = ".($reception_date ? "'".$this->db->idate($reception_date)."'" : 'null');
1947 $sql .= " WHERE rowid = ".((int) $this->id);
1948
1949 dol_syslog(get_class($this)."::setReceptionDate", LOG_DEBUG);
1950 $resql = $this->db->query($sql);
1951 if ($resql) {
1952 $this->date_reception = $reception_date;
1953 return 1;
1954 } else {
1955 $this->error = $this->db->error();
1956 return -1;
1957 }
1958 } else {
1959 return -2;
1960 }
1961 }
1962
1963 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1969 public function fetch_delivery_methods()
1970 {
1971 // phpcs:enable
1972 global $langs;
1973 $this->meths = array();
1974
1975 $sql = "SELECT em.rowid, em.code, em.libelle";
1976 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1977 $sql .= " WHERE em.active = 1";
1978 $sql .= " ORDER BY em.libelle ASC";
1979
1980 $resql = $this->db->query($sql);
1981 if ($resql) {
1982 while ($obj = $this->db->fetch_object($resql)) {
1983 $label = $langs->trans('ReceptionMethod'.$obj->code);
1984 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1985 }
1986 }
1987 }
1988
1989 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1996 public function list_delivery_methods($id = 0)
1997 {
1998 // phpcs:enable
1999 global $langs;
2000
2001 $this->listmeths = array();
2002 $i = 0;
2003
2004 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
2005 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2006 if (!empty($id)) {
2007 $sql .= " WHERE em.rowid = ".((int) $id);
2008 }
2009
2010 $resql = $this->db->query($sql);
2011 if ($resql) {
2012 while ($obj = $this->db->fetch_object($resql)) {
2013 $this->listmeths[$i]['rowid'] = $obj->rowid;
2014 $this->listmeths[$i]['code'] = $obj->code;
2015 $label = $langs->trans('ReceptionMethod'.$obj->code);
2016 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
2017 $this->listmeths[$i]['description'] = $obj->description;
2018 $this->listmeths[$i]['tracking'] = $obj->tracking;
2019 $this->listmeths[$i]['active'] = $obj->active;
2020 $i++;
2021 }
2022 }
2023 }
2024
2031 public function getUrlTrackingStatus($value = '')
2032 {
2033 if (!empty($this->shipping_method_id)) {
2034 $sql = "SELECT em.code, em.tracking";
2035 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2036 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
2037
2038 $resql = $this->db->query($sql);
2039 if ($resql) {
2040 if ($obj = $this->db->fetch_object($resql)) {
2041 $tracking = $obj->tracking;
2042 }
2043 }
2044 }
2045
2046 if (!empty($tracking) && !empty($value)) {
2047 $url = str_replace('{TRACKID}', $value, $tracking);
2048 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
2049 } else {
2050 $this->tracking_url = $value;
2051 }
2052 }
2053
2059 public function setClosed()
2060 {
2061 global $conf, $langs, $user;
2062
2063 $error = 0;
2064
2065 // Protection. This avoid to move stock later when we should not
2066 if ($this->statut == Reception::STATUS_CLOSED) {
2067 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
2068 return 0;
2069 }
2070
2071 $this->db->begin();
2072
2073 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
2074 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2075
2076 $resql = $this->db->query($sql);
2077 if ($resql) {
2078 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
2079 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
2080 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2081
2082 $order = new CommandeFournisseur($this->db);
2083 $order->fetch($this->origin_id);
2084
2085 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
2086
2087 $receptions_match_order = 1;
2088 foreach ($order->lines as $line) {
2089 $lineid = $line->id;
2090 $qty = $line->qty;
2091 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
2092 $receptions_match_order = 0;
2093 $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';
2094 dol_syslog($text);
2095 break;
2096 }
2097 }
2098 if ($receptions_match_order) {
2099 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');
2100 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
2101 }
2102 }
2103
2104 $this->statut = self::STATUS_CLOSED;
2105 $this->status = self::STATUS_CLOSED;
2106
2107 // If stock increment is done on closing
2108 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2109 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2110
2111 $langs->load("agenda");
2112
2113 // Loop on each product line to add a stock movement
2114 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
2115 $sql = "SELECT cd.fk_product, cd.subprice,";
2116 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2117 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2118 $sql .= " ed.fk_elementdet, ed.cost_price";
2119 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2120 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2121 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2122 $sql .= " AND cd.rowid = ed.fk_elementdet";
2123
2124 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2125 $resql = $this->db->query($sql);
2126
2127 if ($resql) {
2128 $cpt = $this->db->num_rows($resql);
2129 for ($i = 0; $i < $cpt; $i++) {
2130 $obj = $this->db->fetch_object($resql);
2131
2132 $qty = $obj->qty;
2133
2134 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2135 continue;
2136 }
2137
2138 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
2139
2140 $mouvS = new MouvementStock($this->db);
2141 $mouvS->origin = &$this;
2142 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2143
2144 if (empty($obj->batch)) {
2145 // line without batch detail
2146
2147 // 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
2148 $inventorycode = '';
2149 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2150 if ($result < 0) {
2151 $this->error = $mouvS->error;
2152 $this->errors = $mouvS->errors;
2153 $error++;
2154 break;
2155 }
2156 } else {
2157 // line with batch detail
2158
2159 // 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
2160 $inventorycode = '';
2161 $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);
2162
2163 if ($result < 0) {
2164 $this->error = $mouvS->error;
2165 $this->errors = $mouvS->errors;
2166 $error++;
2167 break;
2168 }
2169 }
2170 }
2171 } else {
2172 $this->error = $this->db->lasterror();
2173 $error++;
2174 }
2175 }
2176
2177 // Call trigger
2178 if (!$error) {
2179 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
2180 if ($result < 0) {
2181 $error++;
2182 }
2183 }
2184 } else {
2185 dol_print_error($this->db);
2186 $error++;
2187 }
2188
2189 if (!$error) {
2190 $this->db->commit();
2191 return 1;
2192 } else {
2193 $this->statut = self::STATUS_VALIDATED;
2194 $this->status = self::STATUS_VALIDATED;
2195 $this->db->rollback();
2196 return -1;
2197 }
2198 }
2199
2205 public function setBilled()
2206 {
2207 global $user;
2208 $error = 0;
2209
2210 $this->db->begin();
2211
2212 if ($this->statut == Reception::STATUS_VALIDATED) {
2213 // do not close if already closed
2214 $this->setClosed();
2215 }
2216
2217 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
2218 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2219
2220 $resql = $this->db->query($sql);
2221 if ($resql) {
2222 $this->billed = 1;
2223
2224 // Call trigger
2225 $result = $this->call_trigger('RECEPTION_BILLED', $user);
2226 if ($result < 0) {
2227 $this->billed = 0;
2228 $error++;
2229 }
2230 } else {
2231 $error++;
2232 $this->errors[] = $this->db->lasterror;
2233 }
2234
2235 if (empty($error)) {
2236 $this->db->commit();
2237 return 1;
2238 } else {
2239 $this->db->rollback();
2240 return -1;
2241 }
2242 }
2243
2249 public function reOpen()
2250 {
2251 global $langs, $user;
2252
2253 $error = 0;
2254
2255 $this->db->begin();
2256
2257 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
2258 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2259
2260 $resql = $this->db->query($sql);
2261 $rollbackStatus = $this->status;
2262 $rollbackBilled = $this->billed;
2263 if ($resql) {
2264 $this->statut = self::STATUS_VALIDATED;
2265 $this->status = self::STATUS_VALIDATED;
2266 $this->billed = 0;
2267
2268 // If stock increment is done on closing
2269 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2270 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2271 $numref = (string) $this->ref;
2272 $langs->load("agenda");
2273
2274 // Loop on each product line to add a stock movement
2275 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2276 $sql = "SELECT ed.fk_product, cd.subprice,";
2277 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2278 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2279 $sql .= " ed.fk_elementdet, ed.cost_price";
2280 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2281 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2282 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2283 $sql .= " AND cd.rowid = ed.fk_elementdet";
2284
2285 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2286 $resql = $this->db->query($sql);
2287 if ($resql) {
2288 $cpt = $this->db->num_rows($resql);
2289 for ($i = 0; $i < $cpt; $i++) {
2290 $obj = $this->db->fetch_object($resql);
2291
2292 $qty = $obj->qty;
2293
2294 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2295 continue;
2296 }
2297 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2298
2299 //var_dump($this->lines[$i]);
2300 $mouvS = new MouvementStock($this->db);
2301 $mouvS->origin = &$this;
2302 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2303
2304 if (empty($obj->batch)) {
2305 // line without batch detail
2306
2307 // 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
2308 $inventorycode = '';
2309 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
2310
2311 if ($result < 0) {
2312 $this->error = $mouvS->error;
2313 $this->errors = $mouvS->errors;
2314 $error++;
2315 break;
2316 }
2317 } else {
2318 // line with batch detail
2319
2320 // 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
2321 $inventorycode = '';
2322 $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);
2323 if ($result < 0) {
2324 $this->error = $mouvS->error;
2325 $this->errors = $mouvS->errors;
2326 $error++;
2327 break;
2328 }
2329 }
2330 }
2331 } else {
2332 $this->error = $this->db->lasterror();
2333 $error++;
2334 }
2335 }
2336
2337 if (!$error) {
2338 // Call trigger
2339 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
2340 if ($result < 0) {
2341 $error++;
2342 }
2343 }
2344
2345 if (!$error && $this->origin == 'order_supplier') {
2346 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2347
2348 $commande = new CommandeFournisseur($this->db);
2349 $commande->fetch($this->origin_id);
2350 $result = $commande->setStatus($user, 4);
2351 if ($result < 0) {
2352 $error++;
2353 $this->error = $commande->error;
2354 $this->errors = $commande->errors;
2355 }
2356 }
2357 } else {
2358 $error++;
2359 $this->errors[] = $this->db->lasterror();
2360 }
2361
2362 if (!$error) {
2363 $this->db->commit();
2364 return 1;
2365 } else {
2366 $this->statut = $this->status = $rollbackStatus;
2367 $this->billed = $rollbackBilled;
2368 $this->db->rollback();
2369 return -1;
2370 }
2371 }
2372
2379 public function setDraft($user)
2380 {
2381 // phpcs:enable
2382 global $langs;
2383
2384 $error = 0;
2385
2386 // Protection
2387 if ($this->statut <= self::STATUS_DRAFT) {
2388 return 0;
2389 }
2390
2391 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
2392 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
2393 $this->error = 'Permission denied';
2394 return -1;
2395 }
2396
2397 $this->db->begin();
2398
2399 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2400 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2401 $sql .= " WHERE rowid = ".((int) $this->id);
2402
2403 dol_syslog(__METHOD__, LOG_DEBUG);
2404 if ($this->db->query($sql)) {
2405 // If stock increment is done on closing
2406 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2407 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2408
2409 $langs->load("agenda");
2410
2411 // Loop on each product line to add a stock movement
2412 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2413 $sql = "SELECT cd.fk_product, cd.subprice,";
2414 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2415 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2416 $sql .= " ed.fk_elementdet, ed.cost_price";
2417 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2418 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2419 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2420 $sql .= " AND cd.rowid = ed.fk_elementdet";
2421
2422 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2423 $resql = $this->db->query($sql);
2424 if ($resql) {
2425 $cpt = $this->db->num_rows($resql);
2426 for ($i = 0; $i < $cpt; $i++) {
2427 $obj = $this->db->fetch_object($resql);
2428
2429 $qty = $obj->qty;
2430
2431 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2432 continue;
2433 }
2434
2435 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2436
2437 //var_dump($this->lines[$i]);
2438 $mouvS = new MouvementStock($this->db);
2439 $mouvS->origin = &$this;
2440 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2441
2442 if (empty($obj->batch)) {
2443 // line without batch detail
2444
2445 // 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
2446 $inventorycode = '';
2447 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2448 if ($result < 0) {
2449 $this->error = $mouvS->error;
2450 $this->errors = $mouvS->errors;
2451 $error++;
2452 break;
2453 }
2454 } else {
2455 // line with batch detail
2456
2457 // 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
2458 $inventorycode = '';
2459 $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);
2460 if ($result < 0) {
2461 $this->error = $mouvS->error;
2462 $this->errors = $mouvS->errors;
2463 $error++;
2464 break;
2465 }
2466 }
2467 }
2468 } else {
2469 $this->error = $this->db->lasterror();
2470 $error++;
2471 }
2472 }
2473
2474 if (!$error) {
2475 // Call trigger
2476 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2477 if ($result < 0) {
2478 $error++;
2479 }
2480 }
2481 if ($this->origin == 'order_supplier') {
2482 if (!empty($this->origin) && $this->origin_id > 0) {
2483 $this->fetch_origin();
2484 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2485 // Check if there is no more reception validated.
2486 $this->origin_object->fetchObjectLinked();
2487 $setStatut = 1;
2488 if (!empty($this->origin_object->linkedObjects['reception'])) {
2489 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2490 if ($rcption->statut > 0) {
2491 $setStatut = 0;
2492 break;
2493 }
2494 }
2495 //var_dump($this->$origin->receptions);exit;
2496 if ($setStatut) {
2497 $this->origin_object->setStatut(3); // CommandeFournisseur ordered
2498 }
2499 }
2500 }
2501 }
2502 }
2503
2504 if (!$error) {
2505 $this->statut = self::STATUS_DRAFT;
2506 $this->status = self::STATUS_DRAFT;
2507 $this->db->commit();
2508 return 1;
2509 } else {
2510 $this->db->rollback();
2511 return -1;
2512 }
2513 } else {
2514 $this->error = $this->db->error();
2515 $this->db->rollback();
2516 return -1;
2517 }
2518 }
2519
2530 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2531 {
2532 global $conf, $langs;
2533
2534 $langs->load("receptions");
2535
2536 if (!dol_strlen($modele)) {
2537 $modele = 'squille';
2538
2539 if ($this->model_pdf) {
2540 $modele = $this->model_pdf;
2541 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2542 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2543 }
2544 }
2545
2546 $modelpath = "core/modules/reception/doc/";
2547
2548 $this->fetch_origin();
2549
2550 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2551 }
2552
2561 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2562 {
2563 $tables = array('reception');
2564
2565 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2566 }
2567
2576 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2577 {
2578 $tables = array(
2579 'receptiondet_batch'
2580 );
2581
2582 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2583 }
2584}
$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:169
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)
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.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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:492
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:141