dolibarr 19.0.4
reception.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
4 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2014-2020 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Quentin Vial-Gouteyron <quentin.vial-gouteyron@atm-consulting.fr>
14 * Copyright (C) 2022-2023 Frédéric France <frederic.france@netlogic.fr>
15 * Copyright (C) 2026 Mathieu Moulin <mathieu@iprospective.fr>
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('commande')) {
44 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
45}
46
47
52{
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 = "commande_fournisseur_dispatch";
71 public $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
72
76 public $picto = 'dollyrevert';
77
78 public $socid;
79 public $ref_supplier;
80
81 public $entrepot_id;
82 public $tracking_number;
83 public $tracking_url;
84 public $billed;
85 public $model_pdf;
86
87 public $weight;
88 public $trueWeight;
89 public $weight_units;
90 public $trueWidth;
91 public $width_units;
92 public $trueHeight;
93 public $height_units;
94 public $trueDepth;
95 public $depth_units;
96 // A denormalized value
97 public $trueSize;
98 public $size_units;
99 public $user_author_id;
100
101 public $date_delivery; // Date delivery planed
102
108 public $date;
109
113 public $date_reception;
114
118 public $date_creation;
119
123 public $date_valid;
124
125 public $meths;
126 public $listmeths; // List of carriers
127
131 public $commandeFournisseur;
132
136 public $lines = array();
137
138
139 // detail of lot and qty = array(id in llx_commande_fournisseur_dispatch, batch, qty)
140 // We can use this to know warehouse planned to be used for each lot.
141 public $detail_batch;
142
143 const STATUS_DRAFT = 0;
144 const STATUS_VALIDATED = 1;
145 const STATUS_CLOSED = 2;
146
147
148
154 public function __construct($db)
155 {
156 $this->db = $db;
157 }
158
165 public function getNextNumRef($soc)
166 {
167 global $langs, $conf;
168 $langs->load("receptions");
169
170 if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
171 $mybool = false;
172
173 $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
174 $classname = $conf->global->RECEPTION_ADDON_NUMBER;
175
176 // Include file with class
177 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
178
179 foreach ($dirmodels as $reldir) {
180 $dir = dol_buildpath($reldir."core/modules/reception/");
181
182 // Load file with numbering class (if found)
183 $mybool |= @include_once $dir.$file;
184 }
185
186 if (!$mybool) {
187 dol_print_error('', "Failed to include file ".$file);
188 return '';
189 }
190
191 $obj = new $classname();
192
193 $numref = "";
194 $numref = $obj->getNextValue($soc, $this);
195
196 if ($numref != "") {
197 return $numref;
198 } else {
199 dol_print_error($this->db, get_class($this)."::getNextNumRef ".$obj->error);
200 return "";
201 }
202 } else {
203 print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
204 return "";
205 }
206 }
207
215 public function create($user, $notrigger = 0)
216 {
217 global $conf;
218
219 $now = dol_now();
220
221 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
222 $error = 0;
223
224 // Clean parameters
225 $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
226 if (empty($this->fk_project)) {
227 $this->fk_project = 0;
228 }
229 if (empty($this->weight_units)) {
230 $this->weight_units = 0;
231 }
232 if (empty($this->size_units)) {
233 $this->size_units = 0;
234 }
235
236 $this->user = $user;
237
238 $this->db->begin();
239
240 $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
241 $sql .= "ref";
242 $sql .= ", entity";
243 $sql .= ", ref_supplier";
244 $sql .= ", date_creation";
245 $sql .= ", fk_user_author";
246 $sql .= ", date_reception";
247 $sql .= ", date_delivery";
248 $sql .= ", fk_soc";
249 $sql .= ", fk_projet";
250 $sql .= ", fk_shipping_method";
251 $sql .= ", tracking_number";
252 $sql .= ", weight";
253 $sql .= ", size";
254 $sql .= ", width";
255 $sql .= ", height";
256 $sql .= ", weight_units";
257 $sql .= ", size_units";
258 $sql .= ", note_private";
259 $sql .= ", note_public";
260 $sql .= ", model_pdf";
261 $sql .= ", fk_incoterms, location_incoterms";
262 $sql .= ") VALUES (";
263 $sql .= "'(PROV)'";
264 $sql .= ", ".((int) $conf->entity);
265 $sql .= ", ".($this->ref_supplier ? "'".$this->db->escape($this->ref_supplier)."'" : "null");
266 $sql .= ", '".$this->db->idate($now)."'";
267 $sql .= ", ".((int) $user->id);
268 $sql .= ", ".($this->date_reception > 0 ? "'".$this->db->idate($this->date_reception)."'" : "null");
269 $sql .= ", ".($this->date_delivery > 0 ? "'".$this->db->idate($this->date_delivery)."'" : "null");
270 $sql .= ", ".((int) $this->socid);
271 $sql .= ", ".((int) $this->fk_project);
272 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
273 $sql .= ", '".$this->db->escape($this->tracking_number)."'";
274 $sql .= ", ".(is_null($this->weight) ? "NULL" : ((float) $this->weight));
275 $sql .= ", ".(is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
276 $sql .= ", ".(is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
277 $sql .= ", ".(is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
278 $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((float) $this->weight_units));
279 $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((float) $this->size_units));
280 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
281 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
282 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
283 $sql .= ", ".(int) $this->fk_incoterms;
284 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
285 $sql .= ")";
286
287 dol_syslog(get_class($this)."::create", LOG_DEBUG);
288
289 $resql = $this->db->query($sql);
290
291 if ($resql) {
292 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
293
294 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
295 $sql .= " SET ref = '(PROV".$this->id.")'";
296 $sql .= " WHERE rowid = ".((int) $this->id);
297
298 dol_syslog(get_class($this)."::create", LOG_DEBUG);
299 if ($this->db->query($sql)) {
300 // Insert of lines
301 $num = count($this->lines);
302 for ($i = 0; $i < $num; $i++) {
303 $this->lines[$i]->fk_reception = $this->id;
304
305 if (!$this->lines[$i]->create($user) > 0) {
306 $error++;
307 }
308 }
309
310 if (!$error && $this->id && $this->origin_id) {
311 $ret = $this->add_object_linked();
312 if (!$ret) {
313 $error++;
314 }
315 }
316
317 // Create extrafields
318 if (!$error) {
319 $result = $this->insertExtraFields();
320 if ($result < 0) {
321 $error++;
322 }
323 }
324
325 if (!$error && !$notrigger) {
326 // Call trigger
327 $result = $this->call_trigger('RECEPTION_CREATE', $user);
328 if ($result < 0) {
329 $error++;
330 }
331 // End call triggers
332 }
333
334 if (!$error) {
335 $this->db->commit();
336 return $this->id;
337 } else {
338 foreach ($this->errors as $errmsg) {
339 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
340 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
341 }
342 $this->db->rollback();
343 return -1 * $error;
344 }
345 } else {
346 $error++;
347 $this->error = $this->db->lasterror()." - sql=$sql";
348 $this->db->rollback();
349 return -2;
350 }
351 } else {
352 $error++;
353 $this->error = $this->db->error()." - sql=$sql";
354 $this->db->rollback();
355 return -1;
356 }
357 }
358
359
360
369 public function fetch($id, $ref = '', $ref_ext = '')
370 {
371 // Check parameters
372 if (empty($id) && empty($ref) && empty($ref_ext)) {
373 return -1;
374 }
375
376 $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";
377 $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
378 $sql .= ", e.date_reception as date_reception, e.model_pdf, e.date_delivery";
379 $sql .= ", e.fk_shipping_method, e.tracking_number";
380 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
381 $sql .= ", e.note_private, e.note_public";
382 $sql .= ', e.fk_incoterms, e.location_incoterms';
383 $sql .= ', i.libelle as label_incoterms';
384 $sql .= " FROM ".MAIN_DB_PREFIX."reception as e";
385 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
386 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
387 $sql .= " WHERE e.entity IN (".getEntity('reception').")";
388 if ($id) {
389 $sql .= " AND e.rowid = ".((int) $id);
390 }
391 if ($ref) {
392 $sql .= " AND e.ref = '".$this->db->escape($ref)."'";
393 }
394 if ($ref_ext) {
395 $sql .= " AND e.ref_ext = '".$this->db->escape($ref_ext)."'";
396 }
397
398 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
399 $result = $this->db->query($sql);
400 if ($result) {
401 if ($this->db->num_rows($result)) {
402 $obj = $this->db->fetch_object($result);
403
404 $this->id = $obj->rowid;
405 $this->entity = $obj->entity;
406 $this->ref = $obj->ref;
407 $this->socid = $obj->socid;
408 $this->ref_supplier = $obj->ref_supplier;
409 $this->ref_ext = $obj->ref_ext;
410 $this->statut = $obj->status;
411 $this->status = $obj->status;
412 $this->billed = $obj->billed;
413
414 $this->user_author_id = $obj->fk_user_author;
415 $this->date_creation = $this->db->jdate($obj->date_creation);
416 $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
417 $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
418 $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planed
419 $this->model_pdf = $obj->model_pdf;
420 $this->shipping_method_id = $obj->fk_shipping_method;
421 $this->tracking_number = $obj->tracking_number;
422 $this->origin = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
423 $this->origin_id = $obj->origin_id;
424
425 $this->trueWeight = $obj->weight;
426 $this->weight_units = $obj->weight_units;
427
428 $this->trueWidth = $obj->width;
429 $this->width_units = $obj->size_units;
430 $this->trueHeight = $obj->height;
431 $this->height_units = $obj->size_units;
432 $this->trueDepth = $obj->size;
433 $this->depth_units = $obj->size_units;
434
435 $this->note_public = $obj->note_public;
436 $this->note_private = $obj->note_private;
437
438 // A denormalized value
439 $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
440 $this->size_units = $obj->size_units;
441
442 //Incoterms
443 $this->fk_incoterms = $obj->fk_incoterms;
444 $this->location_incoterms = $obj->location_incoterms;
445 $this->label_incoterms = $obj->label_incoterms;
446
447 $this->db->free($result);
448
449 //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
450 //$this->pdf_filename = $file;
451
452 // Tracking url
453 $this->getUrlTrackingStatus($obj->tracking_number);
454
455 /*
456 * Thirdparty
457 */
458 $result = $this->fetch_thirdparty();
459
460
461 // Retrieve all extrafields for reception
462 // fetch optionals attributes and labels
463 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
464 $extrafields = new ExtraFields($this->db);
465 $extrafields->fetch_name_optionals_label($this->table_element, true);
466 $this->fetch_optionals();
467
468 /*
469 * Lines
470 */
471 $result = $this->fetch_lines();
472 if ($result < 0) {
473 return -3;
474 }
475
476 return 1;
477 } else {
478 dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
479 $this->error = 'Reception with id '.$id.' not found';
480 return 0;
481 }
482 } else {
483 $this->error = $this->db->error();
484 return -1;
485 }
486 }
487
495 public function valid($user, $notrigger = 0)
496 {
497 global $conf, $langs;
498
499 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
500
501 dol_syslog(get_class($this)."::valid");
502
503 // Protection
504 if ($this->statut) {
505 dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
506 return 0;
507 }
508
509 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
510 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
511 $this->error = 'Permission denied';
512 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
513 return -1;
514 }
515
516 $this->db->begin();
517
518 $error = 0;
519
520 // Define new ref
521 $soc = new Societe($this->db);
522 $soc->fetch($this->socid);
523
524
525 // Define new ref
526 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
527 $numref = $this->getNextNumRef($soc);
528 } else {
529 $numref = $this->ref;
530 }
531
532 $this->newref = dol_sanitizeFileName($numref);
533
534 $now = dol_now();
535
536 // Validate
537 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
538 $sql .= " ref='".$this->db->escape($numref)."'";
539 $sql .= ", fk_statut = 1";
540 $sql .= ", date_valid = '".$this->db->idate($now)."'";
541 $sql .= ", fk_user_valid = ".$user->id;
542 $sql .= " WHERE rowid = ".((int) $this->id);
543 dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
544 $resql = $this->db->query($sql);
545 if (!$resql) {
546 $this->error = $this->db->lasterror();
547 $error++;
548 }
549
550 // If stock increment is done on reception (recommanded choice)
551 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
552 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
553
554 $langs->load("agenda");
555
556 // Loop on each product line to add a stock movement
557 // TODO in future, reception lines may not be linked to order line
558 $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
559 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
560 $sql .= " ed.eatby, ed.sellby, ed.batch,";
561 $sql .= " ed.cost_price";
562 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
563 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
564 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
565 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
566
567 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
568 $resql = $this->db->query($sql);
569 if ($resql) {
570 $cpt = $this->db->num_rows($resql);
571 for ($i = 0; $i < $cpt; $i++) {
572 $obj = $this->db->fetch_object($resql);
573
574 $qty = $obj->qty;
575
576 if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
577 continue;
578 }
579 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
580
581 //var_dump($this->lines[$i]);
582 $mouvS = new MouvementStock($this->db);
583 $mouvS->origin = &$this;
584 $mouvS->setOrigin($this->element, $this->id);
585
586 if (empty($obj->batch)) {
587 // line without batch detail
588
589 // 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.
590 $inventorycode = '';
591 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
592
593 if (intval($result) < 0) {
594 $error++;
595 $this->errors[] = $mouvS->error;
596 $this->errors = array_merge($this->errors, $mouvS->errors);
597 break;
598 }
599 } else {
600 // line with batch detail
601
602 // 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.
603 // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
604 $inventorycode = '';
605 $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);
606
607 if (intval($result) < 0) {
608 $error++;
609 $this->errors[] = $mouvS->error;
610 $this->errors = array_merge($this->errors, $mouvS->errors);
611 break;
612 }
613 }
614 }
615 } else {
616 $this->db->rollback();
617 $this->error = $this->db->error();
618 return -2;
619 }
620 }
621
622 // Change status of order to "reception in process" or "totally received"
623 $status = $this->getStatusDispatch();
624 if ($status < 0) {
625 $error++;
626 } else {
627 $trigger_key = '';
629 $ret = $this->commandeFournisseur->Livraison($user, dol_now(), 'tot', '');
630 if ($ret < 0) {
631 $error++;
632 $this->errors = array_merge($this->errors, $this->commandeFournisseur->errors);
633 }
634 } else {
635 $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
636 if ($ret < 0) {
637 $error++;
638 }
639 }
640 }
641
642 if (!$error && !$notrigger) {
643 // Call trigger
644 $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
645 if ($result < 0) {
646 $error++;
647 }
648 // End call triggers
649 }
650
651 if (!$error) {
652 $this->oldref = $this->ref;
653
654 // Rename directory if dir was a temporary ref
655 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
656 // Now we rename also files into index
657 $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)."'";
658 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'reception/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity);
659 $resql = $this->db->query($sql);
660 if (!$resql) {
661 $error++;
662 $this->error = $this->db->lasterror();
663 }
664 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'reception/".$this->db->escape($this->newref)."'";
665 $sql .= " WHERE filepath = 'reception/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
666 $resql = $this->db->query($sql);
667 if (!$resql) {
668 $error++;
669 $this->error = $this->db->lasterror();
670 }
671
672 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
673 $oldref = dol_sanitizeFileName($this->ref);
674 $newref = dol_sanitizeFileName($numref);
675 $dirsource = $conf->reception->dir_output.'/'.$oldref;
676 $dirdest = $conf->reception->dir_output.'/'.$newref;
677 if (!$error && file_exists($dirsource)) {
678 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
679
680 if (@rename($dirsource, $dirdest)) {
681 dol_syslog("Rename ok");
682 // Rename docs starting with $oldref with $newref
683 $listoffiles = dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
684 foreach ($listoffiles as $fileentry) {
685 $dirsource = $fileentry['name'];
686 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
687 $dirsource = $fileentry['path'].'/'.$dirsource;
688 $dirdest = $fileentry['path'].'/'.$dirdest;
689 @rename($dirsource, $dirdest);
690 }
691 }
692 }
693 }
694 }
695
696 // Set new ref and current status
697 if (!$error) {
698 $this->ref = $numref;
699 $this->statut = self::STATUS_VALIDATED;
700 $this->status = self::STATUS_VALIDATED;
701 }
702
703 if (!$error) {
704 $this->db->commit();
705 return 1;
706 } else {
707 foreach ($this->errors as $errmsg) {
708 dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
709 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
710 }
711 $this->db->rollback();
712 return -1 * $error;
713 }
714 }
715
721 public function getStatusDispatch()
722 {
723 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
724 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
725
727
728 if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
729 if (empty($this->origin_object)) {
730 $this->fetch_origin();
731 if (empty($this->origin_object->lines)) {
732 $res = $this->origin_object->fetch_lines();
733 $this->commandeFournisseur = $this->origin_object; // deprecated
734 if ($res < 0) {
735 return $res;
736 }
737 }
738 }
739
740 $qty_received = array();
741 $qty_wished = array();
742
743 $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
744 $filter = array('t.fk_commande'=>$this->origin_id);
745 if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
746 $filter['t.status'] = 1; // Restrict to lines with status validated
747 }
748
749 $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
750 if ($ret < 0) {
751 $this->error = $supplierorderdispatch->error;
752 $this->errors = $supplierorderdispatch->errors;
753 return $ret;
754 } else {
755 // build array with quantity received by product in all supplier orders (origin)
756 foreach ($supplierorderdispatch->lines as $dispatch_line) {
757 if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
758 $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
759 } else {
760 $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
761 }
762 }
763
764 // qty wished in order supplier (origin)
765 foreach ($this->commandeFournisseur->lines as $origin_line) {
766 // exclude lines not qualified for reception
767 if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
768 continue;
769 }
770
771 $qty_wished[$origin_line->fk_product] += $origin_line->qty;
772 }
773
774 // compare array
775 $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
776 $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
777 $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
778
779 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
781 } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
782 // set totally received if more products received than ordered
783 $close = 0;
784
785 if (count($diff_array) > 0) {
786 // there are some difference between the two arrays
787 // scan the array of results
788 foreach ($diff_array as $key => $value) {
789 // if the quantity delivered is greater or equal to ordered quantity
790 if ($qty_received[$key] >= $qty_wished[$key]) {
791 $close++;
792 }
793 }
794 }
795
796 if ($close == count($diff_array)) {
797 // all the products are received equal or more than the ordered quantity
799 }
800 }
801 }
802 }
803
804 return $status;
805 }
806
823 public function addline($entrepot_id, $id, $qty, $array_options = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $cost_price = 0)
824 {
825 global $conf, $langs, $user;
826
827 $num = count($this->lines);
828 $line = new CommandeFournisseurDispatch($this->db);
829
830 $line->fk_entrepot = $entrepot_id;
831 $line->fk_commandefourndet = $id;
832 $line->qty = $qty;
833
834 $supplierorderline = new CommandeFournisseurLigne($this->db);
835 $result = $supplierorderline->fetch($id);
836 if ($result <= 0) {
837 $this->error = $supplierorderline->error;
838 $this->errors = $supplierorderline->errors;
839 return -1;
840 }
841
842 $fk_product = 0;
843 if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
844 $fk_product = $supplierorderline->fk_product;
845
846 if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
847 $langs->load("errors");
848 $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
849 return -1;
850 }
851 }
852
853 // Check batch is set
854 $product = new Product($this->db);
855 $product->fetch($fk_product);
856 if (isModEnabled('productbatch')) {
857 $langs->load("errors");
858 if (!empty($product->status_batch) && empty($batch)) {
859 $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
860 return -1;
861 } elseif (empty($product->status_batch) && !empty($batch)) {
862 $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
863 return -1;
864 }
865 }
866 unset($product);
867
868 // extrafields
869 $line->array_options = $supplierorderline->array_options;
870 if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
871 foreach ($array_options as $key => $value) {
872 $line->array_options[$key] = $value;
873 }
874 }
875
876 $line->fk_product = $fk_product;
877 $line->fk_commande = $supplierorderline->fk_commande;
878 $line->fk_user = $user->id;
879 $line->comment = $comment;
880 $line->batch = $batch;
881 $line->eatby = $eatby;
882 $line->sellby = $sellby;
883 $line->status = 1;
884 $line->cost_price = $cost_price;
885 $line->fk_reception = $this->id;
886
887 $this->lines[$num] = $line;
888
889 return $num;
890 }
891
892
900 public function update($user = null, $notrigger = 0)
901 {
902 global $conf;
903 $error = 0;
904
905 // Clean parameters
906
907 if (isset($this->ref)) {
908 $this->ref = trim($this->ref);
909 }
910 if (isset($this->entity)) {
911 $this->entity = trim($this->entity);
912 }
913 if (isset($this->ref_supplier)) {
914 $this->ref_supplier = trim($this->ref_supplier);
915 }
916 if (isset($this->socid)) {
917 $this->socid = trim($this->socid);
918 }
919 if (isset($this->fk_user_author)) {
920 $this->fk_user_author = trim($this->fk_user_author);
921 }
922 if (isset($this->fk_user_valid)) {
923 $this->fk_user_valid = trim($this->fk_user_valid);
924 }
925 if (isset($this->shipping_method_id)) {
926 $this->shipping_method_id = trim($this->shipping_method_id);
927 }
928 if (isset($this->tracking_number)) {
929 $this->tracking_number = trim($this->tracking_number);
930 }
931 if (isset($this->statut)) {
932 $this->statut = (int) $this->statut;
933 }
934 if (isset($this->trueDepth)) {
935 $this->trueDepth = trim($this->trueDepth);
936 }
937 if (isset($this->trueWidth)) {
938 $this->trueWidth = trim($this->trueWidth);
939 }
940 if (isset($this->trueHeight)) {
941 $this->trueHeight = trim($this->trueHeight);
942 }
943 if (isset($this->size_units)) {
944 $this->size_units = trim($this->size_units);
945 }
946 if (isset($this->weight_units)) {
947 $this->weight_units = trim($this->weight_units);
948 }
949 if (isset($this->trueWeight)) {
950 $this->weight = trim($this->trueWeight);
951 }
952 if (isset($this->note_private)) {
953 $this->note_private = trim($this->note_private);
954 }
955 if (isset($this->note_public)) {
956 $this->note_public = trim($this->note_public);
957 }
958 if (isset($this->model_pdf)) {
959 $this->model_pdf = trim($this->model_pdf);
960 }
961
962
963 // Check parameters
964 // Put here code to add control on parameters values
965
966 // Update request
967 $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
968
969 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
970 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
971 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
972 $sql .= " date_creation=".(dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
973 $sql .= " fk_user_author=".(isset($this->fk_user_author) ? $this->fk_user_author : "null").",";
974 $sql .= " date_valid=".(dol_strlen($this->date_valid) != 0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
975 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
976 $sql .= " date_reception=".(dol_strlen($this->date_reception) != 0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
977 $sql .= " date_delivery=".(dol_strlen($this->date_delivery) != 0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
978 $sql .= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null").",";
979 $sql .= " tracking_number=".(isset($this->tracking_number) ? "'".$this->db->escape($this->tracking_number)."'" : "null").",";
980 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
981 $sql .= " height=".(($this->trueHeight != '') ? $this->trueHeight : "null").",";
982 $sql .= " width=".(($this->trueWidth != '') ? $this->trueWidth : "null").",";
983 $sql .= " size_units=".(isset($this->size_units) ? $this->size_units : "null").",";
984 $sql .= " size=".(($this->trueDepth != '') ? $this->trueDepth : "null").",";
985 $sql .= " weight_units=".(isset($this->weight_units) ? $this->weight_units : "null").",";
986 $sql .= " weight=".(($this->trueWeight != '') ? $this->trueWeight : "null").",";
987 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
988 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
989 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
990 $sql .= " entity = ".((int) $conf->entity);
991 $sql .= " WHERE rowid=".((int) $this->id);
992
993 $this->db->begin();
994
995 dol_syslog(get_class($this)."::update", LOG_DEBUG);
996 $resql = $this->db->query($sql);
997 if (!$resql) {
998 $error++;
999 $this->errors[] = "Error ".$this->db->lasterror();
1000 }
1001
1002 if (!$error) {
1003 if (!$notrigger) {
1004 // Call trigger
1005 $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1006 if ($result < 0) {
1007 $error++;
1008 }
1009 // End call triggers
1010 }
1011 }
1012
1013 // Commit or rollback
1014 if ($error) {
1015 foreach ($this->errors as $errmsg) {
1016 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1017 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1018 }
1019 $this->db->rollback();
1020 return -1 * $error;
1021 } else {
1022 $this->db->commit();
1023 return 1;
1024 }
1025 }
1026
1033 public function delete(User $user)
1034 {
1035 global $conf, $langs, $user;
1036 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1037
1038 $error = 0;
1039 $this->error = '';
1040
1041
1042 $this->db->begin();
1043
1044 // Stock control
1045 if (isModEnabled('stock') &&
1046 ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > self::STATUS_DRAFT) ||
1047 (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->statut == self::STATUS_CLOSED))
1048 ) {
1049 require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1050
1051 $langs->load("agenda");
1052
1053 // Loop on each product line to add a stock movement
1054 $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as commande_fournisseur_dispatch_id";
1055 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1056 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1057 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1058 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1059
1060 dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1061 $resql = $this->db->query($sql);
1062 if ($resql) {
1063 $cpt = $this->db->num_rows($resql);
1064 for ($i = 0; $i < $cpt; $i++) {
1065 dol_syslog(get_class($this)."::delete movement index ".$i);
1066 $obj = $this->db->fetch_object($resql);
1067
1068 $mouvS = new MouvementStock($this->db);
1069 // we do not log origin because it will be deleted
1070 $mouvS->origin = null;
1071
1072 $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
1073 if ($result < 0) {
1074 $error++;
1075 $this->error = $mouvS->error;
1076 $this->errors = $mouvS->errors;
1077 }
1078 }
1079 } else {
1080 $error++;
1081 $this->errors[] = "Error ".$this->db->lasterror();
1082 }
1083 }
1084
1085 if (!$error) {
1086 $main = MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
1087 $ef = $main."_extrafields";
1088
1089 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1090
1091 $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
1092 $sql .= " WHERE fk_reception = ".((int) $this->id);
1093
1094 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1095 // Delete linked object
1096 $res = $this->deleteObjectLinked();
1097 if ($res < 0) {
1098 $error++;
1099 }
1100
1101 if (!$error) {
1102 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1103 $sql .= " WHERE rowid = ".((int) $this->id);
1104
1105 if ($this->db->query($sql)) {
1106 // Call trigger
1107 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1108 if ($result < 0) {
1109 $error++;
1110 }
1111 // End call triggers
1112
1113 if (!empty($this->origin) && $this->origin_id > 0) {
1114 $this->fetch_origin();
1115 $origin = $this->origin;
1116 if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1117 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1118 $this->$origin->loadReceptions();
1119 //var_dump($this->$origin->receptions);exit;
1120 if (count($this->$origin->receptions) <= 0) {
1121 $this->$origin->setStatut(3); // ordered
1122 }
1123 }
1124 }
1125
1126 if (!$error) {
1127 $this->db->commit();
1128
1129 // We delete PDFs
1130 $ref = dol_sanitizeFileName($this->ref);
1131 if (!empty($conf->reception->dir_output)) {
1132 $dir = $conf->reception->dir_output.'/'.$ref;
1133 $file = $dir.'/'.$ref.'.pdf';
1134 if (file_exists($file)) {
1135 if (!dol_delete_file($file)) {
1136 return 0;
1137 }
1138 }
1139 if (file_exists($dir)) {
1140 if (!dol_delete_dir_recursive($dir)) {
1141 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1142 return 0;
1143 }
1144 }
1145 }
1146
1147 return 1;
1148 } else {
1149 $this->db->rollback();
1150 return -1;
1151 }
1152 } else {
1153 $this->error = $this->db->lasterror()." - sql=$sql";
1154 $this->db->rollback();
1155 return -3;
1156 }
1157 } else {
1158 $this->error = $this->db->lasterror()." - sql=$sql";
1159 $this->db->rollback();
1160 return -2;
1161 }
1162 } else {
1163 $this->error = $this->db->lasterror()." - sql=$sql";
1164 $this->db->rollback();
1165 return -1;
1166 }
1167 } else {
1168 $this->db->rollback();
1169 return -1;
1170 }
1171 }
1172
1173 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1179 public function fetch_lines()
1180 {
1181 // phpcs:enable
1182 $this->lines = array();
1183
1184 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1185
1186 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch WHERE fk_reception = ".((int) $this->id);
1187 $resql = $this->db->query($sql);
1188
1189 if (!empty($resql)) {
1190 while ($obj = $this->db->fetch_object($resql)) {
1191 $line = new CommandeFournisseurDispatch($this->db);
1192
1193 $line->fetch($obj->rowid);
1194
1195 // TODO Remove or keep this ?
1196 $line->fetch_product();
1197
1198 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1199 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1200 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1201 $sql_commfourndet .= ' ORDER BY rang';
1202
1203 $resql_commfourndet = $this->db->query($sql_commfourndet);
1204 if (!empty($resql_commfourndet)) {
1205 $obj = $this->db->fetch_object($resql_commfourndet);
1206 $line->qty_asked = $obj->qty;
1207 $line->description = $obj->description;
1208 $line->desc = $obj->description;
1209 $line->tva_tx = $obj->tva_tx;
1210 $line->vat_src_code = $obj->vat_src_code;
1211 $line->subprice = $obj->subprice;
1212 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1213 $line->remise_percent = $obj->remise_percent;
1214 $line->label = !empty($obj->label) ? $obj->label : $line->product->label;
1215 $line->ref_supplier = $obj->ref;
1216 $line->total_ht = $obj->total_ht;
1217 $line->total_ttc = $obj->total_ttc;
1218 $line->total_tva = $obj->total_tva;
1219 } else {
1220 $line->qty_asked = 0;
1221 $line->description = '';
1222 $line->desc = '';
1223 $line->label = $obj->label;
1224 }
1225
1226 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1227 $tva = $pu_ht * $line->tva_tx / 100;
1228 $this->total_ht += $pu_ht;
1229 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1230
1231 $this->total_ttc += $pu_ht + $tva;
1232
1233 if (isModEnabled('productbatch') && !empty($line->batch)) {
1234 $detail_batch = new stdClass();
1235 $detail_batch->eatby = $line->eatby;
1236 $detail_batch->sellby = $line->sellby;
1237 $detail_batch->batch = $line->batch;
1238 $detail_batch->qty = $line->qty;
1239 $line->detail_batch[] = $detail_batch;
1240 }
1241
1242 $this->lines[] = $line;
1243 }
1244
1245 return 1;
1246 } else {
1247 return -1;
1248 }
1249 }
1250
1261 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1262 {
1263 global $langs, $hookmanager;
1264
1265 $result = '';
1266 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1267 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1268 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1269
1270 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1271
1272 if ($short) {
1273 return $url;
1274 }
1275
1276 $linkclose = '';
1277 if (empty($notooltip)) {
1278 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1279 $label = $langs->trans("Reception");
1280 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1281 }
1282 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1283 $linkclose .= ' class="classfortooltip"';
1284 }
1285
1286 $linkstart = '<a href="'.$url.'"';
1287 $linkstart .= $linkclose.'>';
1288 $linkend = '</a>';
1289
1290 $result .= $linkstart;
1291 if ($withpicto) {
1292 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1293 }
1294 if ($withpicto != 2) {
1295 $result .= $this->ref;
1296 }
1297
1298 $result .= $linkend;
1299
1300 global $action;
1301 $hookmanager->initHooks(array($this->element . 'dao'));
1302 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1303 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1304 if ($reshook > 0) {
1305 $result = $hookmanager->resPrint;
1306 } else {
1307 $result .= $hookmanager->resPrint;
1308 }
1309 return $result;
1310 }
1311
1318 public function getLibStatut($mode = 0)
1319 {
1320 return $this->LibStatut($this->statut, $mode);
1321 }
1322
1323 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1331 public function LibStatut($status, $mode)
1332 {
1333 // phpcs:enable
1334 global $langs;
1335
1336 // List of long language codes for status
1337 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1338 $this->labelStatus[0] = 'StatusReceptionDraft';
1339 // product to receive if stock increase is on close or already received if stock increase is on validation
1340 $this->labelStatus[1] = 'StatusReceptionValidated';
1341 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1342 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1343 }
1344 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1345 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1346 }
1347 $this->labelStatus[2] = 'StatusReceptionProcessed';
1348
1349 // List of short language codes for status
1350 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1351 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1352 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1353 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1354
1355 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1356 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1357
1358 $statusType = 'status'.$status;
1359 if ($status == self::STATUS_VALIDATED) {
1360 $statusType = 'status4';
1361 }
1362 if ($status == self::STATUS_CLOSED) {
1363 $statusType = 'status6';
1364 }
1365
1366 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1367 }
1368
1376 public function getKanbanView($option = '', $arraydata = null)
1377 {
1378 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1379
1380 $return = '<div class="box-flex-item box-flex-grow-zero">';
1381 $return .= '<div class="info-box info-box-sm">';
1382 $return .= '<div class="info-box-icon bg-infobox-action">';
1383 $return .= img_picto('', 'order');
1384 $return .= '</div>';
1385 $return .= '<div class="info-box-content">';
1386 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1387 if ($selected >= 0) {
1388 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1389 }
1390 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1391 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1392 }
1393 /*if (property_exists($this, 'total_ht')) {
1394 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1395 }*/
1396 if (method_exists($this, 'getLibStatut')) {
1397 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1398 }
1399 $return .= '</div>';
1400 $return .= '</div>';
1401 $return .= '</div>';
1402
1403 return $return;
1404 }
1405
1413 public function initAsSpecimen()
1414 {
1415 global $langs;
1416
1417 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1418 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1419 $now = dol_now();
1420
1421 dol_syslog(get_class($this)."::initAsSpecimen");
1422
1423 $order = new CommandeFournisseur($this->db);
1424 $order->initAsSpecimen();
1425
1426 // Initialise parametres
1427 $this->id = 0;
1428 $this->ref = 'SPECIMEN';
1429 $this->specimen = 1;
1430 $this->statut = 1;
1431 $this->date = $now;
1432 $this->date_creation = $now;
1433 $this->date_valid = $now;
1434 $this->date_delivery = $now;
1435 $this->date_reception = $now + 24 * 3600;
1436
1437 $this->entrepot_id = 0;
1438 $this->socid = 1;
1439
1440 $this->origin_id = 1;
1441 $this->origin = 'commande';
1442 $this->origin_object = $order;
1443 $this->commandeFournisseur = $order; // deprecated
1444
1445 $this->note_private = 'Private note';
1446 $this->note_public = 'Public note';
1447
1448 $nbp = 5;
1449 $xnbp = 0;
1450 while ($xnbp < $nbp) {
1451 $line = new CommandeFournisseurDispatch($this->db);
1452 $line->desc = $langs->trans("Description")." ".$xnbp;
1453 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1454 $line->label = $langs->trans("Description")." ".$xnbp;
1455 $line->qty = 10;
1456
1457 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1458
1459 $this->lines[] = $line;
1460 $xnbp++;
1461 }
1462 }
1463
1471 public function setDeliveryDate($user, $delivery_date)
1472 {
1473 // phpcs:enable
1474 if ($user->hasRight('reception', 'creer')) {
1475 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1476 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1477 $sql .= " WHERE rowid = ".((int) $this->id);
1478
1479 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1480 $resql = $this->db->query($sql);
1481 if ($resql) {
1482 $this->date_delivery = $delivery_date;
1483 return 1;
1484 } else {
1485 $this->error = $this->db->error();
1486 return -1;
1487 }
1488 } else {
1489 return -2;
1490 }
1491 }
1492
1493 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1499 public function fetch_delivery_methods()
1500 {
1501 // phpcs:enable
1502 global $langs;
1503 $this->meths = array();
1504
1505 $sql = "SELECT em.rowid, em.code, em.libelle";
1506 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1507 $sql .= " WHERE em.active = 1";
1508 $sql .= " ORDER BY em.libelle ASC";
1509
1510 $resql = $this->db->query($sql);
1511 if ($resql) {
1512 while ($obj = $this->db->fetch_object($resql)) {
1513 $label = $langs->trans('ReceptionMethod'.$obj->code);
1514 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1515 }
1516 }
1517 }
1518
1519 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1526 public function list_delivery_methods($id = '')
1527 {
1528 // phpcs:enable
1529 global $langs;
1530
1531 $this->listmeths = array();
1532 $i = 0;
1533
1534 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1535 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1536 if ($id != '') {
1537 $sql .= " WHERE em.rowid = ".((int) $id);
1538 }
1539
1540 $resql = $this->db->query($sql);
1541 if ($resql) {
1542 while ($obj = $this->db->fetch_object($resql)) {
1543 $this->listmeths[$i]['rowid'] = $obj->rowid;
1544 $this->listmeths[$i]['code'] = $obj->code;
1545 $label = $langs->trans('ReceptionMethod'.$obj->code);
1546 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1547 $this->listmeths[$i]['description'] = $obj->description;
1548 $this->listmeths[$i]['tracking'] = $obj->tracking;
1549 $this->listmeths[$i]['active'] = $obj->active;
1550 $i++;
1551 }
1552 }
1553 }
1554
1561 public function getUrlTrackingStatus($value = '')
1562 {
1563 if (!empty($this->shipping_method_id)) {
1564 $sql = "SELECT em.code, em.tracking";
1565 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1566 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1567
1568 $resql = $this->db->query($sql);
1569 if ($resql) {
1570 if ($obj = $this->db->fetch_object($resql)) {
1571 $tracking = $obj->tracking;
1572 }
1573 }
1574 }
1575
1576 if (!empty($tracking) && !empty($value)) {
1577 $url = str_replace('{TRACKID}', $value, $tracking);
1578 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">'.($value ? $value : 'url').'</a>', $url, $url);
1579 } else {
1580 $this->tracking_url = $value;
1581 }
1582 }
1583
1589 public function setClosed()
1590 {
1591 global $conf, $langs, $user;
1592
1593 $error = 0;
1594
1595 // Protection. This avoid to move stock later when we should not
1596 if ($this->statut == Reception::STATUS_CLOSED) {
1597 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1598 return 0;
1599 }
1600
1601 $this->db->begin();
1602
1603 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1604 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1605
1606 $resql = $this->db->query($sql);
1607 if ($resql) {
1608 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1609 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1610 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1611
1612 $order = new CommandeFournisseur($this->db);
1613 $order->fetch($this->origin_id);
1614
1615 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1616
1617 $receptions_match_order = 1;
1618 foreach ($order->lines as $line) {
1619 $lineid = $line->id;
1620 $qty = $line->qty;
1621 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1622 $receptions_match_order = 0;
1623 $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';
1624 dol_syslog($text);
1625 break;
1626 }
1627 }
1628 if ($receptions_match_order) {
1629 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');
1630 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1631 }
1632 }
1633
1634 $this->statut = self::STATUS_CLOSED;
1635 $this->status = self::STATUS_CLOSED;
1636
1637 // If stock increment is done on closing
1638 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1639 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1640
1641 $langs->load("agenda");
1642
1643 // Loop on each product line to add a stock movement
1644 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1645 $sql = "SELECT cd.fk_product, cd.subprice,";
1646 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1647 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1648 $sql .= " ed.cost_price";
1649 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1650 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1651 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1652 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1653
1654 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1655 $resql = $this->db->query($sql);
1656
1657 if ($resql) {
1658 $cpt = $this->db->num_rows($resql);
1659 for ($i = 0; $i < $cpt; $i++) {
1660 $obj = $this->db->fetch_object($resql);
1661
1662 $qty = $obj->qty;
1663
1664 if ($qty <= 0) {
1665 continue;
1666 }
1667 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1668
1669 $mouvS = new MouvementStock($this->db);
1670 $mouvS->origin = &$this;
1671 $mouvS->setOrigin($this->element, $this->id);
1672
1673 if (empty($obj->batch)) {
1674 // line without batch detail
1675
1676 // 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
1677 $inventorycode = '';
1678 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1679 if ($result < 0) {
1680 $this->error = $mouvS->error;
1681 $this->errors = $mouvS->errors;
1682 $error++;
1683 break;
1684 }
1685 } else {
1686 // line with batch detail
1687
1688 // 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
1689 $inventorycode = '';
1690 $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);
1691
1692 if ($result < 0) {
1693 $this->error = $mouvS->error;
1694 $this->errors = $mouvS->errors;
1695 $error++;
1696 break;
1697 }
1698 }
1699 }
1700 } else {
1701 $this->error = $this->db->lasterror();
1702 $error++;
1703 }
1704 }
1705
1706 // Call trigger
1707 if (!$error) {
1708 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1709 if ($result < 0) {
1710 $error++;
1711 }
1712 }
1713 } else {
1714 dol_print_error($this->db);
1715 $error++;
1716 }
1717
1718 if (!$error) {
1719 $this->db->commit();
1720 return 1;
1721 } else {
1722 $this->db->rollback();
1723 return -1;
1724 }
1725 }
1726
1732 public function setBilled()
1733 {
1734 global $user;
1735 $error = 0;
1736
1737 $this->db->begin();
1738
1739 if ($this->statut == Reception::STATUS_VALIDATED) {
1740 // do not close if already closed
1741 $this->setClosed();
1742 }
1743
1744 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1745 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1746
1747 $resql = $this->db->query($sql);
1748 if ($resql) {
1749 $this->billed = 1;
1750
1751 // Call trigger
1752 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1753 if ($result < 0) {
1754 $this->billed = 0;
1755 $error++;
1756 }
1757 } else {
1758 $error++;
1759 $this->errors[] = $this->db->lasterror;
1760 }
1761
1762 if (empty($error)) {
1763 $this->db->commit();
1764 return 1;
1765 } else {
1766 $this->db->rollback();
1767 return -1;
1768 }
1769 }
1770
1776 public function reOpen()
1777 {
1778 global $conf, $langs, $user;
1779
1780 $error = 0;
1781
1782 $this->db->begin();
1783
1784 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1785 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1786
1787 $resql = $this->db->query($sql);
1788 if ($resql) {
1789 $this->statut = self::STATUS_VALIDATED;
1790 $this->status = self::STATUS_VALIDATED;
1791 $this->billed = 0;
1792
1793 // If stock increment is done on closing
1794 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1795 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1796 $numref = $this->ref;
1797 $langs->load("agenda");
1798
1799 // Loop on each product line to add a stock movement
1800 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1801 $sql = "SELECT ed.fk_product, cd.subprice,";
1802 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1803 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1804 $sql .= " ed.cost_price";
1805 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1806 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1807 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1808 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1809
1810 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1811 $resql = $this->db->query($sql);
1812 if ($resql) {
1813 $cpt = $this->db->num_rows($resql);
1814 for ($i = 0; $i < $cpt; $i++) {
1815 $obj = $this->db->fetch_object($resql);
1816
1817 $qty = $obj->qty;
1818
1819 if ($qty <= 0) {
1820 continue;
1821 }
1822
1823 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1824
1825 //var_dump($this->lines[$i]);
1826 $mouvS = new MouvementStock($this->db);
1827 $mouvS->origin = &$this;
1828 $mouvS->setOrigin($this->element, $this->id);
1829
1830 if (empty($obj->batch)) {
1831 // line without batch detail
1832
1833 // 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
1834 $inventorycode = '';
1835 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1836
1837 if ($result < 0) {
1838 $this->error = $mouvS->error;
1839 $this->errors = $mouvS->errors;
1840 $error++;
1841 break;
1842 }
1843 } else {
1844 // line with batch detail
1845
1846 // 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
1847 $inventorycode = '';
1848 $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);
1849 if ($result < 0) {
1850 $this->error = $mouvS->error;
1851 $this->errors = $mouvS->errors;
1852 $error++;
1853 break;
1854 }
1855 }
1856 }
1857 } else {
1858 $this->error = $this->db->lasterror();
1859 $error++;
1860 }
1861 }
1862
1863 if (!$error) {
1864 // Call trigger
1865 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1866 if ($result < 0) {
1867 $error++;
1868 }
1869 }
1870
1871 if (!$error && $this->origin == 'order_supplier') {
1872 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1873
1874 $commande = new CommandeFournisseur($this->db);
1875 $commande->fetch($this->origin_id);
1876 $result = $commande->setStatus($user, 4);
1877 if ($result < 0) {
1878 $error++;
1879 $this->error = $commande->error;
1880 $this->errors = $commande->errors;
1881 }
1882 }
1883 } else {
1884 $error++;
1885 $this->errors[] = $this->db->lasterror();
1886 }
1887
1888 if (!$error) {
1889 $this->db->commit();
1890 return 1;
1891 } else {
1892 $this->db->rollback();
1893 return -1;
1894 }
1895 }
1896
1903 public function setDraft($user)
1904 {
1905 // phpcs:enable
1906 global $conf, $langs;
1907
1908 $error = 0;
1909
1910 // Protection
1911 if ($this->statut <= self::STATUS_DRAFT) {
1912 return 0;
1913 }
1914
1915 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1916 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1917 $this->error = 'Permission denied';
1918 return -1;
1919 }
1920
1921 $this->db->begin();
1922
1923 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1924 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1925 $sql .= " WHERE rowid = ".((int) $this->id);
1926
1927 dol_syslog(__METHOD__, LOG_DEBUG);
1928 if ($this->db->query($sql)) {
1929 // If stock increment is done on closing
1930 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1931 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1932
1933 $langs->load("agenda");
1934
1935 // Loop on each product line to add a stock movement
1936 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1937 $sql = "SELECT cd.fk_product, cd.subprice,";
1938 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1939 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1940 $sql .= " ed.cost_price";
1941 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1942 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1943 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1944 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1945
1946 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1947 $resql = $this->db->query($sql);
1948 if ($resql) {
1949 $cpt = $this->db->num_rows($resql);
1950 for ($i = 0; $i < $cpt; $i++) {
1951 $obj = $this->db->fetch_object($resql);
1952
1953 $qty = $obj->qty;
1954
1955
1956 if ($qty <= 0) {
1957 continue;
1958 }
1959 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1960
1961 //var_dump($this->lines[$i]);
1962 $mouvS = new MouvementStock($this->db);
1963 $mouvS->origin = &$this;
1964 $mouvS->setOrigin($this->element, $this->id);
1965
1966 if (empty($obj->batch)) {
1967 // line without batch detail
1968
1969 // 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
1970 $inventorycode = '';
1971 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1972 if ($result < 0) {
1973 $this->error = $mouvS->error;
1974 $this->errors = $mouvS->errors;
1975 $error++;
1976 break;
1977 }
1978 } else {
1979 // line with batch detail
1980
1981 // 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
1982 $inventorycode = '';
1983 $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);
1984 if ($result < 0) {
1985 $this->error = $mouvS->error;
1986 $this->errors = $mouvS->errors;
1987 $error++;
1988 break;
1989 }
1990 }
1991 }
1992 } else {
1993 $this->error = $this->db->lasterror();
1994 $error++;
1995 }
1996 }
1997
1998 if (!$error) {
1999 // Call trigger
2000 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2001 if ($result < 0) {
2002 $error++;
2003 }
2004 }
2005 if ($this->origin == 'order_supplier') {
2006 if (!empty($this->origin) && $this->origin_id > 0) {
2007 $this->fetch_origin();
2008 $origin = $this->origin;
2009 if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
2010 // Check if there is no more reception validated.
2011 $this->$origin->fetchObjectLinked();
2012 $setStatut = 1;
2013 if (!empty($this->$origin->linkedObjects['reception'])) {
2014 foreach ($this->$origin->linkedObjects['reception'] as $rcption) {
2015 if ($rcption->statut > 0) {
2016 $setStatut = 0;
2017 break;
2018 }
2019 }
2020 //var_dump($this->$origin->receptions);exit;
2021 if ($setStatut) {
2022 $this->$origin->setStatut(3); // ordered
2023 }
2024 }
2025 }
2026 }
2027 }
2028
2029 if (!$error) {
2030 $this->statut = self::STATUS_DRAFT;
2031 $this->status = self::STATUS_DRAFT;
2032 $this->db->commit();
2033 return 1;
2034 } else {
2035 $this->db->rollback();
2036 return -1;
2037 }
2038 } else {
2039 $this->error = $this->db->error();
2040 $this->db->rollback();
2041 return -1;
2042 }
2043 }
2044
2055 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2056 {
2057 global $conf, $langs;
2058
2059 $langs->load("receptions");
2060
2061 if (!dol_strlen($modele)) {
2062 $modele = 'squille';
2063
2064 if ($this->model_pdf) {
2065 $modele = $this->model_pdf;
2066 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2067 $modele = $conf->global->RECEPTION_ADDON_PDF;
2068 }
2069 }
2070
2071 $modelpath = "core/modules/reception/doc/";
2072
2073 $this->fetch_origin();
2074
2075 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2076 }
2077
2086 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2087 {
2088 $tables = array('reception');
2089
2090 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2091 }
2092
2101 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2102 {
2103 $tables = array(
2104 'commande_fournisseur_dispatch'
2105 );
2106
2107 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2108 }
2109}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Definition security.php:604
$object ref
Definition info.php:79
Class to manage table commandefournisseurdispatch.
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_RECEIVED_COMPLETELY
Received completely.
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.
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.
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 clicable link of object (with eventually picto)
update($user=null, $notrigger=0)
Update database.
setClosed()
Classify the reception as closed (this record 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.
addline($entrepot_id, $id, $qty, $array_options=0, $comment='', $eatby='', $sellby='', $batch='', $cost_price=0)
Add an reception line.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create a document onto disk according to template module.
list_delivery_methods($id='')
Fetch all deliveries method and return an array.
setDeliveryDate($user, $delivery_date)
Set the planned delivery date.
getKanbanView($option='', $arraydata=null)
Return clicable 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.
setDraft($user)
Set draft status.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
trait CommonIncoterm
Superclass for incoterm classes.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
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_dir_list($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:62
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1954
$conf db user
Definition repair.php:125