dolibarr 25.0.0-alpha
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-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 * Copyright (C) 2026 Jose MARTINEZ <jose.martinez@pichinov.com>
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <https://www.gnu.org/licenses/>.
32 */
33
40require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
41require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
42require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
43require_once DOL_DOCUMENT_ROOT.'/reception/class/receptionlinebatch.class.php';
44if (isModEnabled("propal")) {
45 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
46}
47if (isModEnabled('order')) {
48 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
49}
50
51
56{
57 use CommonIncoterm;
58
63 public $TRIGGER_PREFIX = 'RECEPTION'; // to be overridden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
64
68 public $code = "";
69
73 public $element = "reception";
74
78 public $fk_element = "fk_reception";
79
83 public $table_element = "reception";
84
88 public $table_element_line = "receptiondet_batch";
89
93 public $picto = 'dollyrevert';
94
98 public $socid;
102 public $ref_supplier;
103
107 public $entrepot_id;
111 public $tracking_number;
115 public $tracking_url;
119 public $billed;
120
124 public $weight;
128 public $trueWeight;
132 public $weight_units;
136 public $trueWidth;
140 public $width_units;
144 public $trueHeight;
148 public $height_units;
152 public $trueDepth;
156 public $depth_units;
160 public $trueSize;
164 public $size_units;
168 public $user_author_id;
169
173 public $date_delivery;
174
180 public $date;
181
185 public $date_reception;
186
190 public $date_valid;
191
195 public $meths;
199 public $listmeths; // List of carriers
200
204 public $lines = array();
205
206
211 public $detail_batch;
212
213 const STATUS_CANCELED = -1;
214 const STATUS_DRAFT = 0;
215 const STATUS_VALIDATED = 1;
216 const STATUS_CLOSED = 2;
217
218
224 public function __construct($db)
225 {
226 $this->db = $db;
227
228 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
229 $this->isextrafieldmanaged = 1;
230 }
231
238 public function getNextNumRef($soc)
239 {
240 global $langs, $conf;
241 $langs->load("receptions");
242
243 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
244 $mybool = false;
245
246 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
247 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
248
249 // Include file with class
250 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
251
252 foreach ($dirmodels as $reldir) {
253 $dir = dol_buildpath($reldir."core/modules/reception/");
254
255 // Load file with numbering class (if found)
256 $mybool = ((bool) @include_once $dir.$file) || $mybool;
257 }
258
259 if (!$mybool) {
260 dol_print_error(null, "Failed to include file ".$file);
261 return '';
262 }
263
264 $obj = new $classname();
265 '@phan-var-force ModelNumRefReception $obj';
268 $numref = $obj->getNextValue($soc, $this);
269
270 if ($numref != "") {
271 return $numref;
272 } else {
273 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
274 return "";
275 }
276 } else {
277 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
278 return "";
279 }
280 }
281
289 public function create($user, $notrigger = 0)
290 {
291 global $conf;
292
293 $now = dol_now();
294
295 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
296 $error = 0;
297
298 // Clean parameters
299 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
300 if (empty($this->fk_project)) {
301 $this->fk_project = 0;
302 }
303 if (empty($this->weight_units)) {
304 $this->weight_units = 0;
305 }
306 if (empty($this->size_units)) {
307 $this->size_units = 0;
308 }
309 if (empty($this->date_creation)) {
310 $this->date_creation = $now;
311 }
312
313 $this->entity = setEntity($this);
314
315 $this->user = $user;
316
317 $this->db->begin();
318
319 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
320 $sql .= "ref";
321 $sql .= ", entity";
322 $sql .= ", ref_supplier";
323 $sql .= ", date_creation";
324 $sql .= ", fk_user_author";
325 $sql .= ", date_reception";
326 $sql .= ", date_delivery";
327 $sql .= ", fk_soc";
328 $sql .= ", fk_projet";
329 $sql .= ", fk_shipping_method";
330 $sql .= ", tracking_number";
331 $sql .= ", weight";
332 $sql .= ", size";
333 $sql .= ", width";
334 $sql .= ", height";
335 $sql .= ", weight_units";
336 $sql .= ", size_units";
337 $sql .= ", note_private";
338 $sql .= ", note_public";
339 $sql .= ", model_pdf";
340 $sql .= ", fk_incoterms, location_incoterms";
341 $sql .= ") VALUES (";
342 $sql .= "'(PROV)'";
343 $sql .= ", ".((int) $this->entity);
344 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
345 $sql .= ", '".$this->db->idate($this->date_creation)."'";
346 $sql .= ", ".((int) $user->id);
347 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
348 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
349 $sql .= ", ".($this->socid > 0 ? ((int) $this->socid) : "null");
350 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
351 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
352 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
353 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
354 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
355 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
356 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
357 $sql .= ", ".((int) $this->weight_units);
358 $sql .= ", ".((int) $this->size_units);
359 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
360 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
361 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
362 $sql .= ", ".(int) $this->fk_incoterms;
363 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
364 $sql .= ")";
365
366 dol_syslog(get_class($this)."::create", LOG_DEBUG);
367
368 $resql = $this->db->query($sql);
369
370 if ($resql) {
371 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
372
373 // update ref
374 $initialref = '(PROV'.$this->id.')';
375 if (!empty($this->ref)) {
376 $initialref = $this->ref;
377 }
378
379 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
380 $sql .= " SET ref = '".$this->db->escape($initialref)."'";
381 $sql .= " WHERE rowid = ".((int) $this->id);
382
383 dol_syslog(get_class($this)."::create", LOG_DEBUG);
384 if ($this->db->query($sql)) {
385 // Insert of lines
386 $num = count($this->lines);
387 for ($i = 0; $i < $num; $i++) {
388 $this->lines[$i]->fk_reception = $this->id;
389
390 if ($this->lines[$i]->create($user) <= 0) {
391 $this->setErrorsFromObject($this->lines[$i]);
392 $error++;
393 }
394 }
395
396 if (!$error && $this->id && $this->origin_id) {
397 $ret = $this->add_object_linked();
398 if (!$ret) {
399 $error++;
400 }
401 }
402
403 // Create extrafields
404 if (!$error) {
405 $result = $this->insertExtraFields();
406 if ($result < 0) {
407 $error++;
408 }
409 }
410
411 if (!$error && !$notrigger) {
412 // Call trigger
413 $result = $this->call_trigger('RECEPTION_CREATE', $user);
414 if ($result < 0) {
415 $error++;
416 }
417 // End call triggers
418 }
419
420 if (!$error) {
421 $this->db->commit();
422 return $this->id;
423 } else {
424 foreach ($this->errors as $errmsg) {
425 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
426 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
427 }
428 $this->db->rollback();
429 return -1 * $error;
430 }
431 } else {
432 $error++;
433 $this->error = $this->db->lasterror()." - sql=$sql";
434 $this->db->rollback();
435 return -2;
436 }
437 } else {
438 $error++;
439 $this->error = $this->db->error()." - sql=$sql";
440 $this->db->rollback();
441 return -1;
442 }
443 }
444
445 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
458 public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [], $parent_line_id = 0, $product_id = 0)
459 {
460 //phpcs:enable
461 global $user;
462
463 $receptionline = new ReceptionLineBatch($this->db);
464 $receptionline->fk_reception = $this->id;
465 $receptionline->entrepot_id = $entrepot_id;
466 $receptionline->fk_elementdet = $origin_line_id;
467 $receptionline->element_type = $this->origin;
468 $receptionline->fk_parent = $parent_line_id;
469 $receptionline->fk_product = $product_id;
470 $receptionline->qty = $qty;
471 $receptionline->rang = $rang;
472 $receptionline->array_options = $array_options;
473
474 if (!($receptionline->fk_product > 0)) {
475 $order_line = new CommandeFournisseurLigne($this->db);
476 $order_line->fetch($receptionline->fk_elementdet);
477 $receptionline->fk_product = $order_line->fk_product;
478 }
479
480 if (($lineId = $receptionline->insert($user)) < 0) {
481 $this->errors[] = $receptionline->error;
482 }
483 return $lineId;
484 }
485
494 public function fetch($id, $ref = '', $ref_ext = '')
495 {
496 // Check parameters
497 if (empty($id) && empty($ref) && empty($ref_ext)) {
498 return -1;
499 }
500
501 $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";
502 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
503 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery, e.date_valid";
504 $sql .= ", e.fk_shipping_method, e.tracking_number";
505 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
506 $sql .= ", e.note_private, e.note_public";
507 $sql .= ', e.fk_incoterms, e.location_incoterms';
508 $sql .= ', i.libelle as label_incoterms';
509 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
510 $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'";
511 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
512
513 if ($id) {
514 $sql .= " WHERE e.rowid = ".((int) $id);
515 } else {
516 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
517 if ($ref) {
518 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
519 } elseif ($ref_ext) {
520 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
521 }
522 }
523
524 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
525 $result = $this->db->query($sql);
526 if ($result) {
527 if ($this->db->num_rows($result)) {
528 $obj = $this->db->fetch_object($result);
529
530 $this->id = $obj->rowid;
531 $this->entity = $obj->entity;
532 $this->ref = $obj->ref;
533 $this->socid = $obj->socid;
534 $this->ref_supplier = $obj->ref_supplier;
535 $this->ref_ext = $obj->ref_ext;
536 $this->statut = $obj->status;
537 $this->status = $obj->status;
538 $this->billed = $obj->billed;
539 $this->fk_project = $obj->fk_project;
540 $this->user_author_id = $obj->fk_user_author;
541 $this->date_creation = $this->db->jdate($obj->date_creation);
542 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
543 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
544 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
545 $this->date_valid = $this->db->jdate($obj->date_valid); // Date validation
546 $this->model_pdf = $obj->model_pdf;
547 $this->shipping_method_id = $obj->fk_shipping_method;
548 $this->tracking_number = $obj->tracking_number;
549 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
550 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
551 $this->origin_id = $obj->origin_id;
552
553 $this->trueWeight = $obj->weight;
554 $this->weight_units = $obj->weight_units;
555
556 $this->trueWidth = $obj->width;
557 $this->width_units = $obj->size_units;
558 $this->trueHeight = $obj->height;
559 $this->height_units = $obj->size_units;
560 $this->trueDepth = $obj->size;
561 $this->depth_units = $obj->size_units;
562
563 $this->note_public = $obj->note_public;
564 $this->note_private = $obj->note_private;
565
566 // A denormalized value
567 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
568 $this->size_units = $obj->size_units;
569
570 //Incoterms
571 $this->fk_incoterms = $obj->fk_incoterms;
572 $this->location_incoterms = $obj->location_incoterms;
573 $this->label_incoterms = $obj->label_incoterms;
574
575 $this->db->free($result);
576
577 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
578 //$this->pdf_filename = $file;
579
580 // Tracking url
581 $this->getUrlTrackingStatus($obj->tracking_number);
582
583 /*
584 * Thirdparty
585 */
586 $result = $this->fetch_thirdparty();
587
588
589 // Retrieve all extrafields for reception
590 // fetch optionals attributes and labels
591 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
592 $extrafields = new ExtraFields($this->db);
593 $extrafields->fetch_name_optionals_label($this->table_element, true);
594 $this->fetch_optionals();
595
596 /*
597 * Lines
598 */
599 if (empty($obj->origin_id)) {
600 $result = $this->fetch_lines_free();
601 } else {
602 $result = $this->fetch_lines();
603 }
604 if ($result < 0) {
605 return -3;
606 }
607
608 return 1;
609 } else {
610 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
611 $this->error = 'Reception with id '.$id.' not found';
612 return 0;
613 }
614 } else {
615 $this->error = $this->db->error();
616 return -1;
617 }
618 }
619
627 public function valid($user, $notrigger = 0)
628 {
629 global $conf, $langs;
630
631 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
632
633 dol_syslog(get_class($this)."::valid");
634
635 // Protection
636 if ($this->statut) {
637 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
638 return 0;
639 }
640
641 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
642 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
643 $this->error = 'Permission denied';
644 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
645 return -1;
646 }
647
648 $this->db->begin();
649
650 $error = 0;
651
652 // Define new ref
653 $soc = new Societe($this->db);
654 $soc->fetch($this->socid);
655
656
657 // Define new ref
658 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
659 $numref = $this->getNextNumRef($soc);
660 } else {
661 $numref = (string) $this->ref;
662 }
663
664 $this->newref = dol_sanitizeFileName($numref);
665
666 $now = dol_now();
667
668 // Validate
669 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
670 $sql .= " ref='".$this->db->escape($numref)."'";
671 $sql .= ", fk_statut = 1";
672 $sql .= ", date_valid = '".$this->db->idate($now)."'";
673 $sql .= ", fk_user_valid = ".((int) $user->id);
674 $sql .= " WHERE rowid = ".((int) $this->id);
675 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
676 $resql = $this->db->query($sql);
677 if (!$resql) {
678 $this->error = $this->db->lasterror();
679 $error++;
680 }
681
682 // If stock increment is done on reception (recommended choice)
683 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
684 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
685
686 $langs->load("agenda");
687
688 // Loop on each product line to add a stock movement
689 // TODO in future, reception lines may not be linked to order line
690 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice, cd.remise_percent,";
691 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
692 $sql .= " ed.eatby, ed.sellby, ed.batch,";
693 $sql .= " ed.fk_elementdet, ed.cost_price";
694 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
695 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
696 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
697 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
698 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
699
700 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
701 $resql = $this->db->query($sql);
702 if ($resql) {
703 $cpt = $this->db->num_rows($resql);
704 for ($i = 0; $i < $cpt; $i++) {
705 $obj = $this->db->fetch_object($resql);
706
707 $qty = $obj->qty;
708
709 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
710 continue;
711 }
712
713 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
714
715 //var_dump($this->lines[$i]);
716 $mouvS = new MouvementStock($this->db);
717 $mouvS->origin = &$this;
718 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
719
720 if (empty($obj->batch)) {
721 // line without batch detail
722
723 // 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.
724 $inventorycode = '';
725 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
726
727 if (intval($result) < 0) {
728 $error++;
729 $this->setErrorsFromObject($mouvS);
730 break;
731 }
732 } else {
733 // line with batch detail
734
735 // 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.
736 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
737 $inventorycode = '';
738 $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);
739
740 if (intval($result) < 0) {
741 $error++;
742 $this->setErrorsFromObject($mouvS);
743 break;
744 }
745 }
746 }
747 } else {
748 $this->db->rollback();
749 $this->error = $this->db->error();
750 return -2;
751 }
752 }
753
754 if (!$error && $this->origin_id > 0) {
755 // Change status of purchase order to "reception in process" or "totally received"
756 $status = $this->getStatusDispatch();
757 if ($status < 0) {
758 $error++;
759 } else {
760 $trigger_key = '';
761 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
762 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
763 if ($ret < 0) {
764 $error++;
765 $this->errors = array_merge($this->errors, $this->origin_object->errors);
766 }
767 } else {
768 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
769 if ($ret < 0) {
770 $error++;
771 }
772 }
773 }
774 }
775
776 if (!$error && !$notrigger) {
777 // Call trigger
778 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
779 if ($result < 0) {
780 $error++;
781 }
782 // End call triggers
783 }
784
785 if (!$error) {
786 $this->oldref = $this->ref;
787
788 // Rename directory if dir was a temporary ref
789 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
790 // Now we rename also files into index
791 $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)."'";
792 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
793 $resql = $this->db->query($sql);
794 if (!$resql) {
795 $error++;
796 $this->error = $this->db->lasterror();
797 }
798 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
799 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
800 $resql = $this->db->query($sql);
801 if (!$resql) {
802 $error++;
803 $this->error = $this->db->lasterror();
804 }
805
806 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
807 $oldref = dol_sanitizeFileName($this->ref);
808 $newref = dol_sanitizeFileName($numref);
809 $dirsource = $conf->reception->dir_output.'/'.$oldref;
810 $dirdest = $conf->reception->dir_output.'/'.$newref;
811 if (!$error && file_exists($dirsource)) {
812 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
813
814 if (@rename($dirsource, $dirdest)) {
815 dol_syslog("Rename ok");
816 // Rename docs starting with $oldref with $newref
817 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
818 foreach ($listoffiles as $fileentry) {
819 $dirsource = $fileentry['name'];
820 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
821 $dirsource = $fileentry['path'].'/'.$dirsource;
822 $dirdest = $fileentry['path'].'/'.$dirdest;
823 @rename($dirsource, $dirdest);
824 }
825 }
826 }
827 }
828 }
829
830 // Set new ref and current status
831 if (!$error) {
832 $this->ref = $numref;
833 $this->statut = self::STATUS_VALIDATED;
834 $this->status = self::STATUS_VALIDATED;
835 }
836
837 if (!$error) {
838 $this->db->commit();
839 return 1;
840 } else {
841 foreach ($this->errors as $errmsg) {
842 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
843 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
844 }
845 $this->db->rollback();
846 return -1 * $error;
847 }
848 }
849
855 public function getStatusDispatch()
856 {
857 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
858 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
859
861
862 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
863 if (empty($this->origin_object)) {
864 $this->fetch_origin();
865 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
866 $res = $this->origin_object->fetch_lines();
867 $this->commandeFournisseur = null; // deprecated
868 if ($res < 0) {
869 return $res;
870 }
871 } elseif ($this->origin_object instanceof CommandeFournisseur && empty($this->origin_object->lines)) {
872 $res = $this->origin_object->fetch_lines();
873 $this->commandeFournisseur = $this->origin_object; // deprecated
874 if ($res < 0) {
875 return $res;
876 }
877 }
878 }
879
880 $qty_received = array();
881 $qty_wished = array();
882
883 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
884 $filter = array('t.fk_element' => $this->origin_id);
885 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
886 $filter['t.status'] = 1; // Restrict to lines with status validated
887 }
888
889 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
890 if ($ret < 0) {
891 $this->setErrorsFromObject($supplierorderdispatch);
892 return $ret;
893 } else {
894 // build array with quantity received by product in all supplier orders (origin)
895 foreach ($supplierorderdispatch->lines as $dispatch_line) {
896 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
897 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
898 } else {
899 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
900 }
901 }
902
903 // qty wished in origin (purchase order, ...)
904 foreach ($this->origin_object->lines as $origin_line) {
905 // exclude lines not qualified for reception
906 if (($origin_line->product_type > 0 && (!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') || empty($origin_line->stockable_product))) || $origin_line->product_type > 1) {
907 continue;
908 }
909 if (array_key_exists($origin_line->fk_product, $qty_wished)) {
910 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
911 } else {
912 $qty_wished[$origin_line->fk_product] = $origin_line->qty;
913 }
914 }
915
916 // compare array
917 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
918 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
919 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
920
921 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
923 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
924 // set totally received if more products received than ordered
925 $close = 0;
926
927 if (count($diff_array) > 0) {
928 // there are some difference between the two arrays
929 // scan the array of results
930 foreach ($diff_array as $key => $value) {
931 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
932 if ($qty_received[$key] >= $qty_wished[$key]) {
933 $close++;
934 }
935 }
936 }
937
938 if ($close == count($diff_array)) {
939 // all the products are received equal or more than the ordered quantity
941 }
942 }
943 }
944 }
945
946 return $status;
947 }
948
965 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
966 {
967 global $conf, $langs, $user;
968
969 $num = count($this->lines);
970 $line = new CommandeFournisseurDispatch($this->db);
971
972 $line->fk_entrepot = $entrepot_id;
973 $line->fk_commandefourndet = $id;
974 $line->qty = $qty;
975
976 $supplierorderline = new CommandeFournisseurLigne($this->db);
977 $result = $supplierorderline->fetch($id);
978 if ($result <= 0) {
979 $this->setErrorsFromObject($supplierorderline);
980 return -1;
981 }
982
983 $fk_product = 0;
984 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
985 $fk_product = $supplierorderline->fk_product;
986
987 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
988 $langs->load("errors");
989 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
990 return -1;
991 }
992 }
993
994 // Check batch is set
995 $product = new Product($this->db);
996 $product->fetch($fk_product);
997 if (isModEnabled('productbatch')) {
998 $langs->load("errors");
999 if (!empty($product->status_batch) && empty($batch)) {
1000 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
1001 return -1;
1002 } elseif (empty($product->status_batch) && !empty($batch)) {
1003 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
1004 return -1;
1005 }
1006
1007 // check sell-by / eat-by date is mandatory
1008 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
1009 if (!empty($errorMsgArr)) {
1010 $errorMessage = '<b>' . $product->ref . '</b> : ';
1011 $errorMessage .= '<ul>';
1012 foreach ($errorMsgArr as $errorMsg) {
1013 $errorMessage .= '<li>' . $errorMsg . '</li>';
1014 }
1015 $errorMessage .= '</ul>';
1016 $this->error = $errorMessage;
1017 return -1;
1018 }
1019 }
1020 unset($product);
1021
1022 // extrafields
1023 $line->array_options = $supplierorderline->array_options;
1024 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
1025 foreach ($array_options as $key => $value) {
1026 $line->array_options[$key] = $value;
1027 }
1028 }
1029
1030 $line->fk_product = $fk_product;
1031 $line->fk_commande = $supplierorderline->fk_commande;
1032 $line->fk_user = $user->id;
1033 $line->comment = $comment;
1034 $line->batch = $batch;
1035 $line->eatby = $eatby;
1036 $line->sellby = $sellby;
1037 $line->status = 1;
1038 $line->cost_price = $cost_price;
1039 $line->fk_reception = $this->id;
1040
1041 $this->lines[$num] = $line;
1042
1043 return $num;
1044 }
1045
1062 public function addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options = [], $cost_price = 0, $ref_fourn = '', $fk_entrepot = 0, $batch = '')
1063 {
1064 global $mysoc, $langs, $user;
1065
1066 if ($this->status == self::STATUS_DRAFT) {
1067 if (empty($rang)) {
1068 $rang = 0;
1069 }
1070
1071 $qty = (float) price2num($qty);
1072
1073 $this->db->begin();
1074
1075 // Rank to use
1076 $ranktouse = $this->rang;
1077 if ($ranktouse == -1) {
1078 $rangmax = $this->line_max($this->fk_reception);
1079 $ranktouse = $rangmax + 1;
1080 }
1081
1082 // Insert line
1083 $this->line = new ReceptionLineBatch($this->db);
1084 $this->line->fk_reception = $this->id;
1085 $this->line->element_type = $element_type;
1086 $this->line->fk_product = $fk_product;
1087 $this->line->description = $description;
1088
1089 $this->line->qty = (float) $qty;
1090 $this->line->fk_unit = $fk_unit;
1091 $this->line->rang = $ranktouse;
1092 $this->line->cost_price = (float) $cost_price;
1093 $this->line->ref_fourn = trim((string) $ref_fourn);
1094 if ((int) $fk_entrepot > 0) {
1095 $this->line->fk_entrepot = (int) $fk_entrepot;
1096 }
1097 if ((string) $batch !== '') {
1098 $this->line->batch = trim((string) $batch);
1099 }
1100
1101 if (is_array($array_options) && count($array_options) > 0) {
1102 $this->line->array_options = $array_options;
1103 }
1104
1105 $result = $this->line->insert($user);
1106 if ($result > 0) {
1107 if (!isset($this->context['createfromclone'])) {
1108 if ($this->fk_reception) {
1109 $this->line_order(true, 'DESC');
1110 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1111 $linecount = count($this->lines);
1112 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1113 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1114 }
1115 }
1116 $this->lines[] = $this->line;
1117 }
1118
1119 $this->db->commit();
1120 return $this->line->id;
1121 } else {
1122 $this->error = $this->line->error;
1123 dol_syslog(get_class($this)."::addlinefree error=".$this->error, LOG_ERR);
1124 $this->db->rollback();
1125 return -2;
1126 }
1127 } else {
1128 dol_syslog(get_class($this)."::addlinefree status of reception must be Draft to allow use of ->addlinefree()", LOG_ERR);
1129 return -3;
1130 }
1131 }
1132
1147 public function updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options = array())
1148 {
1149 global $mysoc, $langs, $user;
1150
1151 if ($this->statut == self::STATUS_DRAFT) {
1152 $this->db->begin();
1153
1154 if (empty($qty)) {
1155 $qty = 0;
1156 }
1157 if (empty($rang)) {
1158 $rang = 0;
1159 }
1160
1161 $qty = (float) $qty;
1162 $description = trim($description);
1163
1164 // Fetch current line from the database and then clone the object and set it in $oldline property
1165 $line = new ReceptionLineBatch($this->db);
1166
1167 $line->fetch($rowid);
1168 $line->fetch_optionals();
1169
1170 if (!empty($line->fk_product)) {
1171 $product = new Product($this->db);
1172 $result = $product->fetch($line->fk_product);
1173 $product_type = $product->type;
1174 }
1175
1176 $staticline = clone $line;
1177
1178 $line->oldline = $staticline;
1179 $this->line = $line;
1180 $this->line->context = $this->context;
1181 $this->line->rang = $rang;
1182 $this->line->fk_reception = $this->id;
1183 $this->line->element_type = $element_type;
1184 $this->line->fk_product = $line->fk_product;
1185 $this->line->qty = $qty;
1186 $this->line->fk_unit = $fk_unit;
1187 $this->line->description = $description;
1188
1189 if (is_array($array_options) && count($array_options) > 0) {
1190 // We replace values in this->line->array_options only for entries defined into $array_options
1191 foreach ($array_options as $key => $value) {
1192 $this->line->array_options[$key] = $array_options[$key];
1193 }
1194 }
1195
1196
1197 $result = $this->line->update($user, $notrigger);
1198 if ($result > 0) {
1199 // Reorder if child line
1200
1201
1202 $this->db->commit();
1203 return $result;
1204 } else {
1205 $this->error = $this->line->error;
1206
1207 $this->db->rollback();
1208 return -1;
1209 }
1210 } else {
1211 $this->error = get_class($this)."::updatelinefree reception status makes operation forbidden";
1212 $this->errors = array('ReceptionStatusMakeOperationForbidden');
1213 return -2;
1214 }
1215 }
1216
1217 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1223 public function fetch_lines_free()
1224 {
1225 // phpcs:enable
1226 $this->lines = array();
1227
1228 $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, rc.cost_price, rc.ref_fourn, rc.batch, rc.eatby, rc.sellby';
1229 $sql .= ' FROM '.MAIN_DB_PREFIX.'receptiondet_batch as rc';
1230 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = rc.fk_product)';
1231 $sql .= ' WHERE rc.fk_reception = '.((int) $this->id);
1232
1233 $sql .= ' ORDER BY rc.rang, rc.rowid';
1234
1235 dol_syslog(get_class($this)."::fetch_lines_free", LOG_DEBUG);
1236 $result = $this->db->query($sql);
1237 if ($result) {
1238 $num = $this->db->num_rows($result);
1239
1240 $i = 0;
1241 while ($i < $num) {
1242 $objp = $this->db->fetch_object($result);
1243
1244 $line = new ReceptionLineBatch($this->db);
1245
1246 $line->rowid = $objp->rowid;
1247 $line->id = $objp->rowid;
1248 $line->fk_reception = $this->id;
1249
1250 $line->description = $objp->description;
1251 $line->qty = $objp->qty;
1252 $line->fk_entrepot = $objp->fk_entrepot;
1253 $line->cost_price = $objp->cost_price;
1254 $line->ref_fourn = $objp->ref_fourn;
1255 $line->batch = $objp->batch;
1256 $line->eatby = $objp->eatby;
1257 $line->sellby = $objp->sellby;
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 COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as 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."receptiondet_batch as ed";
1454 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
1455 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1456 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
1457 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
1458
1459 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1460 $resql = $this->db->query($sql);
1461 if ($resql) {
1462 $cpt = $this->db->num_rows($resql);
1463 for ($i = 0; $i < $cpt; $i++) {
1464 dol_syslog(get_class($this)."::delete movement index ".$i);
1465 $obj = $this->db->fetch_object($resql);
1466
1467 $mouvS = new MouvementStock($this->db);
1468 // we do not log origin because it will be deleted
1469 $mouvS->origin = null;
1470
1471 $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
1472 if ($result < 0) {
1473 $error++;
1474 $this->error = $mouvS->error;
1475 $this->errors = $mouvS->errors;
1476 }
1477 }
1478 } else {
1479 $error++;
1480 $this->errors[] = "Error ".$this->db->lasterror();
1481 }
1482 }
1483
1484 if (!$error) {
1485 $sanitized_main = MAIN_DB_PREFIX.'receptiondet_batch';
1486 $sanitized_ef = $sanitized_main."_extrafields";
1487
1488 $sqlef = "DELETE FROM ".$sanitized_ef." WHERE fk_object IN (SELECT rowid FROM ".$sanitized_main." WHERE fk_reception = ".((int) $this->id).")";
1489
1490 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1491 $sql .= " WHERE fk_reception = ".((int) $this->id);
1492
1493 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1494 // Delete linked object
1495 $res = $this->deleteObjectLinked();
1496 if ($res < 0) {
1497 $error++;
1498 }
1499
1500 if (!$error) {
1501 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1502 $sql .= " WHERE rowid = ".((int) $this->id);
1503
1504 if ($this->db->query($sql)) {
1505 // Call trigger
1506 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1507 if ($result < 0) {
1508 $error++;
1509 }
1510 // End call triggers
1511
1512 if (!empty($this->origin) && $this->origin_id > 0) {
1513 $this->fetch_origin();
1515 '@phan-var-force CommandeFournisseur $origin_object';
1517 if ($origin_object->status == 4) { // If order source of reception is "partially received"
1518 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1519 $origin_object->loadReceptions();
1520 //var_dump($this->$origin->receptions);exit;
1521 if (count($origin_object->receptions) <= 0) {
1522 $origin_object->setStatut(3); // CommandeFournisseur ordered
1523 }
1524 }
1525 }
1526
1527 if (!$error) {
1528 $this->db->commit();
1529
1530 // We delete PDFs
1531 $ref = dol_sanitizeFileName($this->ref);
1532 if (!empty($conf->reception->dir_output)) {
1533 $dir = $conf->reception->dir_output.'/'.$ref;
1534 $file = $dir.'/'.$ref.'.pdf';
1535 if (file_exists($file)) {
1536 if (!dol_delete_file($file)) {
1537 return 0;
1538 }
1539 }
1540 if (file_exists($dir)) {
1541 if (!dol_delete_dir_recursive($dir)) {
1542 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1543 return 0;
1544 }
1545 }
1546 }
1547
1548 return 1;
1549 } else {
1550 $this->db->rollback();
1551 return -1;
1552 }
1553 } else {
1554 $this->error = $this->db->lasterror()." - sql=$sql";
1555 $this->db->rollback();
1556 return -3;
1557 }
1558 } else {
1559 $this->error = $this->db->lasterror()." - sql=$sql";
1560 $this->db->rollback();
1561 return -2;
1562 }
1563 } else {
1564 $this->error = $this->db->lasterror()." - sql=$sql";
1565 $this->db->rollback();
1566 return -1;
1567 }
1568 } else {
1569 $this->db->rollback();
1570 return -1;
1571 }
1572 }
1573
1574 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1580 public function fetch_lines()
1581 {
1582 // phpcs:enable
1583 $this->lines = array();
1584
1585 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1586
1587 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1588 $sql .= " WHERE fk_reception = ".((int) $this->id);
1589
1590 $resql = $this->db->query($sql);
1591
1592 if (!empty($resql)) {
1593 while ($obj = $this->db->fetch_object($resql)) {
1594 $line = new CommandeFournisseurDispatch($this->db);
1595
1596 $line->fetch($obj->rowid);
1597
1598 // TODO Remove or keep this ?
1599 $line->fetch_product();
1600
1601 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1602 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1603 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1604 $sql_commfourndet .= ' ORDER BY rang';
1605
1606 $resql_commfourndet = $this->db->query($sql_commfourndet);
1607 if (!empty($resql_commfourndet)) {
1608 $obj = $this->db->fetch_object($resql_commfourndet);
1609 $line->qty_asked = $obj->qty;
1610 $line->description = $obj->description;
1611 $line->desc = $obj->description;
1612 $line->tva_tx = $obj->tva_tx;
1613 $line->vat_src_code = $obj->vat_src_code;
1614 $line->subprice = $obj->subprice;
1615 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1616 $line->remise_percent = $obj->remise_percent;
1617 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1618 $line->ref_supplier = $obj->ref;
1619 $line->total_ht = $obj->total_ht;
1620 $line->total_ttc = $obj->total_ttc;
1621 $line->total_tva = $obj->total_tva;
1622 } else {
1623 $line->qty_asked = 0;
1624 $line->description = '';
1625 $line->desc = '';
1626 $line->label = $obj->label;
1627 }
1628
1629 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1630 $tva = $pu_ht * $line->tva_tx / 100;
1631 $this->total_ht += $pu_ht;
1632 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1633
1634 $this->total_ttc += $pu_ht + $tva;
1635
1636 if (isModEnabled('productbatch') && !empty($line->batch)) {
1637 $detail_batch = new stdClass();
1638 $detail_batch->eatby = $line->eatby;
1639 $detail_batch->sellby = $line->sellby;
1640 $detail_batch->batch = $line->batch;
1641 $detail_batch->qty = $line->qty;
1642
1643 $line->detail_batch[] = $detail_batch;
1644 }
1645
1646 $this->lines[] = $line;
1647 }
1648
1649 return 1;
1650 } else {
1651 return -1;
1652 }
1653 }
1654
1665 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0)
1666 {
1667 global $langs, $hookmanager;
1668
1669 $result = '';
1670 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1671 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1672 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1673
1674 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1675
1676 if ($short) {
1677 return $url;
1678 }
1679
1680 $linkclose = '';
1681 if (empty($notooltip)) {
1682 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1683 $label = $langs->trans("Reception");
1684 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1685 }
1686 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1687 $linkclose .= ' class="classfortooltip"';
1688 }
1689
1690 $linkstart = '<a href="'.$url.'"';
1691 $linkstart .= $linkclose.'>';
1692 $linkend = '</a>';
1693
1694 $result .= $linkstart;
1695 if ($withpicto) {
1696 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1697 }
1698 if ($withpicto != 2) {
1699 $result .= $this->ref;
1700 }
1701
1702 $result .= $linkend;
1703
1704 global $action;
1705 $hookmanager->initHooks(array($this->element . 'dao'));
1706 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1707 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1708 if ($reshook > 0) {
1709 $result = $hookmanager->resPrint;
1710 } else {
1711 $result .= $hookmanager->resPrint;
1712 }
1713 return $result;
1714 }
1715
1722 public function getLibStatut($mode = 0)
1723 {
1724 return $this->LibStatut($this->statut, $mode);
1725 }
1726
1727 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1735 public function LibStatut($status, $mode)
1736 {
1737 // phpcs:enable
1738 global $langs;
1739
1740 // List of long language codes for status
1741 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1742 $this->labelStatus[0] = 'StatusReceptionDraft';
1743 // product to receive if stock increase is on close or already received if stock increase is on validation
1744 $this->labelStatus[1] = 'StatusReceptionValidated';
1745 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1746 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1747 }
1748 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1749 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1750 }
1751 $this->labelStatus[2] = 'StatusReceptionProcessed';
1752
1753 // List of short language codes for status
1754 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1755 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1756 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1757 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1758
1759 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1760 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1761
1762 $statusType = 'status'.$status;
1763 if ($status == self::STATUS_VALIDATED) {
1764 $statusType = 'status4';
1765 }
1766 if ($status == self::STATUS_CLOSED) {
1767 $statusType = 'status6';
1768 }
1769
1770 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1771 }
1772
1780 public function getKanbanView($option = '', $arraydata = null)
1781 {
1782 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1783
1784 $return = '<div class="box-flex-item box-flex-grow-zero">';
1785 $return .= '<div class="info-box info-box-sm">';
1786 $return .= '<div class="info-box-icon bg-infobox-action">';
1787 $return .= img_picto('', 'order');
1788 $return .= '</div>';
1789 $return .= '<div class="info-box-content">';
1790 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1791 if ($selected >= 0) {
1792 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1793 }
1794 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1795 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1796 }
1797 /*if (property_exists($this, 'total_ht')) {
1798 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency()).' '.$langs->trans('HT').'</div>';
1799 }*/
1800 if (method_exists($this, 'getLibStatut')) {
1801 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1802 }
1803 $return .= '</div>';
1804 $return .= '</div>';
1805 $return .= '</div>';
1806
1807 return $return;
1808 }
1809
1817 public function initAsSpecimen()
1818 {
1819 global $langs;
1820
1821 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1822 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1823 $now = dol_now();
1824
1825 dol_syslog(get_class($this)."::initAsSpecimen");
1826
1827 $order = new CommandeFournisseur($this->db);
1828 $order->initAsSpecimen();
1829
1830 // Initialise parameters
1831 $this->id = 0;
1832 $this->ref = 'SPECIMEN';
1833 $this->specimen = 1;
1834 $this->statut = 1;
1835 $this->status = 1;
1836 $this->date = $now;
1837 $this->date_creation = $now;
1838 $this->date_valid = $now;
1839 $this->date_delivery = $now;
1840 $this->date_reception = $now + 24 * 3600;
1841 $this->entrepot_id = 0;
1842 $this->socid = 1;
1843 $this->origin_id = 1;
1844 $this->origin_type = 'supplier_order';
1845 $this->origin_object = $order;
1846 $this->note_private = 'Private note';
1847 $this->note_public = 'Public note';
1848 $this->tracking_number = 'TRACKID-ABC123';
1849 $this->fk_incoterms = 1;
1850 $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)
1851 $xnbp = 0;
1852 while ($xnbp < $nbp) {
1853 $line = new CommandeFournisseurDispatch($this->db);
1854 $line->desc = $langs->trans("Description")." ".$xnbp;
1855 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1856 $line->label = $langs->trans("Description")." ".$xnbp;
1857 $line->qty = 10;
1858
1859 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1860
1861 $this->lines[] = $line;
1862 $xnbp++;
1863 }
1864
1865 return 1;
1866 }
1867
1875 public function setDeliveryDate($user, $delivery_date)
1876 {
1877 // phpcs:enable
1878 if ($user->hasRight('reception', 'creer')) {
1879 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1880 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1881 $sql .= " WHERE rowid = ".((int) $this->id);
1882
1883 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1884 $resql = $this->db->query($sql);
1885 if ($resql) {
1886 $this->date_delivery = $delivery_date;
1887 return 1;
1888 } else {
1889 $this->error = $this->db->error();
1890 return -1;
1891 }
1892 } else {
1893 return -2;
1894 }
1895 }
1896
1904 public function setReceptionDate($user, $reception_date)
1905 {
1906 if ($user->hasRight('reception', 'creer')) {
1907 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1908 $sql .= " SET date_reception = ".($reception_date ? "'".$this->db->idate($reception_date)."'" : 'null');
1909 $sql .= " WHERE rowid = ".((int) $this->id);
1910
1911 dol_syslog(get_class($this)."::setReceptionDate", LOG_DEBUG);
1912 $resql = $this->db->query($sql);
1913 if ($resql) {
1914 $this->date_reception = $reception_date;
1915 return 1;
1916 } else {
1917 $this->error = $this->db->error();
1918 return -1;
1919 }
1920 } else {
1921 return -2;
1922 }
1923 }
1924
1925 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1931 public function fetch_delivery_methods()
1932 {
1933 // phpcs:enable
1934 global $langs;
1935 $this->meths = array();
1936
1937 $sql = "SELECT em.rowid, em.code, em.libelle";
1938 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1939 $sql .= " WHERE em.active = 1";
1940 $sql .= " ORDER BY em.libelle ASC";
1941
1942 $resql = $this->db->query($sql);
1943 if ($resql) {
1944 while ($obj = $this->db->fetch_object($resql)) {
1945 $label = $langs->trans('ReceptionMethod'.$obj->code);
1946 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1947 }
1948 }
1949 }
1950
1951 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1958 public function list_delivery_methods($id = 0)
1959 {
1960 // phpcs:enable
1961 global $langs;
1962
1963 $this->listmeths = array();
1964 $i = 0;
1965
1966 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1967 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1968 if (!empty($id)) {
1969 $sql .= " WHERE em.rowid = ".((int) $id);
1970 }
1971
1972 $resql = $this->db->query($sql);
1973 if ($resql) {
1974 while ($obj = $this->db->fetch_object($resql)) {
1975 $this->listmeths[$i]['rowid'] = $obj->rowid;
1976 $this->listmeths[$i]['code'] = $obj->code;
1977 $label = $langs->trans('ReceptionMethod'.$obj->code);
1978 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1979 $this->listmeths[$i]['description'] = $obj->description;
1980 $this->listmeths[$i]['tracking'] = $obj->tracking;
1981 $this->listmeths[$i]['active'] = $obj->active;
1982 $i++;
1983 }
1984 }
1985 }
1986
1993 public function getUrlTrackingStatus($value = '')
1994 {
1995 if (!empty($this->shipping_method_id)) {
1996 $sql = "SELECT em.code, em.tracking";
1997 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1998 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1999
2000 $resql = $this->db->query($sql);
2001 if ($resql) {
2002 if ($obj = $this->db->fetch_object($resql)) {
2003 $tracking = $obj->tracking;
2004 }
2005 }
2006 }
2007
2008 if (!empty($tracking) && !empty($value)) {
2009 $url = str_replace('{TRACKID}', $value, $tracking);
2010 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
2011 } else {
2012 $this->tracking_url = $value;
2013 }
2014 }
2015
2021 public function setClosed()
2022 {
2023 global $conf, $langs, $user;
2024
2025 $error = 0;
2026
2027 // Protection. This avoid to move stock later when we should not
2028 if ($this->statut == Reception::STATUS_CLOSED) {
2029 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
2030 return 0;
2031 }
2032
2033 $this->db->begin();
2034
2035 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
2036 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2037
2038 $resql = $this->db->query($sql);
2039 if ($resql) {
2040 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
2041 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
2042 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2043
2044 $order = new CommandeFournisseur($this->db);
2045 $order->fetch($this->origin_id);
2046
2047 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
2048
2049 $receptions_match_order = 1;
2050 foreach ($order->lines as $line) {
2051 $lineid = $line->id;
2052 $qty = $line->qty;
2053 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
2054 $receptions_match_order = 0;
2055 $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';
2056 dol_syslog($text);
2057 break;
2058 }
2059 }
2060 if ($receptions_match_order) {
2061 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');
2062 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
2063 }
2064 }
2065
2066 $this->statut = self::STATUS_CLOSED;
2067 $this->status = self::STATUS_CLOSED;
2068
2069 // If stock increment is done on closing
2070 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2071 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2072
2073 $langs->load("agenda");
2074
2075 // Loop on each product line to add a stock movement
2076 // TODO possibility to receive from a proposal or other origin ?
2077 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2078 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2079 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2080 $sql .= " ed.fk_elementdet, ed.cost_price";
2081 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2082 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2083 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2084 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2085 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2086
2087 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2088 $resql = $this->db->query($sql);
2089
2090 if ($resql) {
2091 $cpt = $this->db->num_rows($resql);
2092 for ($i = 0; $i < $cpt; $i++) {
2093 $obj = $this->db->fetch_object($resql);
2094
2095 $qty = $obj->qty;
2096
2097 if ($qty <= 0) {
2098 continue;
2099 }
2100
2101 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
2102
2103 $mouvS = new MouvementStock($this->db);
2104 $mouvS->origin = &$this;
2105 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2106
2107 if (empty($obj->batch)) {
2108 // line without batch detail
2109
2110 // 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
2111 $inventorycode = '';
2112 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2113 if ($result < 0) {
2114 $this->error = $mouvS->error;
2115 $this->errors = $mouvS->errors;
2116 $error++;
2117 break;
2118 }
2119 } else {
2120 // line with batch detail
2121
2122 // 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
2123 $inventorycode = '';
2124 $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);
2125
2126 if ($result < 0) {
2127 $this->error = $mouvS->error;
2128 $this->errors = $mouvS->errors;
2129 $error++;
2130 break;
2131 }
2132 }
2133 }
2134 } else {
2135 $this->error = $this->db->lasterror();
2136 $error++;
2137 }
2138 }
2139
2140 // Call trigger
2141 if (!$error) {
2142 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
2143 if ($result < 0) {
2144 $error++;
2145 }
2146 }
2147 } else {
2148 dol_print_error($this->db);
2149 $error++;
2150 }
2151
2152 if (!$error) {
2153 $this->db->commit();
2154 return 1;
2155 } else {
2156 $this->statut = self::STATUS_VALIDATED;
2157 $this->status = self::STATUS_VALIDATED;
2158 $this->db->rollback();
2159 return -1;
2160 }
2161 }
2162
2168 public function setBilled()
2169 {
2170 global $user;
2171 $error = 0;
2172
2173 $this->db->begin();
2174
2175 if ($this->statut == Reception::STATUS_VALIDATED) {
2176 // do not close if already closed
2177 $this->setClosed();
2178 }
2179
2180 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
2181 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2182
2183 $resql = $this->db->query($sql);
2184 if ($resql) {
2185 $this->billed = 1;
2186
2187 // Call trigger
2188 $result = $this->call_trigger('RECEPTION_BILLED', $user);
2189 if ($result < 0) {
2190 $this->billed = 0;
2191 $error++;
2192 }
2193 } else {
2194 $error++;
2195 $this->errors[] = $this->db->lasterror;
2196 }
2197
2198 if (empty($error)) {
2199 $this->db->commit();
2200 return 1;
2201 } else {
2202 $this->db->rollback();
2203 return -1;
2204 }
2205 }
2206
2212 public function reOpen()
2213 {
2214 global $langs, $user;
2215
2216 $error = 0;
2217
2218 $this->db->begin();
2219
2220 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
2221 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2222
2223 $resql = $this->db->query($sql);
2224 $rollbackStatus = $this->status;
2225 $rollbackBilled = $this->billed;
2226 if ($resql) {
2227 $this->statut = self::STATUS_VALIDATED;
2228 $this->status = self::STATUS_VALIDATED;
2229 $this->billed = 0;
2230
2231 // If stock increment is done on closing
2232 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2233 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2234 $numref = (string) $this->ref;
2235 $langs->load("agenda");
2236
2237 // Loop on each product line to add a stock movement
2238 // TODO possibility to receive from a proposal or other origin
2239 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2240 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2241 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2242 $sql .= " ed.fk_elementdet, ed.cost_price";
2243 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2244 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2245 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2246 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2247 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2248
2249 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2250 $resql = $this->db->query($sql);
2251 if ($resql) {
2252 $cpt = $this->db->num_rows($resql);
2253 for ($i = 0; $i < $cpt; $i++) {
2254 $obj = $this->db->fetch_object($resql);
2255
2256 $qty = $obj->qty;
2257
2258 if ($qty <= 0) {
2259 continue;
2260 }
2261 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2262
2263 //var_dump($this->lines[$i]);
2264 $mouvS = new MouvementStock($this->db);
2265 $mouvS->origin = &$this;
2266 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2267
2268 if (empty($obj->batch)) {
2269 // line without batch detail
2270
2271 // 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
2272 $inventorycode = '';
2273 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
2274
2275 if ($result < 0) {
2276 $this->error = $mouvS->error;
2277 $this->errors = $mouvS->errors;
2278 $error++;
2279 break;
2280 }
2281 } else {
2282 // line with batch detail
2283
2284 // 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
2285 $inventorycode = '';
2286 $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);
2287 if ($result < 0) {
2288 $this->error = $mouvS->error;
2289 $this->errors = $mouvS->errors;
2290 $error++;
2291 break;
2292 }
2293 }
2294 }
2295 } else {
2296 $this->error = $this->db->lasterror();
2297 $error++;
2298 }
2299 }
2300
2301 if (!$error) {
2302 // Call trigger
2303 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
2304 if ($result < 0) {
2305 $error++;
2306 }
2307 }
2308
2309 if (!$error && $this->origin == 'order_supplier') {
2310 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2311
2312 $commande = new CommandeFournisseur($this->db);
2313 $commande->fetch($this->origin_id);
2314 $result = $commande->setStatus($user, 4);
2315 if ($result < 0) {
2316 $error++;
2317 $this->error = $commande->error;
2318 $this->errors = $commande->errors;
2319 }
2320 }
2321 } else {
2322 $error++;
2323 $this->errors[] = $this->db->lasterror();
2324 }
2325
2326 if (!$error) {
2327 $this->db->commit();
2328 return 1;
2329 } else {
2330 $this->statut = $this->status = $rollbackStatus;
2331 $this->billed = $rollbackBilled;
2332 $this->db->rollback();
2333 return -1;
2334 }
2335 }
2336
2343 public function setDraft($user)
2344 {
2345 // phpcs:enable
2346 global $langs;
2347
2348 $error = 0;
2349
2350 // Protection
2351 if ($this->statut <= self::STATUS_DRAFT) {
2352 return 0;
2353 }
2354
2355 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
2356 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
2357 $this->error = 'Permission denied';
2358 return -1;
2359 }
2360
2361 $this->db->begin();
2362
2363 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2364 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2365 $sql .= " WHERE rowid = ".((int) $this->id);
2366
2367 dol_syslog(__METHOD__, LOG_DEBUG);
2368 if ($this->db->query($sql)) {
2369 // If stock increment is done on closing
2370 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2371 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2372
2373 $langs->load("agenda");
2374
2375 // Loop on each product line to add a stock movement
2376 // TODO possibility to receive from a proposal or other origin
2377 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2378 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2379 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2380 $sql .= " ed.fk_elementdet, ed.cost_price";
2381 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2382 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2383 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2384 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2385 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2386
2387 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2388 $resql = $this->db->query($sql);
2389 if ($resql) {
2390 $cpt = $this->db->num_rows($resql);
2391 for ($i = 0; $i < $cpt; $i++) {
2392 $obj = $this->db->fetch_object($resql);
2393
2394 $qty = $obj->qty;
2395
2396 if ($qty <= 0) {
2397 continue;
2398 }
2399
2400 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2401
2402 //var_dump($this->lines[$i]);
2403 $mouvS = new MouvementStock($this->db);
2404 $mouvS->origin = &$this;
2405 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2406
2407 if (empty($obj->batch)) {
2408 // line without batch detail
2409
2410 // 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
2411 $inventorycode = '';
2412 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2413 if ($result < 0) {
2414 $this->error = $mouvS->error;
2415 $this->errors = $mouvS->errors;
2416 $error++;
2417 break;
2418 }
2419 } else {
2420 // line with batch detail
2421
2422 // 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
2423 $inventorycode = '';
2424 $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);
2425 if ($result < 0) {
2426 $this->error = $mouvS->error;
2427 $this->errors = $mouvS->errors;
2428 $error++;
2429 break;
2430 }
2431 }
2432 }
2433 } else {
2434 $this->error = $this->db->lasterror();
2435 $error++;
2436 }
2437 }
2438
2439 if (!$error) {
2440 // Call trigger
2441 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2442 if ($result < 0) {
2443 $error++;
2444 }
2445 }
2446 if ($this->origin == 'order_supplier') {
2447 if (!empty($this->origin) && $this->origin_id > 0) {
2448 $this->fetch_origin();
2449 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2450 // Check if there is no more reception validated.
2451 $this->origin_object->fetchObjectLinked();
2452 $setStatut = 1;
2453 if (!empty($this->origin_object->linkedObjects['reception'])) {
2454 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2455 if ($rcption->statut > 0) {
2456 $setStatut = 0;
2457 break;
2458 }
2459 }
2460 //var_dump($this->$origin->receptions);exit;
2461 if ($setStatut) {
2462 $this->origin_object->setStatut(3); // CommandeFournisseur ordered
2463 }
2464 }
2465 }
2466 }
2467 }
2468
2469 if (!$error) {
2470 $this->statut = self::STATUS_DRAFT;
2471 $this->status = self::STATUS_DRAFT;
2472 $this->db->commit();
2473 return 1;
2474 } else {
2475 $this->db->rollback();
2476 return -1;
2477 }
2478 } else {
2479 $this->error = $this->db->error();
2480 $this->db->rollback();
2481 return -1;
2482 }
2483 }
2484
2495 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2496 {
2497 global $conf, $langs;
2498
2499 $langs->load("receptions");
2500
2501 if (!dol_strlen($modele)) {
2502 $modele = 'squille';
2503
2504 if ($this->model_pdf) {
2505 $modele = $this->model_pdf;
2506 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2507 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2508 }
2509 }
2510
2511 $modelpath = "core/modules/reception/doc/";
2512
2513 $this->fetch_origin();
2514
2515 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2516 }
2517
2526 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2527 {
2528 $tables = array('reception');
2529
2530 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2531 }
2532
2541 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2542 {
2543 $tables = array(
2544 'receptiondet_batch'
2545 );
2546
2547 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2548 }
2549}
$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.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='')
Set status of an object.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
fetch_origin()
Read linked origin object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
Class to manage Dolibarr database access.
Class to manage standard extra fields.
Class to manage stock movements.
Class to manage products or services.
static checkSellOrEatByMandatoryFromProductAndDates($product, $sellBy, $eatBy, $onlyFieldName='', $alreadyCheckConf=false)
Check sell or eat by date is mandatory from product and sell-by and eat-by dates.
Class to manage receptions.
setBilled()
Classify the reception as invoiced (used for example by trigger when WORKFLOW_RECEPTION_CLASSIFY_BILL...
fetch_delivery_methods()
Fetch deliveries method and return an array.
getLibStatut($mode=0)
Return status label.
valid($user, $notrigger=0)
Validate object and update stock if option enabled.
updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options=array())
Update a simple reception line.
getUrlTrackingStatus($value='')
Forge an set tracking url.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLinesArray()
Create an array of reception lines.
fetch_lines_free()
Load lines of simple reception.
update($user=null, $notrigger=0)
Update database.
setClosed()
Classify the reception as closed (this records also the stock movement)
static replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a product id with another one.
LibStatut($status, $mode)
Return label of a status.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0)
Return clickable link of object (with eventually picto)
create_line($entrepot_id, $origin_line_id, $qty, $rang=0, $array_options=[], $parent_line_id=0, $product_id=0)
Create a reception line.
setDeliveryDate($user, $delivery_date)
Set the planned delivery date.
list_delivery_methods($id=0)
Fetch all deliveries method and return an array.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
__construct($db)
Constructor.
initAsSpecimen()
Initialise an instance with random values.
setReceptionDate($user, $reception_date)
Set the reception date.
fetch_lines()
Load lines.
addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options=[], $cost_price=0, $ref_fourn='', $fk_entrepot=0, $batch='')
Add a simple reception line.
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:65
dol_now($mode='gmt')
Return date for now.
setEntity($currentobject)
Set entity id to use when to create an object.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
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.
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.
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)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:487
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:134