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