dolibarr 21.0.0-beta
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-2024 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
37require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
38require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
40if (isModEnabled("propal")) {
41 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
42}
43if (isModEnabled('order')) {
44 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
45}
46
47
52{
53 use CommonIncoterm;
54
58 public $code = "";
59
63 public $element = "reception";
64
68 public $fk_element = "fk_reception";
69 public $table_element = "reception";
70 public $table_element_line = "receptiondet_batch";
71
75 public $picto = 'dollyrevert';
76
80 public $socid;
84 public $ref_supplier;
85
89 public $entrepot_id;
93 public $tracking_number;
97 public $tracking_url;
101 public $billed;
102
106 public $weight;
110 public $trueWeight;
114 public $weight_units;
118 public $trueWidth;
122 public $width_units;
126 public $trueHeight;
130 public $height_units;
134 public $trueDepth;
138 public $depth_units;
142 public $trueSize;
146 public $size_units;
150 public $user_author_id;
151
155 public $date_delivery;
156
162 public $date;
163
167 public $date_reception;
168
172 public $date_valid;
173
177 public $meths;
181 public $listmeths; // List of carriers
182
186 public $lines = array();
187
188
193 public $detail_batch;
194
195 const STATUS_DRAFT = 0;
196 const STATUS_VALIDATED = 1;
197 const STATUS_CLOSED = 2;
198
199
205 public function __construct($db)
206 {
207 $this->db = $db;
208
209 $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
210 }
211
218 public function getNextNumRef($soc)
219 {
220 global $langs, $conf;
221 $langs->load("receptions");
222
223 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
224 $mybool = false;
225
226 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
227 $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
228
229 // Include file with class
230 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
231
232 foreach ($dirmodels as $reldir) {
233 $dir = dol_buildpath($reldir."core/modules/reception/");
234
235 // Load file with numbering class (if found)
236 $mybool = ((bool) @include_once $dir.$file) || $mybool;
237 }
238
239 if (!$mybool) {
240 dol_print_error(null, "Failed to include file ".$file);
241 return '';
242 }
243
244 $obj = new $classname();
245 '@phan-var-force ModelNumRefReception $obj';
246
247 $numref = $obj->getNextValue($soc, $this);
248
249 if ($numref != "") {
250 return $numref;
251 } else {
252 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
253 return "";
254 }
255 } else {
256 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
257 return "";
258 }
259 }
260
268 public function create($user, $notrigger = 0)
269 {
270 global $conf;
271
272 $now = dol_now();
273
274 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
275 $error = 0;
276
277 // Clean parameters
278 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
279 if (empty($this->fk_project)) {
280 $this->fk_project = 0;
281 }
282 if (empty($this->weight_units)) {
283 $this->weight_units = 0;
284 }
285 if (empty($this->size_units)) {
286 $this->size_units = 0;
287 }
288
289 $this->user = $user;
290
291 $this->db->begin();
292
293 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
294 $sql .= "ref";
295 $sql .= ", entity";
296 $sql .= ", ref_supplier";
297 $sql .= ", date_creation";
298 $sql .= ", fk_user_author";
299 $sql .= ", date_reception";
300 $sql .= ", date_delivery";
301 $sql .= ", fk_soc";
302 $sql .= ", fk_projet";
303 $sql .= ", fk_shipping_method";
304 $sql .= ", tracking_number";
305 $sql .= ", weight";
306 $sql .= ", size";
307 $sql .= ", width";
308 $sql .= ", height";
309 $sql .= ", weight_units";
310 $sql .= ", size_units";
311 $sql .= ", note_private";
312 $sql .= ", note_public";
313 $sql .= ", model_pdf";
314 $sql .= ", fk_incoterms, location_incoterms";
315 $sql .= ") VALUES (";
316 $sql .= "'(PROV)'";
317 $sql .= ", ".((int) $conf->entity);
318 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
319 $sql .= ", '".$this->db->idate($now)."'";
320 $sql .= ", ".((int) $user->id);
321 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
322 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
323 $sql .= ", ".((int) $this->socid);
324 $sql .= ", ".((int) $this->fk_project);
325 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
326 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
327 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
328 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
329 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
330 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
331 $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((float) $this->weight_units));
332 $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((float) $this->size_units));
333 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
334 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
335 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
336 $sql .= ", ".(int) $this->fk_incoterms;
337 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
338 $sql .= ")";
339
340 dol_syslog(get_class($this)."::create", LOG_DEBUG);
341
342 $resql = $this->db->query($sql);
343
344 if ($resql) {
345 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
346
347 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
348 $sql .= " SET ref = '(PROV".((int) $this->id).")'";
349 $sql .= " WHERE rowid = ".((int) $this->id);
350
351 dol_syslog(get_class($this)."::create", LOG_DEBUG);
352 if ($this->db->query($sql)) {
353 // Insert of lines
354 $num = count($this->lines);
355 for ($i = 0; $i < $num; $i++) {
356 $this->lines[$i]->fk_reception = $this->id;
357
358 if (!$this->lines[$i]->create($user) > 0) {
359 $error++;
360 }
361 }
362
363 if (!$error && $this->id && $this->origin_id) {
364 $ret = $this->add_object_linked();
365 if (!$ret) {
366 $error++;
367 }
368 }
369
370 // Create extrafields
371 if (!$error) {
372 $result = $this->insertExtraFields();
373 if ($result < 0) {
374 $error++;
375 }
376 }
377
378 if (!$error && !$notrigger) {
379 // Call trigger
380 $result = $this->call_trigger('RECEPTION_CREATE', $user);
381 if ($result < 0) {
382 $error++;
383 }
384 // End call triggers
385 }
386
387 if (!$error) {
388 $this->db->commit();
389 return $this->id;
390 } else {
391 foreach ($this->errors as $errmsg) {
392 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
393 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
394 }
395 $this->db->rollback();
396 return -1 * $error;
397 }
398 } else {
399 $error++;
400 $this->error = $this->db->lasterror()." - sql=$sql";
401 $this->db->rollback();
402 return -2;
403 }
404 } else {
405 $error++;
406 $this->error = $this->db->error()." - sql=$sql";
407 $this->db->rollback();
408 return -1;
409 }
410 }
411
412
413
422 public function fetch($id, $ref = '', $ref_ext = '')
423 {
424 // Check parameters
425 if (empty($id) && empty($ref) && empty($ref_ext)) {
426 return -1;
427 }
428
429 $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.billed";
430 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
431 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery, e.date_valid";
432 $sql .= ", e.fk_shipping_method, e.tracking_number";
433 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
434 $sql .= ", e.note_private, e.note_public";
435 $sql .= ', e.fk_incoterms, e.location_incoterms';
436 $sql .= ', i.libelle as label_incoterms';
437 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
438 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
439 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
440 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
441 if ($id) {
442 $sql .= " AND e.rowid = ".((int) $id);
443 }
444 if ($ref) {
445 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
446 }
447 if ($ref_ext) {
448 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
449 }
450
451 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
452 $result = $this->db->query($sql);
453 if ($result) {
454 if ($this->db->num_rows($result)) {
455 $obj = $this->db->fetch_object($result);
456
457 $this->id = $obj->rowid;
458 $this->entity = $obj->entity;
459 $this->ref = $obj->ref;
460 $this->socid = $obj->socid;
461 $this->ref_supplier = $obj->ref_supplier;
462 $this->ref_ext = $obj->ref_ext;
463 $this->statut = $obj->status;
464 $this->status = $obj->status;
465 $this->billed = $obj->billed;
466
467 $this->user_author_id = $obj->fk_user_author;
468 $this->date_creation = $this->db->jdate($obj->date_creation);
469 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
470 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
471 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planned
472 $this->date_valid = $this->db->jdate($obj->date_valid); // Date validation
473 $this->model_pdf = $obj->model_pdf;
474 $this->shipping_method_id = $obj->fk_shipping_method;
475 $this->tracking_number = $obj->tracking_number;
476 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
477 $this->origin_type = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
478 $this->origin_id = $obj->origin_id;
479
480 $this->trueWeight = $obj->weight;
481 $this->weight_units = $obj->weight_units;
482
483 $this->trueWidth = $obj->width;
484 $this->width_units = $obj->size_units;
485 $this->trueHeight = $obj->height;
486 $this->height_units = $obj->size_units;
487 $this->trueDepth = $obj->size;
488 $this->depth_units = $obj->size_units;
489
490 $this->note_public = $obj->note_public;
491 $this->note_private = $obj->note_private;
492
493 // A denormalized value
494 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
495 $this->size_units = $obj->size_units;
496
497 //Incoterms
498 $this->fk_incoterms = $obj->fk_incoterms;
499 $this->location_incoterms = $obj->location_incoterms;
500 $this->label_incoterms = $obj->label_incoterms;
501
502 $this->db->free($result);
503
504 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
505 //$this->pdf_filename = $file;
506
507 // Tracking url
508 $this->getUrlTrackingStatus($obj->tracking_number);
509
510 /*
511 * Thirdparty
512 */
513 $result = $this->fetch_thirdparty();
514
515
516 // Retrieve all extrafields for reception
517 // fetch optionals attributes and labels
518 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
519 $extrafields = new ExtraFields($this->db);
520 $extrafields->fetch_name_optionals_label($this->table_element, true);
521 $this->fetch_optionals();
522
523 /*
524 * Lines
525 */
526 $result = $this->fetch_lines();
527 if ($result < 0) {
528 return -3;
529 }
530
531 return 1;
532 } else {
533 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
534 $this->error = 'Reception with id '.$id.' not found';
535 return 0;
536 }
537 } else {
538 $this->error = $this->db->error();
539 return -1;
540 }
541 }
542
550 public function valid($user, $notrigger = 0)
551 {
552 global $conf, $langs;
553
554 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
555
556 dol_syslog(get_class($this)."::valid");
557
558 // Protection
559 if ($this->statut) {
560 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
561 return 0;
562 }
563
564 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
565 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
566 $this->error = 'Permission denied';
567 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
568 return -1;
569 }
570
571 $this->db->begin();
572
573 $error = 0;
574
575 // Define new ref
576 $soc = new Societe($this->db);
577 $soc->fetch($this->socid);
578
579
580 // Define new ref
581 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
582 $numref = $this->getNextNumRef($soc);
583 } else {
584 $numref = $this->ref;
585 }
586
587 $this->newref = dol_sanitizeFileName($numref);
588
589 $now = dol_now();
590
591 // Validate
592 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
593 $sql .= " ref='".$this->db->escape($numref)."'";
594 $sql .= ", fk_statut = 1";
595 $sql .= ", date_valid = '".$this->db->idate($now)."'";
596 $sql .= ", fk_user_valid = ".((int) $user->id);
597 $sql .= " WHERE rowid = ".((int) $this->id);
598 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
599 $resql = $this->db->query($sql);
600 if (!$resql) {
601 $this->error = $this->db->lasterror();
602 $error++;
603 }
604
605 // If stock increment is done on reception (recommended choice)
606 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
607 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
608
609 $langs->load("agenda");
610
611 // Loop on each product line to add a stock movement
612 // TODO in future, reception lines may not be linked to order line
613 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
614 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
615 $sql .= " ed.eatby, ed.sellby, ed.batch,";
616 $sql .= " ed.cost_price";
617 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
618 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
619 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
620 $sql .= " AND cd.rowid = ed.fk_elementdet";
621
622 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
623 $resql = $this->db->query($sql);
624 if ($resql) {
625 $cpt = $this->db->num_rows($resql);
626 for ($i = 0; $i < $cpt; $i++) {
627 $obj = $this->db->fetch_object($resql);
628
629 $qty = $obj->qty;
630
631 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
632 continue;
633 }
634 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
635
636 //var_dump($this->lines[$i]);
637 $mouvS = new MouvementStock($this->db);
638 $mouvS->origin = &$this;
639 $mouvS->setOrigin($this->element, $this->id);
640
641 if (empty($obj->batch)) {
642 // line without batch detail
643
644 // 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.
645 $inventorycode = '';
646 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
647
648 if (intval($result) < 0) {
649 $error++;
650 $this->setErrorsFromObject($mouvS);
651 break;
652 }
653 } else {
654 // line with batch detail
655
656 // 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.
657 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
658 $inventorycode = '';
659 $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);
660
661 if (intval($result) < 0) {
662 $error++;
663 $this->setErrorsFromObject($mouvS);
664 break;
665 }
666 }
667 }
668 } else {
669 $this->db->rollback();
670 $this->error = $this->db->error();
671 return -2;
672 }
673 }
674
675 if (!$error) {
676 // Change status of purchase order to "reception in process" or "totally received"
677 $status = $this->getStatusDispatch();
678 if ($status < 0) {
679 $error++;
680 } else {
681 $trigger_key = '';
682 if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
683 $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
684 if ($ret < 0) {
685 $error++;
686 $this->errors = array_merge($this->errors, $this->origin_object->errors);
687 }
688 } else {
689 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
690 if ($ret < 0) {
691 $error++;
692 }
693 }
694 }
695 }
696
697 if (!$error && !$notrigger) {
698 // Call trigger
699 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
700 if ($result < 0) {
701 $error++;
702 }
703 // End call triggers
704 }
705
706 if (!$error) {
707 $this->oldref = $this->ref;
708
709 // Rename directory if dir was a temporary ref
710 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
711 // Now we rename also files into index
712 $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)."'";
713 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
714 $resql = $this->db->query($sql);
715 if (!$resql) {
716 $error++;
717 $this->error = $this->db->lasterror();
718 }
719 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
720 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
721 $resql = $this->db->query($sql);
722 if (!$resql) {
723 $error++;
724 $this->error = $this->db->lasterror();
725 }
726
727 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
728 $oldref = dol_sanitizeFileName($this->ref);
729 $newref = dol_sanitizeFileName($numref);
730 $dirsource = $conf->reception->dir_output.'/'.$oldref;
731 $dirdest = $conf->reception->dir_output.'/'.$newref;
732 if (!$error && file_exists($dirsource)) {
733 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
734
735 if (@rename($dirsource, $dirdest)) {
736 dol_syslog("Rename ok");
737 // Rename docs starting with $oldref with $newref
738 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
739 foreach ($listoffiles as $fileentry) {
740 $dirsource = $fileentry['name'];
741 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
742 $dirsource = $fileentry['path'].'/'.$dirsource;
743 $dirdest = $fileentry['path'].'/'.$dirdest;
744 @rename($dirsource, $dirdest);
745 }
746 }
747 }
748 }
749 }
750
751 // Set new ref and current status
752 if (!$error) {
753 $this->ref = $numref;
754 $this->statut = self::STATUS_VALIDATED;
755 $this->status = self::STATUS_VALIDATED;
756 }
757
758 if (!$error) {
759 $this->db->commit();
760 return 1;
761 } else {
762 foreach ($this->errors as $errmsg) {
763 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
764 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
765 }
766 $this->db->rollback();
767 return -1 * $error;
768 }
769 }
770
776 public function getStatusDispatch()
777 {
778 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
779 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
780
782
783 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
784 if (empty($this->origin_object)) {
785 $this->fetch_origin();
786 if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
787 $res = $this->origin_object->fetch_lines();
788 if ($this->origin_object instanceof CommandeFournisseur) {
789 $this->commandeFournisseur = $this->origin_object; // deprecated
790 } else {
791 $this->commandeFournisseur = null; // deprecated
792 }
793 if ($res < 0) {
794 return $res;
795 }
796 }
797 }
798
799 $qty_received = array();
800 $qty_wished = array();
801
802 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
803 $filter = array('t.fk_element' => $this->origin_id);
804 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
805 $filter['t.status'] = 1; // Restrict to lines with status validated
806 }
807
808 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
809 if ($ret < 0) {
810 $this->setErrorsFromObject($supplierorderdispatch);
811 return $ret;
812 } else {
813 // build array with quantity received by product in all supplier orders (origin)
814 foreach ($supplierorderdispatch->lines as $dispatch_line) {
815 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
816 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
817 } else {
818 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
819 }
820 }
821
822 // qty wished in origin (purchase order, ...)
823 foreach ($this->origin_object->lines as $origin_line) {
824 // exclude lines not qualified for reception
825 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
826 continue;
827 }
828
829 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
830 }
831
832 // compare array
833 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
834 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
835 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
836
837 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
839 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
840 // set totally received if more products received than ordered
841 $close = 0;
842
843 if (count($diff_array) > 0) {
844 // there are some difference between the two arrays
845 // scan the array of results
846 foreach ($diff_array as $key => $value) {
847 // if the quantity delivered is greater or equal to ordered quantity @phan-suppress-next-line PhanTypeInvalidDimOffset
848 if ($qty_received[$key] >= $qty_wished[$key]) {
849 $close++;
850 }
851 }
852 }
853
854 if ($close == count($diff_array)) {
855 // all the products are received equal or more than the ordered quantity
857 }
858 }
859 }
860 }
861
862 return $status;
863 }
864
881 public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
882 {
883 global $conf, $langs, $user;
884
885 $num = count($this->lines);
886 $line = new CommandeFournisseurDispatch($this->db);
887
888 $line->fk_entrepot = $entrepot_id;
889 $line->fk_commandefourndet = $id;
890 $line->qty = $qty;
891
892 $supplierorderline = new CommandeFournisseurLigne($this->db);
893 $result = $supplierorderline->fetch($id);
894 if ($result <= 0) {
895 $this->setErrorsFromObject($supplierorderline);
896 return -1;
897 }
898
899 $fk_product = 0;
900 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
901 $fk_product = $supplierorderline->fk_product;
902
903 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
904 $langs->load("errors");
905 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
906 return -1;
907 }
908 }
909
910 // Check batch is set
911 $product = new Product($this->db);
912 $product->fetch($fk_product);
913 if (isModEnabled('productbatch')) {
914 $langs->load("errors");
915 if (!empty($product->status_batch) && empty($batch)) {
916 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
917 return -1;
918 } elseif (empty($product->status_batch) && !empty($batch)) {
919 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
920 return -1;
921 }
922
923 // check sell-by / eat-by date is mandatory
924 $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
925 if (!empty($errorMsgArr)) {
926 $errorMessage = '<b>' . $product->ref . '</b> : ';
927 $errorMessage .= '<ul>';
928 foreach ($errorMsgArr as $errorMsg) {
929 $errorMessage .= '<li>' . $errorMsg . '</li>';
930 }
931 $errorMessage .= '</ul>';
932 $this->error = $errorMessage;
933 return -1;
934 }
935 }
936 unset($product);
937
938 // extrafields
939 $line->array_options = $supplierorderline->array_options;
940 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
941 foreach ($array_options as $key => $value) {
942 $line->array_options[$key] = $value;
943 }
944 }
945
946 $line->fk_product = $fk_product;
947 $line->fk_commande = $supplierorderline->fk_commande;
948 $line->fk_user = $user->id;
949 $line->comment = $comment;
950 $line->batch = $batch;
951 $line->eatby = $eatby;
952 $line->sellby = $sellby;
953 $line->status = 1;
954 $line->cost_price = $cost_price;
955 $line->fk_reception = $this->id;
956
957 $this->lines[$num] = $line;
958
959 return $num;
960 }
961
962
970 public function update($user = null, $notrigger = 0)
971 {
972 global $conf;
973 $error = 0;
974
975 // Clean parameters
976
977 if (isset($this->ref)) {
978 $this->ref = trim($this->ref);
979 }
980 if (isset($this->entity)) {
981 $this->entity = (int) $this->entity;
982 }
983 if (isset($this->ref_supplier)) {
984 $this->ref_supplier = trim($this->ref_supplier);
985 }
986 if (isset($this->socid)) {
987 $this->socid = (int) trim((string) $this->socid);
988 }
989 if (isset($this->fk_user_author)) {
990 $this->fk_user_author = (int) $this->fk_user_author;
991 }
992 if (isset($this->fk_user_valid)) {
993 $this->fk_user_valid = (int) $this->fk_user_valid;
994 }
995 if (isset($this->shipping_method_id)) {
996 $this->shipping_method_id = (int) $this->shipping_method_id;
997 }
998 if (isset($this->tracking_number)) {
999 $this->tracking_number = trim($this->tracking_number);
1000 }
1001 if (isset($this->statut)) {
1002 $this->statut = (int) $this->statut;
1003 }
1004 if (isset($this->trueDepth)) {
1005 $this->trueDepth = (float) trim((string) $this->trueDepth);
1006 }
1007 if (isset($this->trueWidth)) {
1008 $this->trueWidth = (float) trim((string) $this->trueWidth);
1009 }
1010 if (isset($this->trueHeight)) {
1011 $this->trueHeight = (float) trim((string) $this->trueHeight);
1012 }
1013 if (isset($this->size_units)) {
1014 $this->size_units = trim((string) $this->size_units);
1015 }
1016 if (isset($this->weight_units)) {
1017 $this->weight_units = (float) trim((string) $this->weight_units);
1018 }
1019 if (isset($this->trueWeight)) {
1020 $this->weight = (float) trim((string) $this->trueWeight);
1021 }
1022 if (isset($this->note_private)) {
1023 $this->note_private = trim($this->note_private);
1024 }
1025 if (isset($this->note_public)) {
1026 $this->note_public = trim($this->note_public);
1027 }
1028 if (isset($this->model_pdf)) {
1029 $this->model_pdf = trim($this->model_pdf);
1030 }
1031
1032
1033 // Check parameters
1034 // Put here code to add control on parameters values
1035
1036 // Update request
1037 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
1038
1039 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1040 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1041 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1042 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1043 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
1044 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1045 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
1046 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
1047 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1048 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
1049 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
1050 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1051 $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
1052 $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
1053 $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
1054 $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
1055 $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
1056 $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
1057 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1058 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1059 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1060 $sql .= " entity = ".((int) $conf->entity);
1061 $sql .= " WHERE rowid=".((int) $this->id);
1062
1063 $this->db->begin();
1064
1065 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1066 $resql = $this->db->query($sql);
1067 if (!$resql) {
1068 $error++;
1069 $this->errors[] = "Error ".$this->db->lasterror();
1070 }
1071
1072 if (!$error) {
1073 if (!$notrigger) {
1074 // Call trigger
1075 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1076 if ($result < 0) {
1077 $error++;
1078 }
1079 // End call triggers
1080 }
1081 }
1082
1083 // Commit or rollback
1084 if ($error) {
1085 foreach ($this->errors as $errmsg) {
1086 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1087 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1088 }
1089 $this->db->rollback();
1090 return -1 * $error;
1091 } else {
1092 $this->db->commit();
1093 return 1;
1094 }
1095 }
1096
1103 public function delete(User $user)
1104 {
1105 global $conf, $langs, $user;
1106 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1107
1108 $error = 0;
1109 $this->error = '';
1110
1111
1112 $this->db->begin();
1113
1114 // Stock control
1115 if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT)
1116 || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED))
1117 ) {
1118 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1119
1120 $langs->load("agenda");
1121
1122 // Loop on each product line to add a stock movement
1123 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1124 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1125 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1126 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1127 $sql .= " AND cd.rowid = ed.fk_elementdet";
1128
1129 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1130 $resql = $this->db->query($sql);
1131 if ($resql) {
1132 $cpt = $this->db->num_rows($resql);
1133 for ($i = 0; $i < $cpt; $i++) {
1134 dol_syslog(get_class($this)."::delete movement index ".$i);
1135 $obj = $this->db->fetch_object($resql);
1136
1137 $mouvS = new MouvementStock($this->db);
1138 // we do not log origin because it will be deleted
1139 $mouvS->origin = null;
1140
1141 $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
1142 if ($result < 0) {
1143 $error++;
1144 $this->error = $mouvS->error;
1145 $this->errors = $mouvS->errors;
1146 }
1147 }
1148 } else {
1149 $error++;
1150 $this->errors[] = "Error ".$this->db->lasterror();
1151 }
1152 }
1153
1154 if (!$error) {
1155 $main = MAIN_DB_PREFIX.'receptiondet_batch';
1156 $ef = $main."_extrafields";
1157
1158 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1159
1160 $sql = "DELETE FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1161 $sql .= " WHERE fk_reception = ".((int) $this->id);
1162
1163 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1164 // Delete linked object
1165 $res = $this->deleteObjectLinked();
1166 if ($res < 0) {
1167 $error++;
1168 }
1169
1170 if (!$error) {
1171 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1172 $sql .= " WHERE rowid = ".((int) $this->id);
1173
1174 if ($this->db->query($sql)) {
1175 // Call trigger
1176 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1177 if ($result < 0) {
1178 $error++;
1179 }
1180 // End call triggers
1181
1182 if (!empty($this->origin) && $this->origin_id > 0) {
1183 $this->fetch_origin();
1185 '@phan-var-force CommandeFournisseur $origin_object';
1186 if ($origin_object->statut == 4) { // If order source of reception is "partially received"
1187 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1188 $origin_object->loadReceptions();
1189 //var_dump($this->$origin->receptions);exit;
1190 if (count($origin_object->receptions) <= 0) {
1191 $origin_object->setStatut(3); // ordered
1192 }
1193 }
1194 }
1195
1196 if (!$error) {
1197 $this->db->commit();
1198
1199 // We delete PDFs
1200 $ref = dol_sanitizeFileName($this->ref);
1201 if (!empty($conf->reception->dir_output)) {
1202 $dir = $conf->reception->dir_output.'/'.$ref;
1203 $file = $dir.'/'.$ref.'.pdf';
1204 if (file_exists($file)) {
1205 if (!dol_delete_file($file)) {
1206 return 0;
1207 }
1208 }
1209 if (file_exists($dir)) {
1210 if (!dol_delete_dir_recursive($dir)) {
1211 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1212 return 0;
1213 }
1214 }
1215 }
1216
1217 return 1;
1218 } else {
1219 $this->db->rollback();
1220 return -1;
1221 }
1222 } else {
1223 $this->error = $this->db->lasterror()." - sql=$sql";
1224 $this->db->rollback();
1225 return -3;
1226 }
1227 } else {
1228 $this->error = $this->db->lasterror()." - sql=$sql";
1229 $this->db->rollback();
1230 return -2;
1231 }
1232 } else {
1233 $this->error = $this->db->lasterror()." - sql=$sql";
1234 $this->db->rollback();
1235 return -1;
1236 }
1237 } else {
1238 $this->db->rollback();
1239 return -1;
1240 }
1241 }
1242
1243 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1249 public function fetch_lines()
1250 {
1251 // phpcs:enable
1252 $this->lines = array();
1253
1254 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1255
1256 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."receptiondet_batch";
1257 $sql .= " WHERE fk_reception = ".((int) $this->id);
1258
1259 $resql = $this->db->query($sql);
1260
1261 if (!empty($resql)) {
1262 while ($obj = $this->db->fetch_object($resql)) {
1263 $line = new CommandeFournisseurDispatch($this->db);
1264
1265 $line->fetch($obj->rowid);
1266
1267 // TODO Remove or keep this ?
1268 $line->fetch_product();
1269
1270 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1271 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1272 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1273 $sql_commfourndet .= ' ORDER BY rang';
1274
1275 $resql_commfourndet = $this->db->query($sql_commfourndet);
1276 if (!empty($resql_commfourndet)) {
1277 $obj = $this->db->fetch_object($resql_commfourndet);
1278 $line->qty_asked = $obj->qty;
1279 $line->description = $obj->description;
1280 $line->desc = $obj->description;
1281 $line->tva_tx = $obj->tva_tx;
1282 $line->vat_src_code = $obj->vat_src_code;
1283 $line->subprice = $obj->subprice;
1284 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1285 $line->remise_percent = $obj->remise_percent;
1286 $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1287 $line->ref_supplier = $obj->ref;
1288 $line->total_ht = $obj->total_ht;
1289 $line->total_ttc = $obj->total_ttc;
1290 $line->total_tva = $obj->total_tva;
1291 } else {
1292 $line->qty_asked = 0;
1293 $line->description = '';
1294 $line->desc = '';
1295 $line->label = $obj->label;
1296 }
1297
1298 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1299 $tva = $pu_ht * $line->tva_tx / 100;
1300 $this->total_ht += $pu_ht;
1301 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1302
1303 $this->total_ttc += $pu_ht + $tva;
1304
1305 if (isModEnabled('productbatch') && !empty($line->batch)) {
1306 $detail_batch = new stdClass();
1307 $detail_batch->eatby = $line->eatby;
1308 $detail_batch->sellby = $line->sellby;
1309 $detail_batch->batch = $line->batch;
1310 $detail_batch->qty = $line->qty;
1311
1312 $line->detail_batch[] = $detail_batch;
1313 }
1314
1315 $this->lines[] = $line;
1316 }
1317
1318 return 1;
1319 } else {
1320 return -1;
1321 }
1322 }
1323
1334 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1335 {
1336 global $langs, $hookmanager;
1337
1338 $result = '';
1339 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1340 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1341 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1342
1343 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1344
1345 if ($short) {
1346 return $url;
1347 }
1348
1349 $linkclose = '';
1350 if (empty($notooltip)) {
1351 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1352 $label = $langs->trans("Reception");
1353 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1354 }
1355 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1356 $linkclose .= ' class="classfortooltip"';
1357 }
1358
1359 $linkstart = '<a href="'.$url.'"';
1360 $linkstart .= $linkclose.'>';
1361 $linkend = '</a>';
1362
1363 $result .= $linkstart;
1364 if ($withpicto) {
1365 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1366 }
1367 if ($withpicto != 2) {
1368 $result .= $this->ref;
1369 }
1370
1371 $result .= $linkend;
1372
1373 global $action;
1374 $hookmanager->initHooks(array($this->element . 'dao'));
1375 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1376 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1377 if ($reshook > 0) {
1378 $result = $hookmanager->resPrint;
1379 } else {
1380 $result .= $hookmanager->resPrint;
1381 }
1382 return $result;
1383 }
1384
1391 public function getLibStatut($mode = 0)
1392 {
1393 return $this->LibStatut($this->statut, $mode);
1394 }
1395
1396 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1404 public function LibStatut($status, $mode)
1405 {
1406 // phpcs:enable
1407 global $langs;
1408
1409 // List of long language codes for status
1410 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1411 $this->labelStatus[0] = 'StatusReceptionDraft';
1412 // product to receive if stock increase is on close or already received if stock increase is on validation
1413 $this->labelStatus[1] = 'StatusReceptionValidated';
1414 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1415 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1416 }
1417 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1418 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1419 }
1420 $this->labelStatus[2] = 'StatusReceptionProcessed';
1421
1422 // List of short language codes for status
1423 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1424 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1425 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1426 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1427
1428 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1429 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1430
1431 $statusType = 'status'.$status;
1432 if ($status == self::STATUS_VALIDATED) {
1433 $statusType = 'status4';
1434 }
1435 if ($status == self::STATUS_CLOSED) {
1436 $statusType = 'status6';
1437 }
1438
1439 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1440 }
1441
1449 public function getKanbanView($option = '', $arraydata = null)
1450 {
1451 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1452
1453 $return = '<div class="box-flex-item box-flex-grow-zero">';
1454 $return .= '<div class="info-box info-box-sm">';
1455 $return .= '<div class="info-box-icon bg-infobox-action">';
1456 $return .= img_picto('', 'order');
1457 $return .= '</div>';
1458 $return .= '<div class="info-box-content">';
1459 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1460 if ($selected >= 0) {
1461 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1462 }
1463 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1464 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1465 }
1466 /*if (property_exists($this, 'total_ht')) {
1467 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1468 }*/
1469 if (method_exists($this, 'getLibStatut')) {
1470 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1471 }
1472 $return .= '</div>';
1473 $return .= '</div>';
1474 $return .= '</div>';
1475
1476 return $return;
1477 }
1478
1486 public function initAsSpecimen()
1487 {
1488 global $langs;
1489
1490 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1491 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1492 $now = dol_now();
1493
1494 dol_syslog(get_class($this)."::initAsSpecimen");
1495
1496 $order = new CommandeFournisseur($this->db);
1497 $order->initAsSpecimen();
1498
1499 // Initialise parameters
1500 $this->id = 0;
1501 $this->ref = 'SPECIMEN';
1502 $this->specimen = 1;
1503 $this->statut = 1;
1504 $this->status = 1;
1505 $this->date = $now;
1506 $this->date_creation = $now;
1507 $this->date_valid = $now;
1508 $this->date_delivery = $now;
1509 $this->date_reception = $now + 24 * 3600;
1510
1511 $this->entrepot_id = 0;
1512 $this->socid = 1;
1513
1514 $this->origin_id = 1;
1515 $this->origin_type = 'supplier_order';
1516 $this->origin_object = $order;
1517
1518 $this->note_private = 'Private note';
1519 $this->note_public = 'Public note';
1520
1521 $this->tracking_number = 'TRACKID-ABC123';
1522
1523 $this->fk_incoterms = 1;
1524
1525 $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)
1526 $xnbp = 0;
1527 while ($xnbp < $nbp) {
1528 $line = new CommandeFournisseurDispatch($this->db);
1529 $line->desc = $langs->trans("Description")." ".$xnbp;
1530 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1531 $line->label = $langs->trans("Description")." ".$xnbp;
1532 $line->qty = 10;
1533
1534 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1535
1536 $this->lines[] = $line;
1537 $xnbp++;
1538 }
1539
1540 return 1;
1541 }
1542
1550 public function setDeliveryDate($user, $delivery_date)
1551 {
1552 // phpcs:enable
1553 if ($user->hasRight('reception', 'creer')) {
1554 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1555 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1556 $sql .= " WHERE rowid = ".((int) $this->id);
1557
1558 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1559 $resql = $this->db->query($sql);
1560 if ($resql) {
1561 $this->date_delivery = $delivery_date;
1562 return 1;
1563 } else {
1564 $this->error = $this->db->error();
1565 return -1;
1566 }
1567 } else {
1568 return -2;
1569 }
1570 }
1571
1572 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1578 public function fetch_delivery_methods()
1579 {
1580 // phpcs:enable
1581 global $langs;
1582 $this->meths = array();
1583
1584 $sql = "SELECT em.rowid, em.code, em.libelle";
1585 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1586 $sql .= " WHERE em.active = 1";
1587 $sql .= " ORDER BY em.libelle ASC";
1588
1589 $resql = $this->db->query($sql);
1590 if ($resql) {
1591 while ($obj = $this->db->fetch_object($resql)) {
1592 $label = $langs->trans('ReceptionMethod'.$obj->code);
1593 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1594 }
1595 }
1596 }
1597
1598 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1605 public function list_delivery_methods($id = 0)
1606 {
1607 // phpcs:enable
1608 global $langs;
1609
1610 $this->listmeths = array();
1611 $i = 0;
1612
1613 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1614 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1615 if (!empty($id)) {
1616 $sql .= " WHERE em.rowid = ".((int) $id);
1617 }
1618
1619 $resql = $this->db->query($sql);
1620 if ($resql) {
1621 while ($obj = $this->db->fetch_object($resql)) {
1622 $this->listmeths[$i]['rowid'] = $obj->rowid;
1623 $this->listmeths[$i]['code'] = $obj->code;
1624 $label = $langs->trans('ReceptionMethod'.$obj->code);
1625 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1626 $this->listmeths[$i]['description'] = $obj->description;
1627 $this->listmeths[$i]['tracking'] = $obj->tracking;
1628 $this->listmeths[$i]['active'] = $obj->active;
1629 $i++;
1630 }
1631 }
1632 }
1633
1640 public function getUrlTrackingStatus($value = '')
1641 {
1642 if (!empty($this->shipping_method_id)) {
1643 $sql = "SELECT em.code, em.tracking";
1644 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1645 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1646
1647 $resql = $this->db->query($sql);
1648 if ($resql) {
1649 if ($obj = $this->db->fetch_object($resql)) {
1650 $tracking = $obj->tracking;
1651 }
1652 }
1653 }
1654
1655 if (!empty($tracking) && !empty($value)) {
1656 $url = str_replace('{TRACKID}', $value, $tracking);
1657 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1658 } else {
1659 $this->tracking_url = $value;
1660 }
1661 }
1662
1668 public function setClosed()
1669 {
1670 global $conf, $langs, $user;
1671
1672 $error = 0;
1673
1674 // Protection. This avoid to move stock later when we should not
1675 if ($this->statut == Reception::STATUS_CLOSED) {
1676 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1677 return 0;
1678 }
1679
1680 $this->db->begin();
1681
1682 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1683 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1684
1685 $resql = $this->db->query($sql);
1686 if ($resql) {
1687 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1688 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1689 $order = new CommandeFournisseur($this->db);
1690 $order->fetch($this->origin_id);
1691
1692 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1693
1694 $receptions_match_order = 1;
1695 foreach ($order->lines as $line) {
1696 $lineid = $line->id;
1697 $qty = $line->qty;
1698 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1699 $receptions_match_order = 0;
1700 $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';
1701 dol_syslog($text);
1702 break;
1703 }
1704 }
1705 if ($receptions_match_order) {
1706 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');
1707 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1708 }
1709 }
1710
1711 $this->statut = self::STATUS_CLOSED;
1712 $this->status = self::STATUS_CLOSED;
1713
1714 // If stock increment is done on closing
1715 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1716 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1717
1718 $langs->load("agenda");
1719
1720 // Loop on each product line to add a stock movement
1721 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1722 $sql = "SELECT cd.fk_product, cd.subprice,";
1723 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1724 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1725 $sql .= " ed.cost_price";
1726 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1727 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1728 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1729 $sql .= " AND cd.rowid = ed.fk_elementdet";
1730
1731 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1732 $resql = $this->db->query($sql);
1733
1734 if ($resql) {
1735 $cpt = $this->db->num_rows($resql);
1736 for ($i = 0; $i < $cpt; $i++) {
1737 $obj = $this->db->fetch_object($resql);
1738
1739 $qty = $obj->qty;
1740
1741 if ($qty <= 0) {
1742 continue;
1743 }
1744 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1745
1746 $mouvS = new MouvementStock($this->db);
1747 $mouvS->origin = &$this;
1748 $mouvS->setOrigin($this->element, $this->id);
1749
1750 if (empty($obj->batch)) {
1751 // line without batch detail
1752
1753 // 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
1754 $inventorycode = '';
1755 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1756 if ($result < 0) {
1757 $this->error = $mouvS->error;
1758 $this->errors = $mouvS->errors;
1759 $error++;
1760 break;
1761 }
1762 } else {
1763 // line with batch detail
1764
1765 // 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
1766 $inventorycode = '';
1767 $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);
1768
1769 if ($result < 0) {
1770 $this->error = $mouvS->error;
1771 $this->errors = $mouvS->errors;
1772 $error++;
1773 break;
1774 }
1775 }
1776 }
1777 } else {
1778 $this->error = $this->db->lasterror();
1779 $error++;
1780 }
1781 }
1782
1783 // Call trigger
1784 if (!$error) {
1785 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1786 if ($result < 0) {
1787 $error++;
1788 }
1789 }
1790 } else {
1791 dol_print_error($this->db);
1792 $error++;
1793 }
1794
1795 if (!$error) {
1796 $this->db->commit();
1797 return 1;
1798 } else {
1799 $this->statut = self::STATUS_VALIDATED;
1800 $this->status = self::STATUS_VALIDATED;
1801 $this->db->rollback();
1802 return -1;
1803 }
1804 }
1805
1811 public function setBilled()
1812 {
1813 global $user;
1814 $error = 0;
1815
1816 $this->db->begin();
1817
1818 if ($this->statut == Reception::STATUS_VALIDATED) {
1819 // do not close if already closed
1820 $this->setClosed();
1821 }
1822
1823 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1824 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1825
1826 $resql = $this->db->query($sql);
1827 if ($resql) {
1828 $this->billed = 1;
1829
1830 // Call trigger
1831 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1832 if ($result < 0) {
1833 $this->billed = 0;
1834 $error++;
1835 }
1836 } else {
1837 $error++;
1838 $this->errors[] = $this->db->lasterror;
1839 }
1840
1841 if (empty($error)) {
1842 $this->db->commit();
1843 return 1;
1844 } else {
1845 $this->db->rollback();
1846 return -1;
1847 }
1848 }
1849
1855 public function reOpen()
1856 {
1857 global $conf, $langs, $user;
1858
1859 $error = 0;
1860
1861 $this->db->begin();
1862
1863 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1864 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1865
1866 $resql = $this->db->query($sql);
1867 if ($resql) {
1868 $this->statut = self::STATUS_VALIDATED;
1869 $this->status = self::STATUS_VALIDATED;
1870 $this->billed = 0;
1871
1872 // If stock increment is done on closing
1873 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1874 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1875 $numref = $this->ref;
1876 $langs->load("agenda");
1877
1878 // Loop on each product line to add a stock movement
1879 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1880 $sql = "SELECT ed.fk_product, cd.subprice,";
1881 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1882 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1883 $sql .= " ed.cost_price";
1884 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1885 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
1886 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1887 $sql .= " AND cd.rowid = ed.fk_elementdet";
1888
1889 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1890 $resql = $this->db->query($sql);
1891 if ($resql) {
1892 $cpt = $this->db->num_rows($resql);
1893 for ($i = 0; $i < $cpt; $i++) {
1894 $obj = $this->db->fetch_object($resql);
1895
1896 $qty = $obj->qty;
1897
1898 if ($qty <= 0) {
1899 continue;
1900 }
1901
1902 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1903
1904 //var_dump($this->lines[$i]);
1905 $mouvS = new MouvementStock($this->db);
1906 $mouvS->origin = &$this;
1907 $mouvS->setOrigin($this->element, $this->id);
1908
1909 if (empty($obj->batch)) {
1910 // line without batch detail
1911
1912 // 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
1913 $inventorycode = '';
1914 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1915
1916 if ($result < 0) {
1917 $this->error = $mouvS->error;
1918 $this->errors = $mouvS->errors;
1919 $error++;
1920 break;
1921 }
1922 } else {
1923 // line with batch detail
1924
1925 // 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
1926 $inventorycode = '';
1927 $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);
1928 if ($result < 0) {
1929 $this->error = $mouvS->error;
1930 $this->errors = $mouvS->errors;
1931 $error++;
1932 break;
1933 }
1934 }
1935 }
1936 } else {
1937 $this->error = $this->db->lasterror();
1938 $error++;
1939 }
1940 }
1941
1942 if (!$error) {
1943 // Call trigger
1944 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1945 if ($result < 0) {
1946 $error++;
1947 }
1948 }
1949
1950 if (!$error && $this->origin == 'order_supplier') {
1951 $commande = new CommandeFournisseur($this->db);
1952 $commande->fetch($this->origin_id);
1953 $result = $commande->setStatus($user, 4);
1954 if ($result < 0) {
1955 $error++;
1956 $this->error = $commande->error;
1957 $this->errors = $commande->errors;
1958 }
1959 }
1960 } else {
1961 $error++;
1962 $this->errors[] = $this->db->lasterror();
1963 }
1964
1965 if (!$error) {
1966 $this->db->commit();
1967 return 1;
1968 } else {
1969 $this->db->rollback();
1970 return -1;
1971 }
1972 }
1973
1980 public function setDraft($user)
1981 {
1982 // phpcs:enable
1983 global $conf, $langs;
1984
1985 $error = 0;
1986
1987 // Protection
1988 if ($this->statut <= self::STATUS_DRAFT) {
1989 return 0;
1990 }
1991
1992 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1993 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1994 $this->error = 'Permission denied';
1995 return -1;
1996 }
1997
1998 $this->db->begin();
1999
2000 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
2001 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2002 $sql .= " WHERE rowid = ".((int) $this->id);
2003
2004 dol_syslog(__METHOD__, LOG_DEBUG);
2005 if ($this->db->query($sql)) {
2006 // If stock increment is done on closing
2007 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
2008 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2009
2010 $langs->load("agenda");
2011
2012 // Loop on each product line to add a stock movement
2013 // TODO possibilite de receptionner a partir d'une propale ou autre origine
2014 $sql = "SELECT cd.fk_product, cd.subprice,";
2015 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
2016 $sql .= " ed.eatby, ed.sellby, ed.batch,";
2017 $sql .= " ed.cost_price";
2018 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
2019 $sql .= " ".MAIN_DB_PREFIX."receptiondet_batch as ed";
2020 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
2021 $sql .= " AND cd.rowid = ed.fk_elementdet";
2022
2023 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2024 $resql = $this->db->query($sql);
2025 if ($resql) {
2026 $cpt = $this->db->num_rows($resql);
2027 for ($i = 0; $i < $cpt; $i++) {
2028 $obj = $this->db->fetch_object($resql);
2029
2030 $qty = $obj->qty;
2031
2032
2033 if ($qty <= 0) {
2034 continue;
2035 }
2036 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2037
2038 //var_dump($this->lines[$i]);
2039 $mouvS = new MouvementStock($this->db);
2040 $mouvS->origin = &$this;
2041 $mouvS->setOrigin($this->element, $this->id);
2042
2043 if (empty($obj->batch)) {
2044 // line without batch detail
2045
2046 // 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
2047 $inventorycode = '';
2048 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
2049 if ($result < 0) {
2050 $this->error = $mouvS->error;
2051 $this->errors = $mouvS->errors;
2052 $error++;
2053 break;
2054 }
2055 } else {
2056 // line with batch detail
2057
2058 // 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
2059 $inventorycode = '';
2060 $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);
2061 if ($result < 0) {
2062 $this->error = $mouvS->error;
2063 $this->errors = $mouvS->errors;
2064 $error++;
2065 break;
2066 }
2067 }
2068 }
2069 } else {
2070 $this->error = $this->db->lasterror();
2071 $error++;
2072 }
2073 }
2074
2075 if (!$error) {
2076 // Call trigger
2077 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2078 if ($result < 0) {
2079 $error++;
2080 }
2081 }
2082 if ($this->origin == 'order_supplier') {
2083 if (!empty($this->origin) && $this->origin_id > 0) {
2084 $this->fetch_origin();
2085 if ($this->origin_object->statut == 4) { // If order source of reception is "partially received"
2086 // Check if there is no more reception validated.
2087 $this->origin_object->fetchObjectLinked();
2088 $setStatut = 1;
2089 if (!empty($this->origin_object->linkedObjects['reception'])) {
2090 foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2091 if ($rcption->statut > 0) {
2092 $setStatut = 0;
2093 break;
2094 }
2095 }
2096 //var_dump($this->$origin->receptions);exit;
2097 if ($setStatut) {
2098 $this->origin_object->setStatut(3); // ordered
2099 }
2100 }
2101 }
2102 }
2103 }
2104
2105 if (!$error) {
2106 $this->statut = self::STATUS_DRAFT;
2107 $this->status = self::STATUS_DRAFT;
2108 $this->db->commit();
2109 return 1;
2110 } else {
2111 $this->db->rollback();
2112 return -1;
2113 }
2114 } else {
2115 $this->error = $this->db->error();
2116 $this->db->rollback();
2117 return -1;
2118 }
2119 }
2120
2131 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2132 {
2133 global $conf, $langs;
2134
2135 $langs->load("receptions");
2136
2137 if (!dol_strlen($modele)) {
2138 $modele = 'squille';
2139
2140 if ($this->model_pdf) {
2141 $modele = $this->model_pdf;
2142 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2143 $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2144 }
2145 }
2146
2147 $modelpath = "core/modules/reception/doc/";
2148
2149 $this->fetch_origin();
2150
2151 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2152 }
2153
2162 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2163 {
2164 $tables = array('reception');
2165
2166 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2167 }
2168
2177 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2178 {
2179 $tables = array(
2180 'receptiondet_batch'
2181 );
2182
2183 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2184 }
2185}
$object ref
Definition info.php:89
Class to manage table ReceptionLineBatch.
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_RECEIVED_COMPLETELY
Received completely.
Class to manage line orders.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
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
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='fk_statut')
Set status of an object.
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.
fetch_origin()
Read linked origin object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
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.
getNomUrl($withpicto=0, $option=0, $max=0, $short=0, $notooltip=0)
Return clickable link of object (with eventually picto)
update($user=null, $notrigger=0)
Update database.
setClosed()
Classify the reception as closed (this records also the stock movement)
getNextNumRef($soc)
Return next contract ref.
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.
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.
fetch_lines()
Load lines.
create($user, $notrigger=0)
Create reception en base.
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 third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
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:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:153