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-2026 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
222 public $fk_warehouse;
223
229 public function __construct($db)
230 {
231 $this->db = $db;
232
233 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
234 $this->isextrafieldmanaged = 1;
235 }
236
243 public function getNextNumRef($soc)
244 {
245 global $langs, $conf;
246 $langs->load("receptions");
247
248 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
249 $mybool = false;
250
251 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
252 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
253
254 // Include file with class
255 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
256
257 foreach ($dirmodels as $reldir) {
258 $dir = dol_buildpath($reldir."core/modules/reception/");
259
260 // Load file with numbering class (if found)
261 $mybool = ((bool) @include_once $dir.$file) || $mybool;
262 }
263
264 if (!$mybool) {
265 dol_print_error(null, "Failed to include file ".$file);
266 return '';
267 }
268
269 $obj = new $classname();
270 '@phan-var-force ModelNumRefReception $obj';
273 $numref = $obj->getNextValue($soc, $this);
274
275 if ($numref != "") {
276 return $numref;
277 } else {
278 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
279 return "";
280 }
281 } else {
282 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
283 return "";
284 }
285 }
286
294 public function create($user, $notrigger = 0)
295 {
296 global $conf;
297
298 $now = dol_now();
299
300 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
301 $error = 0;
302
303 // Clean parameters
304 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
305 if (empty($this->fk_project)) {
306 $this->fk_project = 0;
307 }
308 if (empty($this->weight_units)) {
309 $this->weight_units = 0;
310 }
311 if (empty($this->size_units)) {
312 $this->size_units = 0;
313 }
314 if (empty($this->date_creation)) {
315 $this->date_creation = $now;
316 }
317
318 $this->entity = setEntity($this);
319
320 $this->user = $user;
321
322 $this->db->begin();
323
324 // If there is only one active warehouse, use it as the default warehouse of the reception
325 if (empty($this->fk_warehouse)) {
326 $resqlwh = $this->db->query("SELECT rowid FROM ".MAIN_DB_PREFIX."entrepot WHERE entity IN (".getEntity('stock').") AND statut = 1");
327 if ($resqlwh && $this->db->num_rows($resqlwh) == 1) {
328 $objwh = $this->db->fetch_object($resqlwh);
329 $this->fk_warehouse = (int) $objwh->rowid;
330 }
331 }
332
333 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
334 $sql .= "ref";
335 $sql .= ", entity";
336 $sql .= ", ref_supplier";
337 $sql .= ", date_creation";
338 $sql .= ", fk_user_author";
339 $sql .= ", date_reception";
340 $sql .= ", date_delivery";
341 $sql .= ", fk_soc";
342 $sql .= ", fk_projet";
343 $sql .= ", fk_warehouse";
344 $sql .= ", fk_shipping_method";
345 $sql .= ", tracking_number";
346 $sql .= ", weight";
347 $sql .= ", size";
348 $sql .= ", width";
349 $sql .= ", height";
350 $sql .= ", weight_units";
351 $sql .= ", size_units";
352 $sql .= ", note_private";
353 $sql .= ", note_public";
354 $sql .= ", model_pdf";
355 $sql .= ", fk_incoterms, location_incoterms";
356 $sql .= ") VALUES (";
357 $sql .= "'(PROV)'";
358 $sql .= ", ".((int) $this->entity);
359 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
360 $sql .= ", '".$this->db->idate($this->date_creation)."'";
361 $sql .= ", ".((int) $user->id);
362 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
363 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
364 $sql .= ", ".($this->socid > 0 ? ((int) $this->socid) : "null");
365 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
366 $sql .= ", ".($this->fk_warehouse > 0 ? ((int) $this->fk_warehouse) : "null");
367 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
368 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
369 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
370 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
371 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
372 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
373 $sql .= ", ".((int) $this->weight_units);
374 $sql .= ", ".((int) $this->size_units);
375 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
376 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
377 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
378 $sql .= ", ".(int) $this->fk_incoterms;
379 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
380 $sql .= ")";
381
382 dol_syslog(get_class($this)."::create", LOG_DEBUG);
383
384 $resql = $this->db->query($sql);
385
386 if ($resql) {
387 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
388
389 // update ref
390 $initialref = '(PROV'.$this->id.')';
391 if (!empty($this->ref)) {
392 $initialref = $this->ref;
393 }
394
395 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
396 $sql .= " SET ref = '".$this->db->escape($initialref)."'";
397 $sql .= " WHERE rowid = ".((int) $this->id);
398
399 dol_syslog(get_class($this)."::create", LOG_DEBUG);
400 if ($this->db->query($sql)) {
401 // Insert of lines
402 $num = count($this->lines);
403 for ($i = 0; $i < $num; $i++) {
404 $this->lines[$i]->fk_reception = $this->id;
405
406 if ($this->lines[$i]->create($user) <= 0) {
407 $this->setErrorsFromObject($this->lines[$i]);
408 $error++;
409 }
410 }
411
412 if (!$error && $this->id && $this->origin_id) {
413 $ret = $this->add_object_linked();
414 if (!$ret) {
415 $error++;
416 }
417 }
418
419 // Create extrafields
420 if (!$error) {
421 $result = $this->insertExtraFields();
422 if ($result < 0) {
423 $error++;
424 }
425 }
426
427 if (!$error && !$notrigger) {
428 // Call trigger
429 $result = $this->call_trigger('RECEPTION_CREATE', $user);
430 if ($result < 0) {
431 $error++;
432 }
433 // End call triggers
434 }
435
436 if (!$error) {
437 $this->db->commit();
438 return $this->id;
439 } else {
440 foreach ($this->errors as $errmsg) {
441 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
442 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
443 }
444 $this->db->rollback();
445 return -1 * $error;
446 }
447 } else {
448 $error++;
449 $this->error = $this->db->lasterror()." - sql=$sql";
450 $this->db->rollback();
451 return -2;
452 }
453 } else {
454 $error++;
455 $this->error = $this->db->error()." - sql=$sql";
456 $this->db->rollback();
457 return -1;
458 }
459 }
460
461 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
474 public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [], $parent_line_id = 0, $product_id = 0)
475 {
476 //phpcs:enable
477 global $user;
478
479 $receptionline = new ReceptionLineBatch($this->db);
480 $receptionline->fk_reception = $this->id;
481 $receptionline->entrepot_id = $entrepot_id;
482 $receptionline->fk_elementdet = $origin_line_id;
483 $receptionline->element_type = $this->origin;
484 $receptionline->fk_parent = $parent_line_id;
485 $receptionline->fk_product = $product_id;
486 $receptionline->qty = $qty;
487 $receptionline->rang = $rang;
488 $receptionline->array_options = $array_options;
489
490 if (!($receptionline->fk_product > 0)) {
491 $order_line = new CommandeFournisseurLigne($this->db);
492 $order_line->fetch($receptionline->fk_elementdet);
493 $receptionline->fk_product = $order_line->fk_product;
494 }
495
496 if (($lineId = $receptionline->insert($user)) < 0) {
497 $this->errors[] = $receptionline->error;
498 }
499 return $lineId;
500 }
501
510 public function fetch($id, $ref = '', $ref_ext = '')
511 {
512 // Check parameters
513 if (empty($id) && empty($ref) && empty($ref_ext)) {
514 return -1;
515 }
516
517 $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, e.fk_warehouse";
518 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
519 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery, e.date_valid";
520 $sql .= ", e.fk_shipping_method, e.tracking_number";
521 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
522 $sql .= ", e.note_private, e.note_public";
523 $sql .= ', e.fk_incoterms, e.location_incoterms';
524 $sql .= ', i.libelle as label_incoterms';
525 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
526 $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'";
527 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
528
529 if ($id) {
530 $sql .= " WHERE e.rowid = ".((int) $id);
531 } else {
532 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
533 if ($ref) {
534 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
535 } elseif ($ref_ext) {
536 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
537 }
538 }
539
540 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
541 $result = $this->db->query($sql);
542 if ($result) {
543 if ($this->db->num_rows($result)) {
544 $obj = $this->db->fetch_object($result);
545
546 $this->id = $obj->rowid;
547 $this->entity = $obj->entity;
548 $this->ref = $obj->ref;
549 $this->socid = $obj->socid;
550 $this->ref_supplier = $obj->ref_supplier;
551 $this->ref_ext = $obj->ref_ext;
552 $this->statut = $obj->status;
553 $this->status = $obj->status;
554 $this->billed = $obj->billed;
555 $this->fk_warehouse = $obj->fk_warehouse;
556 $this->fk_project = $obj->fk_project;
557 $this->user_author_id = $obj->fk_user_author;
558 $this->date_creation = $this->db->jdate($obj->date_creation);
559 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
560 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
561 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
562 $this->date_valid = $this->db->jdate($obj->date_valid); // Date validation
563 $this->model_pdf = $obj->model_pdf;
564 $this->shipping_method_id = $obj->fk_shipping_method;
565 $this->tracking_number = $obj->tracking_number;
566 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
567 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
568 $this->origin_id = $obj->origin_id;
569
570 $this->trueWeight = $obj->weight;
571 $this->weight_units = $obj->weight_units;
572
573 $this->trueWidth = $obj->width;
574 $this->width_units = $obj->size_units;
575 $this->trueHeight = $obj->height;
576 $this->height_units = $obj->size_units;
577 $this->trueDepth = $obj->size;
578 $this->depth_units = $obj->size_units;
579
580 $this->note_public = $obj->note_public;
581 $this->note_private = $obj->note_private;
582
583 // A denormalized value
584 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
585 $this->size_units = $obj->size_units;
586
587 //Incoterms
588 $this->fk_incoterms = $obj->fk_incoterms;
589 $this->location_incoterms = $obj->location_incoterms;
590 $this->label_incoterms = $obj->label_incoterms;
591
592 $this->db->free($result);
593
594 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
595 //$this->pdf_filename = $file;
596
597 // Tracking url
598 $this->getUrlTrackingStatus($obj->tracking_number);
599
600 /*
601 * Thirdparty
602 */
603 $result = $this->fetch_thirdparty();
604
605
606 // Retrieve all extrafields for reception
607 // fetch optionals attributes and labels
608 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
609 $extrafields = new ExtraFields($this->db);
610 $extrafields->fetch_name_optionals_label($this->table_element, true);
611 $this->fetch_optionals();
612
613 /*
614 * Lines
615 */
616 if (empty($obj->origin_id)) {
617 $result = $this->fetch_lines_free();
618 } else {
619 $result = $this->fetch_lines();
620 }
621 if ($result < 0) {
622 return -3;
623 }
624
625 return 1;
626 } else {
627 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
628 $this->error = 'Reception with id '.$id.' not found';
629 return 0;
630 }
631 } else {
632 $this->error = $this->db->error();
633 return -1;
634 }
635 }
636
644 public function valid($user, $notrigger = 0)
645 {
646 global $conf, $langs;
647
648 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
649
650 dol_syslog(get_class($this)."::valid");
651
652 // Protection
653 if ($this->statut) {
654 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
655 return 0;
656 }
657
658 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
659 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
660 $this->error = 'Permission denied';
661 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
662 return -1;
663 }
664
665 $this->db->begin();
666
667 $error = 0;
668
669 // Define new ref
670 $soc = new Societe($this->db);
671 $soc->fetch($this->socid);
672
673
674 // Define new ref
675 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
676 $numref = $this->getNextNumRef($soc);
677 } else {
678 $numref = (string) $this->ref;
679 }
680
681 $this->newref = dol_sanitizeFileName($numref);
682
683 $now = dol_now();
684
685 // Validate
686 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
687 $sql .= " ref='".$this->db->escape($numref)."'";
688 $sql .= ", fk_statut = 1";
689 $sql .= ", date_valid = '".$this->db->idate($now)."'";
690 $sql .= ", fk_user_valid = ".((int) $user->id);
691 $sql .= " WHERE rowid = ".((int) $this->id);
692 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
693 $resql = $this->db->query($sql);
694 if (!$resql) {
695 $this->error = $this->db->lasterror();
696 $error++;
697 }
698
699 // If stock increment is done on reception (recommended choice)
700 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
701 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
702
703 $langs->load("agenda");
704
705 // Loop on each product line to add a stock movement
706 // TODO in future, reception lines may not be linked to order line
707 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice, cd.remise_percent,";
708 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
709 $sql .= " ed.eatby, ed.sellby, ed.batch,";
710 $sql .= " ed.fk_elementdet, ed.cost_price";
711 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
712 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
713 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
714 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
715 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
716
717 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
718 $resql = $this->db->query($sql);
719 if ($resql) {
720 $cpt = $this->db->num_rows($resql);
721 for ($i = 0; $i < $cpt; $i++) {
722 $obj = $this->db->fetch_object($resql);
723
724 $qty = $obj->qty;
725
726 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
727 continue;
728 }
729
730 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
731
732 //var_dump($this->lines[$i]);
733 $mouvS = new MouvementStock($this->db);
734 $mouvS->origin = &$this;
735 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
736
737 if (empty($obj->batch)) {
738 // line without batch detail
739
740 // 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.
741 $inventorycode = '';
742 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
743
744 if (intval($result) < 0) {
745 $error++;
746 $this->setErrorsFromObject($mouvS);
747 break;
748 }
749 } else {
750 // line with batch detail
751
752 // 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.
753 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
754 $inventorycode = '';
755 $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);
756
757 if (intval($result) < 0) {
758 $error++;
759 $this->setErrorsFromObject($mouvS);
760 break;
761 }
762 }
763 }
764 } else {
765 $this->db->rollback();
766 $this->error = $this->db->error();
767 return -2;
768 }
769 }
770
771 if (!$error && $this->origin_id > 0) {
772 // Change status of purchase order to "reception in process" or "totally received"
773 $status = $this->getStatusDispatch();
774 if ($status < 0) {
775 $error++;
776 } else {
777 $trigger_key = '';
778 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
779 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
780 if ($ret < 0) {
781 $error++;
782 $this->errors = array_merge($this->errors, $this->origin_object->errors);
783 }
784 } else {
785 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
786 if ($ret < 0) {
787 $error++;
788 }
789 }
790 }
791 }
792
793 if (!$error && !$notrigger) {
794 // Call trigger
795 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
796 if ($result < 0) {
797 $error++;
798 }
799 // End call triggers
800 }
801
802 if (!$error) {
803 $this->oldref = $this->ref;
804
805 // Rename directory if dir was a temporary ref
806 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
807 // Now we rename also files into index
808 $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)."'";
809 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
810 $resql = $this->db->query($sql);
811 if (!$resql) {
812 $error++;
813 $this->error = $this->db->lasterror();
814 }
815 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
816 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
817 $resql = $this->db->query($sql);
818 if (!$resql) {
819 $error++;
820 $this->error = $this->db->lasterror();
821 }
822
823 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
824 $oldref = dol_sanitizeFileName($this->ref);
825 $newref = dol_sanitizeFileName($numref);
826 $dirsource = $conf->reception->dir_output.'/'.$oldref;
827 $dirdest = $conf->reception->dir_output.'/'.$newref;
828 if (!$error && file_exists($dirsource)) {
829 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
830
831 if (@rename($dirsource, $dirdest)) {
832 dol_syslog("Rename ok");
833 // Rename docs starting with $oldref with $newref
834 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
835 foreach ($listoffiles as $fileentry) {
836 $dirsource = $fileentry['name'];
837 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
838 $dirsource = $fileentry['path'].'/'.$dirsource;
839 $dirdest = $fileentry['path'].'/'.$dirdest;
840 @rename($dirsource, $dirdest);
841 }
842 }
843 }
844 }
845 }
846
847 // Set new ref and current status
848 if (!$error) {
849 $this->ref = $numref;
850 $this->statut = self::STATUS_VALIDATED;
851 $this->status = self::STATUS_VALIDATED;
852 }
853
854 if (!$error) {
855 $this->db->commit();
856 return 1;
857 } else {
858 foreach ($this->errors as $errmsg) {
859 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
860 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
861 }
862 $this->db->rollback();
863 return -1 * $error;
864 }
865 }
866
872 public function getStatusDispatch()
873 {
874 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
875 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
876
878
879 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
880 if (empty($this->origin_object)) {
881 $this->fetch_origin();
882 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
883 $res = $this->origin_object->fetch_lines();
884 $this->commandeFournisseur = null; // deprecated
885 if ($res < 0) {
886 return $res;
887 }
888 } elseif ($this->origin_object instanceof CommandeFournisseur && empty($this->origin_object->lines)) {
889 $res = $this->origin_object->fetch_lines();
890 $this->commandeFournisseur = $this->origin_object; // deprecated
891 if ($res < 0) {
892 return $res;
893 }
894 }
895 }
896
897 $qty_received = array();
898 $qty_wished = array();
899
900 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
901 $filter = array('t.fk_element' => $this->origin_id);
902 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
903 $filter['t.status'] = 1; // Restrict to lines with status validated
904 }
905
906 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
907 if ($ret < 0) {
908 $this->setErrorsFromObject($supplierorderdispatch);
909 return $ret;
910 } else {
911 // Lines of draft receptions are not received yet
912 $draft_lines = array();
913 $sql = "SELECT rb.rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch as rb";
914 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."reception as r ON r.rowid = rb.fk_reception";
915 $sql .= " WHERE rb.fk_element = ".((int) $this->origin_id);
916 $sql .= " AND r.fk_statut = ".self::STATUS_DRAFT;
917 $resql = $this->db->query($sql);
918 if (!$resql) {
919 $this->error = $this->db->lasterror();
920 return -1;
921 }
922 while ($obj = $this->db->fetch_object($resql)) {
923 $draft_lines[(int) $obj->rowid] = (int) $obj->rowid;
924 }
925 $this->db->free($resql);
926
927 // build array with quantity received by product in all supplier orders (origin)
928 foreach ($supplierorderdispatch->lines as $dispatch_line) {
929 if (isset($draft_lines[(int) $dispatch_line->id])) {
930 continue;
931 }
932 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
933 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
934 } else {
935 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
936 }
937 }
938
939 // qty wished in origin (purchase order, ...)
940 foreach ($this->origin_object->lines as $origin_line) {
941 // exclude lines not qualified for reception
942 if (($origin_line->product_type > 0 && (!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') || empty($origin_line->stockable_product))) || $origin_line->product_type > 1) {
943 continue;
944 }
945 if (array_key_exists($origin_line->fk_product, $qty_wished)) {
946 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
947 } else {
948 $qty_wished[$origin_line->fk_product] = $origin_line->qty;
949 }
950 }
951
952 // compare array
953 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
954 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
955 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
956
957 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
959 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
960 // set totally received if more products received than ordered
961 $close = 0;
962
963 if (count($diff_array) > 0) {
964 // there are some difference between the two arrays
965 // scan the array of results
966 foreach ($diff_array as $key => $value) {
967 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
968 if ($qty_received[$key] >= $qty_wished[$key]) {
969 $close++;
970 }
971 }
972 }
973
974 if ($close == count($diff_array)) {
975 // all the products are received equal or more than the ordered quantity
977 }
978 }
979 }
980 }
981
982 return $status;
983 }
984
1001 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
1002 {
1003 // Instantiated below: required here because a caller outside the
1004 // reception card (the REST API, a job) has not loaded them.
1005 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1006 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1007
1008 global $conf, $langs, $user;
1009
1010 $num = count($this->lines);
1011 $line = new CommandeFournisseurDispatch($this->db);
1012
1013 $line->fk_entrepot = $entrepot_id;
1014 $line->fk_commandefourndet = $id;
1015 $line->qty = $qty;
1016
1017 $supplierorderline = new CommandeFournisseurLigne($this->db);
1018 $result = $supplierorderline->fetch($id);
1019 if ($result <= 0) {
1020 $this->setErrorsFromObject($supplierorderline);
1021 return -1;
1022 }
1023
1024 $fk_product = 0;
1025 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
1026 $fk_product = $supplierorderline->fk_product;
1027
1028 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
1029 $langs->load("errors");
1030 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
1031 return -1;
1032 }
1033 }
1034
1035 // Check batch is set
1036 $product = new Product($this->db);
1037 $product->fetch($fk_product);
1038 if (isModEnabled('productbatch')) {
1039 $langs->load("errors");
1040 if (!empty($product->status_batch) && empty($batch)) {
1041 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
1042 return -1;
1043 } elseif (empty($product->status_batch) && !empty($batch)) {
1044 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
1045 return -1;
1046 }
1047
1048 // check sell-by / eat-by date is mandatory
1049 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
1050 if (!empty($errorMsgArr)) {
1051 $errorMessage = '<b>' . $product->ref . '</b> : ';
1052 $errorMessage .= '<ul>';
1053 foreach ($errorMsgArr as $errorMsg) {
1054 $errorMessage .= '<li>' . $errorMsg . '</li>';
1055 }
1056 $errorMessage .= '</ul>';
1057 $this->error = $errorMessage;
1058 return -1;
1059 }
1060 }
1061 unset($product);
1062
1063 // extrafields
1064 $line->array_options = $supplierorderline->array_options;
1065 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
1066 foreach ($array_options as $key => $value) {
1067 $line->array_options[$key] = $value;
1068 }
1069 }
1070
1071 $line->fk_product = $fk_product;
1072 $line->fk_commande = $supplierorderline->fk_commande;
1073 $line->fk_user = $user->id;
1074 $line->comment = $comment;
1075 $line->batch = $batch;
1076 $line->eatby = $eatby;
1077 $line->sellby = $sellby;
1078 $line->status = 1;
1079 $line->cost_price = $cost_price;
1080 $line->fk_reception = $this->id;
1081
1082 $this->lines[$num] = $line;
1083
1084 return $num;
1085 }
1086
1103 public function addlinefree($qty, $element_type, $fk_product, $fk_unit, $rang, $description, $array_options = [], $cost_price = 0, $ref_fourn = '', $fk_entrepot = 0, $batch = '')
1104 {
1105 global $mysoc, $langs, $user;
1106
1107 if ($this->status == self::STATUS_DRAFT) {
1108 if (empty($rang)) {
1109 $rang = 0;
1110 }
1111
1112 $qty = (float) price2num($qty);
1113
1114 $this->db->begin();
1115
1116 // Rank to use
1117 $ranktouse = $this->rang;
1118 if ($ranktouse == -1) {
1119 $rangmax = $this->line_max($this->fk_reception);
1120 $ranktouse = $rangmax + 1;
1121 }
1122
1123 // Insert line
1124 $this->line = new ReceptionLineBatch($this->db);
1125 $this->line->fk_reception = $this->id;
1126 $this->line->element_type = $element_type;
1127 $this->line->fk_product = $fk_product;
1128 $this->line->description = $description;
1129
1130 $this->line->qty = (float) $qty;
1131 $this->line->fk_unit = $fk_unit;
1132 $this->line->rang = $ranktouse;
1133 $this->line->cost_price = (float) $cost_price;
1134 $this->line->ref_fourn = trim((string) $ref_fourn);
1135 if ((int) $fk_entrepot > 0) {
1136 $this->line->fk_entrepot = (int) $fk_entrepot;
1137 }
1138 if ((string) $batch !== '') {
1139 $this->line->batch = trim((string) $batch);
1140 }
1141
1142 if (is_array($array_options) && count($array_options) > 0) {
1143 $this->line->array_options = $array_options;
1144 }
1145
1146 $result = $this->line->insert($user);
1147 if ($result > 0) {
1148 if (!isset($this->context['createfromclone'])) {
1149 if ($this->fk_reception) {
1150 $this->line_order(true, 'DESC');
1151 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) {
1152 $linecount = count($this->lines);
1153 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
1154 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1155 }
1156 }
1157 $this->lines[] = $this->line;
1158 }
1159
1160 $this->db->commit();
1161 return $this->line->id;
1162 } else {
1163 $this->error = $this->line->error;
1164 dol_syslog(get_class($this)."::addlinefree error=".$this->error, LOG_ERR);
1165 $this->db->rollback();
1166 return -2;
1167 }
1168 } else {
1169 dol_syslog(get_class($this)."::addlinefree status of reception must be Draft to allow use of ->addlinefree()", LOG_ERR);
1170 return -3;
1171 }
1172 }
1173
1192 public function updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options = array(), $cost_price = null, $ref_fourn = null, $fk_entrepot = -1, $batch = null)
1193 {
1194 global $mysoc, $langs, $user;
1195
1196 if ($this->statut == self::STATUS_DRAFT) {
1197 $this->db->begin();
1198
1199 if (empty($qty)) {
1200 $qty = 0;
1201 }
1202 if (empty($rang)) {
1203 $rang = 0;
1204 }
1205
1206 $qty = (float) $qty;
1207 $description = trim((string) $description);
1208
1209 // Fetch current line from the database and then clone the object and set it in $oldline property
1210 $line = new ReceptionLineBatch($this->db);
1211
1212 $line->fetch($rowid);
1213 $line->fetch_optionals();
1214
1215 if (!empty($line->fk_product)) {
1216 $product = new Product($this->db);
1217 $result = $product->fetch($line->fk_product);
1218 $product_type = $product->type;
1219 }
1220
1221 $staticline = clone $line;
1222
1223 $line->oldline = $staticline;
1224 $this->line = $line;
1225 $this->line->context = $this->context;
1226 $this->line->rang = $rang;
1227 $this->line->fk_reception = $this->id;
1228 $this->line->element_type = $element_type;
1229 $this->line->fk_product = $line->fk_product;
1230 $this->line->qty = $qty;
1231 $this->line->fk_unit = $fk_unit;
1232 $this->line->description = $description;
1233 if ($cost_price !== null && $cost_price !== '') {
1234 $this->line->cost_price = (float) $cost_price;
1235 }
1236 if ($ref_fourn !== null) {
1237 $this->line->ref_fourn = trim((string) $ref_fourn);
1238 }
1239 if ((int) $fk_entrepot >= 0) {
1240 $this->line->fk_entrepot = ((int) $fk_entrepot > 0 ? (int) $fk_entrepot : 0); // 0 clears the destination warehouse
1241 }
1242 if ($batch !== null) {
1243 $this->line->batch = trim((string) $batch);
1244 }
1245
1246 if (is_array($array_options) && count($array_options) > 0) {
1247 // We replace values in this->line->array_options only for entries defined into $array_options
1248 foreach ($array_options as $key => $value) {
1249 $this->line->array_options[$key] = $array_options[$key];
1250 }
1251 }
1252
1253
1254 $result = $this->line->update($user, $notrigger);
1255 if ($result > 0) {
1256 // Reorder if child line
1257
1258
1259 $this->db->commit();
1260 return $result;
1261 } else {
1262 $this->error = $this->line->error;
1263
1264 $this->db->rollback();
1265 return -1;
1266 }
1267 } else {
1268 $this->error = get_class($this)."::updatelinefree reception status makes operation forbidden";
1269 $this->errors = array('ReceptionStatusMakeOperationForbidden');
1270 return -2;
1271 }
1272 }
1273
1274 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1280 public function fetch_lines_free()
1281 {
1282 // phpcs:enable
1283 $this->lines = array();
1284
1285 $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';
1286 $sql .= ' FROM '.MAIN_DB_PREFIX.'receptiondet_batch as rc';
1287 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = rc.fk_product)';
1288 $sql .= ' WHERE rc.fk_reception = '.((int) $this->id);
1289
1290 $sql .= ' ORDER BY rc.rang, rc.rowid';
1291
1292 dol_syslog(get_class($this)."::fetch_lines_free", LOG_DEBUG);
1293 $result = $this->db->query($sql);
1294 if ($result) {
1295 $num = $this->db->num_rows($result);
1296
1297 $i = 0;
1298 while ($i < $num) {
1299 $objp = $this->db->fetch_object($result);
1300
1301 $line = new ReceptionLineBatch($this->db);
1302
1303 $line->rowid = $objp->rowid;
1304 $line->id = $objp->rowid;
1305 $line->fk_reception = $this->id;
1306
1307 $line->description = $objp->description;
1308 $line->qty = $objp->qty;
1309 $line->fk_entrepot = $objp->fk_entrepot;
1310 $line->cost_price = $objp->cost_price;
1311 $line->ref_fourn = $objp->ref_fourn;
1312 $line->batch = $objp->batch;
1313 $line->eatby = $objp->eatby;
1314 $line->sellby = $objp->sellby;
1315 $line->fk_product = $objp->fk_product;
1316
1317 $line->rang = $objp->rang;
1318
1319
1320 $line->fk_element = $objp->fk_element;
1321 $line->fk_unit = $objp->fk_unit;
1322 $line->fk_elementdet = $objp->fk_elementdet;
1323 $line->fk_element_type = $objp->element_type;
1324 $line->fetch_optionals();
1325
1326 $this->lines[$i] = $line;
1327
1328 $i++;
1329 }
1330
1331 $this->db->free($result);
1332
1333 return 1;
1334 } else {
1335 $this->error = $this->db->error();
1336 return -3;
1337 }
1338 }
1339
1345 public function getLinesArray()
1346 {
1347 return $this->fetch_lines_free();
1348 }
1349
1357 public function update($user = null, $notrigger = 0)
1358 {
1359 global $conf;
1360 $error = 0;
1361
1362 // Clean parameters
1363
1364 if (isset($this->ref)) {
1365 $this->ref = trim($this->ref);
1366 }
1367 if (isset($this->entity)) {
1368 $this->entity = (int) $this->entity;
1369 }
1370 if (isset($this->ref_supplier)) {
1371 $this->ref_supplier = trim($this->ref_supplier);
1372 }
1373 if (isset($this->socid)) {
1374 $this->socid = (int) trim((string) $this->socid);
1375 }
1376 if (isset($this->fk_user_author)) {
1377 $this->fk_user_author = (int) $this->fk_user_author;
1378 }
1379 if (isset($this->fk_user_valid)) {
1380 $this->fk_user_valid = (int) $this->fk_user_valid;
1381 }
1382 if (isset($this->shipping_method_id)) {
1383 $this->shipping_method_id = (int) $this->shipping_method_id;
1384 }
1385 if (isset($this->tracking_number)) {
1386 $this->tracking_number = trim($this->tracking_number);
1387 }
1388 if (isset($this->statut)) {
1389 $this->statut = (int) $this->statut;
1390 }
1391 if (isset($this->trueDepth)) {
1392 $this->trueDepth = price2num($this->trueDepth);
1393 }
1394 if (isset($this->trueWidth)) {
1395 $this->trueWidth = price2num($this->trueWidth);
1396 }
1397 if (isset($this->trueHeight)) {
1398 $this->trueHeight = price2num($this->trueHeight);
1399 }
1400 $this->size_units = (int) $this->size_units;
1401
1402 if (isset($this->trueWeight)) {
1403 $this->weight = price2num($this->trueWeight);
1404 }
1405 $this->weight_units = (int) $this->weight_units;
1406
1407 if (isset($this->note_private)) {
1408 $this->note_private = trim($this->note_private);
1409 }
1410 if (isset($this->note_public)) {
1411 $this->note_public = trim($this->note_public);
1412 }
1413 if (isset($this->model_pdf)) {
1414 $this->model_pdf = trim($this->model_pdf);
1415 }
1416
1417
1418 // Check parameters
1419 // Put here code to add control on parameters values
1420
1421 // Update request
1422 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1423
1424 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1425 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1426 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1427 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1428 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? ((int) $this->fk_user_author) : "null").",";
1429 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1430 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1431 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1432 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1433 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? ((int) $this->shipping_method_id) : "null").",";
1434 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1435 $sql .= " fk_statut=".(isset($this->statut) ? ((int) $this->statut) : "null").",";
1436 $sql .= " height=".(($this->trueHeight != '') ? (float) $this->trueHeight : "null").",";
1437 $sql .= " width=".(($this->trueWidth != '') ? (float) $this->trueWidth : "null").",";
1438 $sql .= " size_units=".((int) $this->size_units).",";
1439 $sql .= " size=".(($this->trueDepth != '') ? (float) $this->trueDepth : "null").",";
1440 $sql .= " weight_units=".((int) $this->weight_units).",";
1441 $sql .= " weight=".(($this->trueWeight != '') ? (float) $this->trueWeight : "null").",";
1442 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1443 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1444 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1445 $sql .= " fk_projet=".((isset($this->fk_project) && $this->fk_project > 0) ? ((int) $this->fk_project) : "null").",";
1446 $sql .= " fk_warehouse=".((isset($this->fk_warehouse) && $this->fk_warehouse > 0) ? ((int) $this->fk_warehouse) : "null").",";
1447 $sql .= " entity = ".((int) $conf->entity);
1448 $sql .= " WHERE rowid=".((int) $this->id);
1449
1450 $this->db->begin();
1451
1452 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1453 $resql = $this->db->query($sql);
1454 if (!$resql) {
1455 $error++;
1456 $this->errors[] = "Error ".$this->db->lasterror();
1457 }
1458
1459 if (!$error) {
1460 if (!$notrigger) {
1461 // Call trigger
1462 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1463 if ($result < 0) {
1464 $error++;
1465 }
1466 // End call triggers
1467 }
1468 }
1469
1470 // Commit or rollback
1471 if ($error) {
1472 foreach ($this->errors as $errmsg) {
1473 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1474 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1475 }
1476 $this->db->rollback();
1477 return -1 * $error;
1478 } else {
1479 $this->db->commit();
1480 return 1;
1481 }
1482 }
1483
1490 public function delete(User $user)
1491 {
1492 global $conf, $langs, $user;
1493 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1494
1495 $error = 0;
1496 $this->error = '';
1497
1498
1499 $this->db->begin();
1500
1501 // Stock control
1502 if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT)
1503 || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED))
1504 ) {
1505 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1506
1507 $langs->load("agenda");
1508
1509 // Loop on each product line to add a stock movement
1510 $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";
1511 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1512 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
1513 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1514 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
1515 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
1516
1517 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1518 $resql = $this->db->query($sql);
1519 if ($resql) {
1520 $cpt = $this->db->num_rows($resql);
1521 for ($i = 0; $i < $cpt; $i++) {
1522 dol_syslog(get_class($this)."::delete movement index ".$i);
1523 $obj = $this->db->fetch_object($resql);
1524
1525 $mouvS = new MouvementStock($this->db);
1526 // we do not log origin because it will be deleted
1527 $mouvS->origin = null;
1528
1529 $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
1530 if ($result < 0) {
1531 $error++;
1532 $this->error = $mouvS->error;
1533 $this->errors = $mouvS->errors;
1534 }
1535 }
1536 } else {
1537 $error++;
1538 $this->errors[] = "Error ".$this->db->lasterror();
1539 }
1540 }
1541
1542 if (!$error) {
1543 $sanitized_main = MAIN_DB_PREFIX.'receptiondet_batch';
1544 $sanitized_ef = $sanitized_main."_extrafields";
1545
1546 $sqlef = "DELETE FROM ".$sanitized_ef." WHERE fk_object IN (SELECT rowid FROM ".$sanitized_main." WHERE fk_reception = ".((int) $this->id).")";
1547
1548 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1549 $sql .= " WHERE fk_reception = ".((int) $this->id);
1550
1551 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1552 // Delete linked object
1553 $res = $this->deleteObjectLinked();
1554 if ($res < 0) {
1555 $error++;
1556 }
1557
1558 if (!$error) {
1559 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1560 $sql .= " WHERE rowid = ".((int) $this->id);
1561
1562 if ($this->db->query($sql)) {
1563 // Call trigger
1564 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1565 if ($result < 0) {
1566 $error++;
1567 }
1568 // End call triggers
1569
1570 if (!empty($this->origin) && $this->origin_id > 0) {
1571 $this->fetch_origin();
1573 '@phan-var-force CommandeFournisseur $origin_object';
1575 if ($origin_object->status == 4) { // If order source of reception is "partially received"
1576 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1577 $origin_object->loadReceptions();
1578 //var_dump($this->$origin->receptions);exit;
1579 if (count($origin_object->receptions) <= 0) {
1580 $origin_object->setStatut(3); // CommandeFournisseur ordered
1581 }
1582 }
1583 }
1584
1585 if (!$error) {
1586 $this->db->commit();
1587
1588 // We delete PDFs
1589 $ref = dol_sanitizeFileName($this->ref);
1590 if (!empty($conf->reception->dir_output)) {
1591 $dir = $conf->reception->dir_output.'/'.$ref;
1592 $file = $dir.'/'.$ref.'.pdf';
1593 if (file_exists($file)) {
1594 if (!dol_delete_file($file)) {
1595 return 0;
1596 }
1597 }
1598 if (file_exists($dir)) {
1599 if (!dol_delete_dir_recursive($dir)) {
1600 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1601 return 0;
1602 }
1603 }
1604 }
1605
1606 return 1;
1607 } else {
1608 $this->db->rollback();
1609 return -1;
1610 }
1611 } else {
1612 $this->error = $this->db->lasterror()." - sql=$sql";
1613 $this->db->rollback();
1614 return -3;
1615 }
1616 } else {
1617 $this->error = $this->db->lasterror()." - sql=$sql";
1618 $this->db->rollback();
1619 return -2;
1620 }
1621 } else {
1622 $this->error = $this->db->lasterror()." - sql=$sql";
1623 $this->db->rollback();
1624 return -1;
1625 }
1626 } else {
1627 $this->db->rollback();
1628 return -1;
1629 }
1630 }
1631
1639 public function deleteLine($user, $lineid)
1640 {
1641 if ($this->status != self::STATUS_DRAFT) {
1642 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1643 return -2;
1644 }
1645
1646 $line = new ReceptionLineBatch($this->db);
1647 if ($line->fetch($lineid) <= 0) {
1648 $this->error = 'ErrorRecordNotFound';
1649 return -1;
1650 }
1651 if ($line->fk_reception != $this->id) {
1652 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1653 return -1;
1654 }
1655
1656 $this->db->begin();
1657 if ($line->delete($user) > 0) {
1658 $this->db->commit();
1659 return 1;
1660 }
1661 $this->error = $line->error;
1662 $this->db->rollback();
1663 return -1;
1664 }
1665
1666 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1672 public function fetch_lines()
1673 {
1674 // Instantiated below: required here because a caller outside the
1675 // reception card (the REST API, a job) has not loaded them.
1676 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1677 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1678
1679 // phpcs:enable
1680 $this->lines = array();
1681
1682 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1683
1684 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1685 $sql .= " WHERE fk_reception = ".((int) $this->id);
1686
1687 $resql = $this->db->query($sql);
1688
1689 if (!empty($resql)) {
1690 while ($obj = $this->db->fetch_object($resql)) {
1691 $line = new CommandeFournisseurDispatch($this->db);
1692
1693 $line->fetch($obj->rowid);
1694
1695 // TODO Remove or keep this ?
1696 $line->fetch_product();
1697
1698 $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';
1699 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1700 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1701 $sql_commfourndet .= ' ORDER BY rang';
1702
1703 $resql_commfourndet = $this->db->query($sql_commfourndet);
1704 if (!empty($resql_commfourndet)) {
1705 $obj = $this->db->fetch_object($resql_commfourndet);
1706 $line->qty_asked = $obj->qty;
1707 $line->description = $obj->description;
1708 $line->desc = $obj->description;
1709 $line->tva_tx = $obj->tva_tx;
1710 $line->vat_src_code = $obj->vat_src_code;
1711 $line->localtax1_tx = $obj->localtax1_tx;
1712 $line->localtax2_tx = $obj->localtax2_tx;
1713 $line->subprice = $obj->subprice;
1714 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1715 $line->remise_percent = $obj->remise_percent;
1716 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1717 $line->ref_supplier = $obj->ref;
1718 $line->total_ht = $obj->total_ht;
1719 $line->total_ttc = $obj->total_ttc;
1720 $line->total_tva = $obj->total_tva;
1721 $line->date_start = $this->db->jdate($obj->date_start);
1722 $line->date_end = $this->db->jdate($obj->date_end);
1723 $line->product_type = $obj->product_type;
1724 } else {
1725 $line->qty_asked = 0;
1726 $line->description = '';
1727 $line->desc = '';
1728 $line->label = $obj->label;
1729 }
1730
1731 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1732 $tva = $pu_ht * $line->tva_tx / 100;
1733 $this->total_ht += $pu_ht;
1734 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1735
1736 $this->total_ttc += $pu_ht + $tva;
1737
1738 if (isModEnabled('productbatch') && !empty($line->batch)) {
1739 $detail_batch = new stdClass();
1740 $detail_batch->eatby = $line->eatby;
1741 $detail_batch->sellby = $line->sellby;
1742 $detail_batch->batch = $line->batch;
1743 $detail_batch->qty = $line->qty;
1744
1745 $line->detail_batch[] = $detail_batch;
1746 }
1747
1748 $this->lines[] = $line;
1749 }
1750
1751 return 1;
1752 } else {
1753 return -1;
1754 }
1755 }
1756
1763 public function getTooltipContentArray($params)
1764 {
1765 global $langs;
1766
1767 $datas = array();
1768 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1769 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1770 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1771
1772 return $datas;
1773 }
1774
1785 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0)
1786 {
1787 global $langs, $hookmanager;
1788
1789 $result = '';
1790 $params = array();
1791 $label = $this->getTooltipContent($params);
1792
1793 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1794
1795 if ($short) {
1796 return $url;
1797 }
1798
1799 $linkclose = '';
1800 if (empty($notooltip)) {
1801 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1802 $label = $langs->trans("Reception");
1803 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1804 }
1805 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1806 $linkclose .= ' class="classfortooltip"';
1807 }
1808
1809 $linkstart = '<a href="'.$url.'"';
1810 $linkstart .= $linkclose.'>';
1811 $linkend = '</a>';
1812
1813 $result .= $linkstart;
1814 if ($withpicto) {
1815 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1816 }
1817 if ($withpicto != 2) {
1818 $result .= $this->ref;
1819 }
1820
1821 $result .= $linkend;
1822
1823 global $action;
1824 $hookmanager->initHooks(array($this->element . 'dao'));
1825 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1826 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1827 if ($reshook > 0) {
1828 $result = $hookmanager->resPrint;
1829 } else {
1830 $result .= $hookmanager->resPrint;
1831 }
1832 return $result;
1833 }
1834
1841 public function getLibStatut($mode = 0)
1842 {
1843 return $this->LibStatut($this->statut, $mode);
1844 }
1845
1846 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1854 public function LibStatut($status, $mode)
1855 {
1856 // phpcs:enable
1857 global $langs;
1858
1859 // List of long language codes for status
1860 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1861 $this->labelStatus[0] = 'StatusReceptionDraft';
1862 // product to receive if stock increase is on close or already received if stock increase is on validation
1863 $this->labelStatus[1] = 'StatusReceptionValidated';
1864 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1865 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1866 }
1867 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1868 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1869 }
1870 $this->labelStatus[2] = 'StatusReceptionProcessed';
1871
1872 // List of short language codes for status
1873 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1874 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1875 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1876 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1877
1878 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1879 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1880
1881 $statusType = 'status'.$status;
1882 if ($status == self::STATUS_VALIDATED) {
1883 $statusType = 'status4';
1884 }
1885 if ($status == self::STATUS_CLOSED) {
1886 $statusType = 'status6';
1887 }
1888
1889 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1890 }
1891
1899 public function getKanbanView($option = '', $arraydata = null)
1900 {
1901 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1902
1903 $return = '<div class="box-flex-item box-flex-grow-zero">';
1904 $return .= '<div class="info-box info-box-sm">';
1905 $return .= '<div class="info-box-icon bg-infobox-action">';
1906 $return .= img_picto('', 'order');
1907 $return .= '</div>';
1908 $return .= '<div class="info-box-content">';
1909 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1910 if ($selected >= 0) {
1911 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1912 }
1913 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1914 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1915 }
1916 /*if (property_exists($this, 'total_ht')) {
1917 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency()).' '.$langs->trans('HT').'</div>';
1918 }*/
1919 if (method_exists($this, 'getLibStatut')) {
1920 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1921 }
1922 $return .= '</div>';
1923 $return .= '</div>';
1924 $return .= '</div>';
1925
1926 return $return;
1927 }
1928
1936 public function initAsSpecimen()
1937 {
1938 global $langs;
1939
1940 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1941 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1942 $now = dol_now();
1943
1944 dol_syslog(get_class($this)."::initAsSpecimen");
1945
1946 $order = new CommandeFournisseur($this->db);
1947 $order->initAsSpecimen();
1948
1949 // Initialise parameters
1950 $this->id = 0;
1951 $this->ref = 'SPECIMEN';
1952 $this->specimen = 1;
1953 $this->statut = 1;
1954 $this->status = 1;
1955 $this->date = $now;
1956 $this->date_creation = $now;
1957 $this->date_valid = $now;
1958 $this->date_delivery = $now;
1959 $this->date_reception = $now + 24 * 3600;
1960 $this->entrepot_id = 0;
1961 $this->socid = 1;
1962 $this->origin_id = 1;
1963 $this->origin_type = 'supplier_order';
1964 $this->origin_object = $order;
1965 $this->note_private = 'Private note';
1966 $this->note_public = 'Public note';
1967 $this->tracking_number = 'TRACKID-ABC123';
1968 $this->fk_incoterms = 1;
1969 $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)
1970 $xnbp = 0;
1971 while ($xnbp < $nbp) {
1972 $line = new CommandeFournisseurDispatch($this->db);
1973 $line->desc = $langs->trans("Description")." ".$xnbp;
1974 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1975 $line->label = $langs->trans("Description")." ".$xnbp;
1976 $line->qty = 10;
1977
1978 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1979
1980 $this->lines[] = $line;
1981 $xnbp++;
1982 }
1983
1984 return 1;
1985 }
1986
1994 public function setDeliveryDate($user, $delivery_date)
1995 {
1996 // phpcs:enable
1997 if ($user->hasRight('reception', 'creer')) {
1998 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1999 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2000 $sql .= " WHERE rowid = ".((int) $this->id);
2001
2002 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
2003 $resql = $this->db->query($sql);
2004 if ($resql) {
2005 $this->date_delivery = $delivery_date;
2006 return 1;
2007 } else {
2008 $this->error = $this->db->error();
2009 return -1;
2010 }
2011 } else {
2012 return -2;
2013 }
2014 }
2015
2023 public function setReceptionDate($user, $reception_date)
2024 {
2025 if ($user->hasRight('reception', 'creer')) {
2026 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2027 $sql .= " SET date_reception = ".($reception_date ? "'".$this->db->idate($reception_date)."'" : 'null');
2028 $sql .= " WHERE rowid = ".((int) $this->id);
2029
2030 dol_syslog(get_class($this)."::setReceptionDate", LOG_DEBUG);
2031 $resql = $this->db->query($sql);
2032 if ($resql) {
2033 $this->date_reception = $reception_date;
2034 return 1;
2035 } else {
2036 $this->error = $this->db->error();
2037 return -1;
2038 }
2039 } else {
2040 return -2;
2041 }
2042 }
2043
2044 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2050 public function fetch_delivery_methods()
2051 {
2052 // phpcs:enable
2053 global $langs;
2054 $this->meths = array();
2055
2056 $sql = "SELECT em.rowid, em.code, em.libelle";
2057 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2058 $sql .= " WHERE em.active = 1";
2059 $sql .= " ORDER BY em.libelle ASC";
2060
2061 $resql = $this->db->query($sql);
2062 if ($resql) {
2063 while ($obj = $this->db->fetch_object($resql)) {
2064 $label = $langs->trans('ReceptionMethod'.$obj->code);
2065 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
2066 }
2067 }
2068 }
2069
2070 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2077 public function list_delivery_methods($id = 0)
2078 {
2079 // phpcs:enable
2080 global $langs;
2081
2082 $this->listmeths = array();
2083 $i = 0;
2084
2085 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
2086 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2087 if (!empty($id)) {
2088 $sql .= " WHERE em.rowid = ".((int) $id);
2089 }
2090
2091 $resql = $this->db->query($sql);
2092 if ($resql) {
2093 while ($obj = $this->db->fetch_object($resql)) {
2094 $this->listmeths[$i]['rowid'] = $obj->rowid;
2095 $this->listmeths[$i]['code'] = $obj->code;
2096 $label = $langs->trans('ReceptionMethod'.$obj->code);
2097 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
2098 $this->listmeths[$i]['description'] = $obj->description;
2099 $this->listmeths[$i]['tracking'] = $obj->tracking;
2100 $this->listmeths[$i]['active'] = $obj->active;
2101 $i++;
2102 }
2103 }
2104 }
2105
2112 public function getUrlTrackingStatus($value = '')
2113 {
2114 if (!empty($this->shipping_method_id)) {
2115 $sql = "SELECT em.code, em.tracking";
2116 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
2117 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
2118
2119 $resql = $this->db->query($sql);
2120 if ($resql) {
2121 if ($obj = $this->db->fetch_object($resql)) {
2122 $tracking = $obj->tracking;
2123 }
2124 }
2125 }
2126
2127 if (!empty($tracking) && !empty($value)) {
2128 $url = str_replace('{TRACKID}', $value, $tracking);
2129 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
2130 } else {
2131 $this->tracking_url = $value;
2132 }
2133 }
2134
2140 public function setClosed()
2141 {
2142 global $conf, $langs, $user;
2143
2144 $error = 0;
2145
2146 // Protection. This avoid to move stock later when we should not
2147 if ($this->statut == Reception::STATUS_CLOSED) {
2148 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
2149 return 0;
2150 }
2151
2152 $this->db->begin();
2153
2154 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
2155 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2156
2157 $resql = $this->db->query($sql);
2158 if ($resql) {
2159 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
2160 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
2161 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2162
2163 $order = new CommandeFournisseur($this->db);
2164 $order->fetch($this->origin_id);
2165
2166 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
2167
2168 $receptions_match_order = 1;
2169 foreach ($order->lines as $line) {
2170 $lineid = $line->id;
2171 $qty = $line->qty;
2172 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
2173 $receptions_match_order = 0;
2174 $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';
2175 dol_syslog($text);
2176 break;
2177 }
2178 }
2179 if ($receptions_match_order) {
2180 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');
2181 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
2182 }
2183 }
2184
2185 $this->statut = self::STATUS_CLOSED;
2186 $this->status = self::STATUS_CLOSED;
2187
2188 // If stock increment is done on closing
2189 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2190 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2191
2192 $langs->load("agenda");
2193
2194 // Loop on each product line to add a stock movement
2195 // TODO possibility to receive from a proposal or other origin ?
2196 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2197 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2198 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2199 $sql .= " ed.fk_elementdet, ed.cost_price";
2200 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2201 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2202 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2203 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2204 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2205
2206 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2207 $resql = $this->db->query($sql);
2208
2209 if ($resql) {
2210 $cpt = $this->db->num_rows($resql);
2211 for ($i = 0; $i < $cpt; $i++) {
2212 $obj = $this->db->fetch_object($resql);
2213
2214 $qty = $obj->qty;
2215
2216 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2217 continue;
2218 }
2219
2220 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid);
2221
2222 $mouvS = new MouvementStock($this->db);
2223 $mouvS->origin = &$this;
2224 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2225
2226 if (empty($obj->batch)) {
2227 // line without batch detail
2228
2229 // 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
2230 $inventorycode = '';
2231 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2232 if ($result < 0) {
2233 $this->error = $mouvS->error;
2234 $this->errors = $mouvS->errors;
2235 $error++;
2236 break;
2237 }
2238 } else {
2239 // line with batch detail
2240
2241 // 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
2242 $inventorycode = '';
2243 $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);
2244
2245 if ($result < 0) {
2246 $this->error = $mouvS->error;
2247 $this->errors = $mouvS->errors;
2248 $error++;
2249 break;
2250 }
2251 }
2252 }
2253 } else {
2254 $this->error = $this->db->lasterror();
2255 $error++;
2256 }
2257 }
2258
2259 // Call trigger
2260 if (!$error) {
2261 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
2262 if ($result < 0) {
2263 $error++;
2264 }
2265 }
2266 } else {
2267 dol_print_error($this->db);
2268 $error++;
2269 }
2270
2271 if (!$error) {
2272 $this->db->commit();
2273 return 1;
2274 } else {
2275 $this->statut = self::STATUS_VALIDATED;
2276 $this->status = self::STATUS_VALIDATED;
2277 $this->db->rollback();
2278 return -1;
2279 }
2280 }
2281
2287 public function setBilled()
2288 {
2289 global $user;
2290 $error = 0;
2291
2292 $this->db->begin();
2293
2294 if ($this->statut == Reception::STATUS_VALIDATED) {
2295 // do not close if already closed
2296 $this->setClosed();
2297 }
2298
2299 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
2300 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2301
2302 $resql = $this->db->query($sql);
2303 if ($resql) {
2304 $this->billed = 1;
2305
2306 // Call trigger
2307 $result = $this->call_trigger('RECEPTION_BILLED', $user);
2308 if ($result < 0) {
2309 $this->billed = 0;
2310 $error++;
2311 }
2312 } else {
2313 $error++;
2314 $this->errors[] = $this->db->lasterror;
2315 }
2316
2317 if (empty($error)) {
2318 $this->db->commit();
2319 return 1;
2320 } else {
2321 $this->db->rollback();
2322 return -1;
2323 }
2324 }
2325
2331 public function reOpen()
2332 {
2333 global $langs, $user;
2334
2335 $error = 0;
2336
2337 $this->db->begin();
2338
2339 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
2340 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
2341
2342 $resql = $this->db->query($sql);
2343 $rollbackStatus = $this->status;
2344 $rollbackBilled = $this->billed;
2345 if ($resql) {
2346 $this->statut = self::STATUS_VALIDATED;
2347 $this->status = self::STATUS_VALIDATED;
2348 $this->billed = 0;
2349
2350 // If stock increment is done on closing
2351 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
2352 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2353 $numref = (string) $this->ref;
2354 $langs->load("agenda");
2355
2356 // Loop on each product line to add a stock movement
2357 // TODO possibility to receive from a proposal or other origin
2358 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2359 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2360 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2361 $sql .= " ed.fk_elementdet, ed.cost_price";
2362 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2363 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2364 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2365 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2366 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2367
2368 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2369 $resql = $this->db->query($sql);
2370 if ($resql) {
2371 $cpt = $this->db->num_rows($resql);
2372 for ($i = 0; $i < $cpt; $i++) {
2373 $obj = $this->db->fetch_object($resql);
2374
2375 $qty = $obj->qty;
2376
2377 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2378 continue;
2379 }
2380 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2381
2382 //var_dump($this->lines[$i]);
2383 $mouvS = new MouvementStock($this->db);
2384 $mouvS->origin = &$this;
2385 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2386
2387 if (empty($obj->batch)) {
2388 // line without batch detail
2389
2390 // 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
2391 $inventorycode = '';
2392 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
2393
2394 if ($result < 0) {
2395 $this->error = $mouvS->error;
2396 $this->errors = $mouvS->errors;
2397 $error++;
2398 break;
2399 }
2400 } else {
2401 // line with batch detail
2402
2403 // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
2404 $inventorycode = '';
2405 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock, $inventorycode);
2406 if ($result < 0) {
2407 $this->error = $mouvS->error;
2408 $this->errors = $mouvS->errors;
2409 $error++;
2410 break;
2411 }
2412 }
2413 }
2414 } else {
2415 $this->error = $this->db->lasterror();
2416 $error++;
2417 }
2418 }
2419
2420 if (!$error) {
2421 // Call trigger
2422 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
2423 if ($result < 0) {
2424 $error++;
2425 }
2426 }
2427
2428 if (!$error && $this->origin == 'order_supplier') {
2429 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
2430
2431 $commande = new CommandeFournisseur($this->db);
2432 $commande->fetch($this->origin_id);
2433 $result = $commande->setStatus($user, 4);
2434 if ($result < 0) {
2435 $error++;
2436 $this->error = $commande->error;
2437 $this->errors = $commande->errors;
2438 }
2439 }
2440 } else {
2441 $error++;
2442 $this->errors[] = $this->db->lasterror();
2443 }
2444
2445 if (!$error) {
2446 $this->db->commit();
2447 return 1;
2448 } else {
2449 $this->statut = $this->status = $rollbackStatus;
2450 $this->billed = $rollbackBilled;
2451 $this->db->rollback();
2452 return -1;
2453 }
2454 }
2455
2462 public function setDraft($user)
2463 {
2464 // phpcs:enable
2465 global $langs;
2466
2467 $error = 0;
2468
2469 // Protection
2470 if ($this->statut <= self::STATUS_DRAFT) {
2471 return 0;
2472 }
2473
2474 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
2475 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
2476 $this->error = 'Permission denied';
2477 return -1;
2478 }
2479
2480 $this->db->begin();
2481
2482 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2483 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2484 $sql .= " WHERE rowid = ".((int) $this->id);
2485
2486 dol_syslog(__METHOD__, LOG_DEBUG);
2487 if ($this->db->query($sql)) {
2488 // If stock increment is done on closing
2489 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2490 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2491
2492 $langs->load("agenda");
2493
2494 // Loop on each product line to add a stock movement
2495 // TODO possibility to receive from a proposal or other origin
2496 $sql = "SELECT COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) as fk_product, cd.subprice,";
2497 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2498 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2499 $sql .= " ed.fk_elementdet, ed.cost_price";
2500 $sql .= " FROM ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2501 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = ed.fk_elementdet";
2502 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2503 $sql .= " AND COALESCE(NULLIF(ed.fk_product, 0), cd.fk_product) IS NOT NULL";
2504 $sql .= " AND ed.fk_entrepot IS NOT NULL AND ed.fk_entrepot > 0"; // A line without destination warehouse generates no stock movement (sample, servicing, ...)
2505
2506 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2507 $resql = $this->db->query($sql);
2508 if ($resql) {
2509 $cpt = $this->db->num_rows($resql);
2510 for ($i = 0; $i < $cpt; $i++) {
2511 $obj = $this->db->fetch_object($resql);
2512
2513 $qty = $obj->qty;
2514
2515 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
2516 continue;
2517 }
2518
2519 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
2520
2521 //var_dump($this->lines[$i]);
2522 $mouvS = new MouvementStock($this->db);
2523 $mouvS->origin = &$this;
2524 $mouvS->setOrigin($this->element, $this->id, $obj->fk_elementdet, $obj->rowid);
2525
2526 if (empty($obj->batch)) {
2527 // line without batch detail
2528
2529 // 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
2530 $inventorycode = '';
2531 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2532 if ($result < 0) {
2533 $this->error = $mouvS->error;
2534 $this->errors = $mouvS->errors;
2535 $error++;
2536 break;
2537 }
2538 } else {
2539 // line with batch detail
2540
2541 // 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
2542 $inventorycode = '';
2543 $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);
2544 if ($result < 0) {
2545 $this->error = $mouvS->error;
2546 $this->errors = $mouvS->errors;
2547 $error++;
2548 break;
2549 }
2550 }
2551 }
2552 } else {
2553 $this->error = $this->db->lasterror();
2554 $error++;
2555 }
2556 }
2557
2558 if (!$error) {
2559 // Call trigger
2560 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2561 if ($result < 0) {
2562 $error++;
2563 }
2564 }
2565 if ($this->origin == 'order_supplier') {
2566 if (!empty($this->origin) && $this->origin_id > 0) {
2567 $this->fetch_origin();
2568 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2569 // Check if there is no more reception validated.
2570 $this->origin_object->fetchObjectLinked();
2571 $setStatut = 1;
2572 if (!empty($this->origin_object->linkedObjects['reception'])) {
2573 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2574 if ($rcption->statut > 0) {
2575 $setStatut = 0;
2576 break;
2577 }
2578 }
2579 //var_dump($this->$origin->receptions);exit;
2580 if ($setStatut) {
2581 $this->origin_object->setStatut(3); // CommandeFournisseur ordered
2582 }
2583 }
2584 }
2585 }
2586 }
2587
2588 if (!$error) {
2589 $this->statut = self::STATUS_DRAFT;
2590 $this->status = self::STATUS_DRAFT;
2591 $this->db->commit();
2592 return 1;
2593 } else {
2594 $this->db->rollback();
2595 return -1;
2596 }
2597 } else {
2598 $this->error = $this->db->error();
2599 $this->db->rollback();
2600 return -1;
2601 }
2602 }
2603
2614 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2615 {
2616 global $conf, $langs;
2617
2618 $langs->load("receptions");
2619
2620 if (!dol_strlen($modele)) {
2621 $modele = 'squille';
2622
2623 if ($this->model_pdf) {
2624 $modele = $this->model_pdf;
2625 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2626 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2627 }
2628 }
2629
2630 $modelpath = "core/modules/reception/doc/";
2631
2632 $this->fetch_origin();
2633
2634 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2635 }
2636
2645 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2646 {
2647 $tables = array('reception');
2648
2649 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2650 }
2651
2660 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2661 {
2662 $tables = array(
2663 'receptiondet_batch'
2664 );
2665
2666 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2667 }
2668}
$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.
getTooltipContent($params)
getTooltipContent
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.
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)
getTooltipContentArray($params)
Return the array of data to show into the tooltip.
__construct($db)
Constructor.
updatelinefree($rowid, $qty, $element_type, $fk_product, $fk_unit, $rang, $description, $notrigger, $array_options=array(), $cost_price=null, $ref_fourn=null, $fk_entrepot=-1, $batch=null)
Update a simple reception line.
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.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
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