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 }
1070 } else {
1071 $error++;
1072 $this->errors[] = "Error ".$this->db->lasterror();
1073 }
1074 }
1075
1076 if (!$error) {
1077 $main = MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
1078 $ef = $main."_extrafields";
1079
1080 $sqlef = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_reception = ".((int) $this->id).")";
1081
1082 $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
1083 $sql .= " WHERE fk_reception = ".((int) $this->id);
1084
1085 if ($this->db->query($sqlef) && $this->db->query($sql)) {
1086 // Delete linked object
1087 $res = $this->deleteObjectLinked();
1088 if ($res < 0) {
1089 $error++;
1090 }
1091
1092 if (!$error) {
1093 $sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
1094 $sql .= " WHERE rowid = ".((int) $this->id);
1095
1096 if ($this->db->query($sql)) {
1097 // Call trigger
1098 $result = $this->call_trigger('RECEPTION_DELETE', $user);
1099 if ($result < 0) {
1100 $error++;
1101 }
1102 // End call triggers
1103
1104 if (!empty($this->origin) && $this->origin_id > 0) {
1105 $this->fetch_origin();
1106 $origin = $this->origin;
1107 if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1108 // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1109 $this->$origin->loadReceptions();
1110 //var_dump($this->$origin->receptions);exit;
1111 if (count($this->$origin->receptions) <= 0) {
1112 $this->$origin->setStatut(3); // ordered
1113 }
1114 }
1115 }
1116
1117 if (!$error) {
1118 $this->db->commit();
1119
1120 // We delete PDFs
1121 $ref = dol_sanitizeFileName($this->ref);
1122 if (!empty($conf->reception->dir_output)) {
1123 $dir = $conf->reception->dir_output.'/'.$ref;
1124 $file = $dir.'/'.$ref.'.pdf';
1125 if (file_exists($file)) {
1126 if (!dol_delete_file($file)) {
1127 return 0;
1128 }
1129 }
1130 if (file_exists($dir)) {
1131 if (!dol_delete_dir_recursive($dir)) {
1132 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1133 return 0;
1134 }
1135 }
1136 }
1137
1138 return 1;
1139 } else {
1140 $this->db->rollback();
1141 return -1;
1142 }
1143 } else {
1144 $this->error = $this->db->lasterror()." - sql=$sql";
1145 $this->db->rollback();
1146 return -3;
1147 }
1148 } else {
1149 $this->error = $this->db->lasterror()." - sql=$sql";
1150 $this->db->rollback();
1151 return -2;
1152 }
1153 } else {
1154 $this->error = $this->db->lasterror()." - sql=$sql";
1155 $this->db->rollback();
1156 return -1;
1157 }
1158 } else {
1159 $this->db->rollback();
1160 return -1;
1161 }
1162 }
1163
1164 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1170 public function fetch_lines()
1171 {
1172 // phpcs:enable
1173 $this->lines = array();
1174
1175 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1176
1177 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch WHERE fk_reception = ".((int) $this->id);
1178 $resql = $this->db->query($sql);
1179
1180 if (!empty($resql)) {
1181 while ($obj = $this->db->fetch_object($resql)) {
1182 $line = new CommandeFournisseurDispatch($this->db);
1183
1184 $line->fetch($obj->rowid);
1185
1186 // TODO Remove or keep this ?
1187 $line->fetch_product();
1188
1189 $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1190 $sql_commfourndet .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet';
1191 $sql_commfourndet .= ' WHERE rowid = '.((int) $line->fk_commandefourndet);
1192 $sql_commfourndet .= ' ORDER BY rang';
1193
1194 $resql_commfourndet = $this->db->query($sql_commfourndet);
1195 if (!empty($resql_commfourndet)) {
1196 $obj = $this->db->fetch_object($resql_commfourndet);
1197 $line->qty_asked = $obj->qty;
1198 $line->description = $obj->description;
1199 $line->desc = $obj->description;
1200 $line->tva_tx = $obj->tva_tx;
1201 $line->vat_src_code = $obj->vat_src_code;
1202 $line->subprice = $obj->subprice;
1203 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1204 $line->remise_percent = $obj->remise_percent;
1205 $line->label = !empty($obj->label) ? $obj->label : $line->product->label;
1206 $line->ref_supplier = $obj->ref;
1207 $line->total_ht = $obj->total_ht;
1208 $line->total_ttc = $obj->total_ttc;
1209 $line->total_tva = $obj->total_tva;
1210 } else {
1211 $line->qty_asked = 0;
1212 $line->description = '';
1213 $line->desc = '';
1214 $line->label = $obj->label;
1215 }
1216
1217 $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1218 $tva = $pu_ht * $line->tva_tx / 100;
1219 $this->total_ht += $pu_ht;
1220 $this->total_tva += $pu_ht * $line->tva_tx / 100;
1221
1222 $this->total_ttc += $pu_ht + $tva;
1223
1224 if (isModEnabled('productbatch') && !empty($line->batch)) {
1225 $detail_batch = new stdClass();
1226 $detail_batch->eatby = $line->eatby;
1227 $detail_batch->sellby = $line->sellby;
1228 $detail_batch->batch = $line->batch;
1229 $detail_batch->qty = $line->qty;
1230 $line->detail_batch[] = $detail_batch;
1231 }
1232
1233 $this->lines[] = $line;
1234 }
1235
1236 return 1;
1237 } else {
1238 return -1;
1239 }
1240 }
1241
1252 public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1253 {
1254 global $langs, $hookmanager;
1255
1256 $result = '';
1257 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Reception").'</u>';
1258 $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1259 $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : '');
1260
1261 $url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1262
1263 if ($short) {
1264 return $url;
1265 }
1266
1267 $linkclose = '';
1268 if (empty($notooltip)) {
1269 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1270 $label = $langs->trans("Reception");
1271 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1272 }
1273 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1274 $linkclose .= ' class="classfortooltip"';
1275 }
1276
1277 $linkstart = '<a href="'.$url.'"';
1278 $linkstart .= $linkclose.'>';
1279 $linkend = '</a>';
1280
1281 $result .= $linkstart;
1282 if ($withpicto) {
1283 $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1284 }
1285 if ($withpicto != 2) {
1286 $result .= $this->ref;
1287 }
1288
1289 $result .= $linkend;
1290
1291 global $action;
1292 $hookmanager->initHooks(array($this->element . 'dao'));
1293 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1294 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1295 if ($reshook > 0) {
1296 $result = $hookmanager->resPrint;
1297 } else {
1298 $result .= $hookmanager->resPrint;
1299 }
1300 return $result;
1301 }
1302
1309 public function getLibStatut($mode = 0)
1310 {
1311 return $this->LibStatut($this->statut, $mode);
1312 }
1313
1314 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1322 public function LibStatut($status, $mode)
1323 {
1324 // phpcs:enable
1325 global $langs;
1326
1327 // List of long language codes for status
1328 $this->labelStatus[-1] = 'StatusReceptionCanceled';
1329 $this->labelStatus[0] = 'StatusReceptionDraft';
1330 // product to receive if stock increase is on close or already received if stock increase is on validation
1331 $this->labelStatus[1] = 'StatusReceptionValidated';
1332 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1333 $this->labelStatus[1] = 'StatusReceptionValidatedReceived';
1334 }
1335 if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1336 $this->labelStatus[1] = 'StatusReceptionValidatedToReceive';
1337 }
1338 $this->labelStatus[2] = 'StatusReceptionProcessed';
1339
1340 // List of short language codes for status
1341 $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1342 $this->labelStatusShort[0] = 'StatusReceptionDraftShort';
1343 $this->labelStatusShort[1] = 'StatusReceptionValidatedShort';
1344 $this->labelStatusShort[2] = 'StatusReceptionProcessedShort';
1345
1346 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1347 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1348
1349 $statusType = 'status'.$status;
1350 if ($status == self::STATUS_VALIDATED) {
1351 $statusType = 'status4';
1352 }
1353 if ($status == self::STATUS_CLOSED) {
1354 $statusType = 'status6';
1355 }
1356
1357 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1358 }
1359
1367 public function getKanbanView($option = '', $arraydata = null)
1368 {
1369 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1370
1371 $return = '<div class="box-flex-item box-flex-grow-zero">';
1372 $return .= '<div class="info-box info-box-sm">';
1373 $return .= '<div class="info-box-icon bg-infobox-action">';
1374 $return .= img_picto('', 'order');
1375 $return .= '</div>';
1376 $return .= '<div class="info-box-content">';
1377 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1378 if ($selected >= 0) {
1379 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1380 }
1381 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1382 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
1383 }
1384 /*if (property_exists($this, 'total_ht')) {
1385 $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1386 }*/
1387 if (method_exists($this, 'getLibStatut')) {
1388 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1389 }
1390 $return .= '</div>';
1391 $return .= '</div>';
1392 $return .= '</div>';
1393
1394 return $return;
1395 }
1396
1404 public function initAsSpecimen()
1405 {
1406 global $langs;
1407
1408 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
1409 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
1410 $now = dol_now();
1411
1412 dol_syslog(get_class($this)."::initAsSpecimen");
1413
1414 $order = new CommandeFournisseur($this->db);
1415 $order->initAsSpecimen();
1416
1417 // Initialise parametres
1418 $this->id = 0;
1419 $this->ref = 'SPECIMEN';
1420 $this->specimen = 1;
1421 $this->statut = 1;
1422 $this->date = $now;
1423 $this->date_creation = $now;
1424 $this->date_valid = $now;
1425 $this->date_delivery = $now;
1426 $this->date_reception = $now + 24 * 3600;
1427
1428 $this->entrepot_id = 0;
1429 $this->socid = 1;
1430
1431 $this->origin_id = 1;
1432 $this->origin = 'commande';
1433 $this->origin_object = $order;
1434 $this->commandeFournisseur = $order; // deprecated
1435
1436 $this->note_private = 'Private note';
1437 $this->note_public = 'Public note';
1438
1439 $nbp = 5;
1440 $xnbp = 0;
1441 while ($xnbp < $nbp) {
1442 $line = new CommandeFournisseurDispatch($this->db);
1443 $line->desc = $langs->trans("Description")." ".$xnbp;
1444 $line->libelle = $langs->trans("Description")." ".$xnbp; // deprecated
1445 $line->label = $langs->trans("Description")." ".$xnbp;
1446 $line->qty = 10;
1447
1448 $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1449
1450 $this->lines[] = $line;
1451 $xnbp++;
1452 }
1453 }
1454
1462 public function setDeliveryDate($user, $delivery_date)
1463 {
1464 // phpcs:enable
1465 if ($user->hasRight('reception', 'creer')) {
1466 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1467 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1468 $sql .= " WHERE rowid = ".((int) $this->id);
1469
1470 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1471 $resql = $this->db->query($sql);
1472 if ($resql) {
1473 $this->date_delivery = $delivery_date;
1474 return 1;
1475 } else {
1476 $this->error = $this->db->error();
1477 return -1;
1478 }
1479 } else {
1480 return -2;
1481 }
1482 }
1483
1484 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1490 public function fetch_delivery_methods()
1491 {
1492 // phpcs:enable
1493 global $langs;
1494 $this->meths = array();
1495
1496 $sql = "SELECT em.rowid, em.code, em.libelle";
1497 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1498 $sql .= " WHERE em.active = 1";
1499 $sql .= " ORDER BY em.libelle ASC";
1500
1501 $resql = $this->db->query($sql);
1502 if ($resql) {
1503 while ($obj = $this->db->fetch_object($resql)) {
1504 $label = $langs->trans('ReceptionMethod'.$obj->code);
1505 $this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1506 }
1507 }
1508 }
1509
1510 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1517 public function list_delivery_methods($id = '')
1518 {
1519 // phpcs:enable
1520 global $langs;
1521
1522 $this->listmeths = array();
1523 $i = 0;
1524
1525 $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1526 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1527 if ($id != '') {
1528 $sql .= " WHERE em.rowid = ".((int) $id);
1529 }
1530
1531 $resql = $this->db->query($sql);
1532 if ($resql) {
1533 while ($obj = $this->db->fetch_object($resql)) {
1534 $this->listmeths[$i]['rowid'] = $obj->rowid;
1535 $this->listmeths[$i]['code'] = $obj->code;
1536 $label = $langs->trans('ReceptionMethod'.$obj->code);
1537 $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code ? $label : $obj->libelle);
1538 $this->listmeths[$i]['description'] = $obj->description;
1539 $this->listmeths[$i]['tracking'] = $obj->tracking;
1540 $this->listmeths[$i]['active'] = $obj->active;
1541 $i++;
1542 }
1543 }
1544 }
1545
1552 public function getUrlTrackingStatus($value = '')
1553 {
1554 if (!empty($this->shipping_method_id)) {
1555 $sql = "SELECT em.code, em.tracking";
1556 $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1557 $sql .= " WHERE em.rowid = ".((int) $this->shipping_method_id);
1558
1559 $resql = $this->db->query($sql);
1560 if ($resql) {
1561 if ($obj = $this->db->fetch_object($resql)) {
1562 $tracking = $obj->tracking;
1563 }
1564 }
1565 }
1566
1567 if (!empty($tracking) && !empty($value)) {
1568 $url = str_replace('{TRACKID}', $value, $tracking);
1569 $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">'.($value ? $value : 'url').'</a>', $url, $url);
1570 } else {
1571 $this->tracking_url = $value;
1572 }
1573 }
1574
1580 public function setClosed()
1581 {
1582 global $conf, $langs, $user;
1583
1584 $error = 0;
1585
1586 // Protection. This avoid to move stock later when we should not
1587 if ($this->statut == Reception::STATUS_CLOSED) {
1588 dol_syslog(get_class($this)."::setClosed already in closed status", LOG_WARNING);
1589 return 0;
1590 }
1591
1592 $this->db->begin();
1593
1594 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut = '.self::STATUS_CLOSED;
1595 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1596
1597 $resql = $this->db->query($sql);
1598 if ($resql) {
1599 // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1600 if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
1601 $order = new CommandeFournisseur($this->db);
1602 $order->fetch($this->origin_id);
1603
1604 $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1605
1606 $receptions_match_order = 1;
1607 foreach ($order->lines as $line) {
1608 $lineid = $line->id;
1609 $qty = $line->qty;
1610 if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1611 $receptions_match_order = 0;
1612 $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';
1613 dol_syslog($text);
1614 break;
1615 }
1616 }
1617 if ($receptions_match_order) {
1618 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');
1619 $order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1620 }
1621 }
1622
1623 $this->statut = self::STATUS_CLOSED;
1624 $this->status = self::STATUS_CLOSED;
1625
1626 // If stock increment is done on closing
1627 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1628 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1629
1630 $langs->load("agenda");
1631
1632 // Loop on each product line to add a stock movement
1633 // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1634 $sql = "SELECT cd.fk_product, cd.subprice,";
1635 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1636 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1637 $sql .= " ed.cost_price";
1638 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1639 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1640 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1641 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1642
1643 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1644 $resql = $this->db->query($sql);
1645
1646 if ($resql) {
1647 $cpt = $this->db->num_rows($resql);
1648 for ($i = 0; $i < $cpt; $i++) {
1649 $obj = $this->db->fetch_object($resql);
1650
1651 $qty = $obj->qty;
1652
1653 if ($qty <= 0) {
1654 continue;
1655 }
1656 dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1657
1658 $mouvS = new MouvementStock($this->db);
1659 $mouvS->origin = &$this;
1660 $mouvS->setOrigin($this->element, $this->id);
1661
1662 if (empty($obj->batch)) {
1663 // line without batch detail
1664
1665 // 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
1666 $inventorycode = '';
1667 $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1668 if ($result < 0) {
1669 $this->error = $mouvS->error;
1670 $this->errors = $mouvS->errors;
1671 $error++;
1672 break;
1673 }
1674 } else {
1675 // line with batch detail
1676
1677 // 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
1678 $inventorycode = '';
1679 $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);
1680
1681 if ($result < 0) {
1682 $this->error = $mouvS->error;
1683 $this->errors = $mouvS->errors;
1684 $error++;
1685 break;
1686 }
1687 }
1688 }
1689 } else {
1690 $this->error = $this->db->lasterror();
1691 $error++;
1692 }
1693 }
1694
1695 // Call trigger
1696 if (!$error) {
1697 $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1698 if ($result < 0) {
1699 $error++;
1700 }
1701 }
1702 } else {
1703 dol_print_error($this->db);
1704 $error++;
1705 }
1706
1707 if (!$error) {
1708 $this->db->commit();
1709 return 1;
1710 } else {
1711 $this->db->rollback();
1712 return -1;
1713 }
1714 }
1715
1721 public function setBilled()
1722 {
1723 global $user;
1724 $error = 0;
1725
1726 $this->db->begin();
1727
1728 if ($this->statut == Reception::STATUS_VALIDATED) {
1729 // do not close if already closed
1730 $this->setClosed();
1731 }
1732
1733 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET billed=1';
1734 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1735
1736 $resql = $this->db->query($sql);
1737 if ($resql) {
1738 $this->billed = 1;
1739
1740 // Call trigger
1741 $result = $this->call_trigger('RECEPTION_BILLED', $user);
1742 if ($result < 0) {
1743 $this->billed = 0;
1744 $error++;
1745 }
1746 } else {
1747 $error++;
1748 $this->errors[] = $this->db->lasterror;
1749 }
1750
1751 if (empty($error)) {
1752 $this->db->commit();
1753 return 1;
1754 } else {
1755 $this->db->rollback();
1756 return -1;
1757 }
1758 }
1759
1765 public function reOpen()
1766 {
1767 global $conf, $langs, $user;
1768
1769 $error = 0;
1770
1771 $this->db->begin();
1772
1773 $sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1774 $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0';
1775
1776 $resql = $this->db->query($sql);
1777 if ($resql) {
1778 $this->statut = self::STATUS_VALIDATED;
1779 $this->status = self::STATUS_VALIDATED;
1780 $this->billed = 0;
1781
1782 // If stock increment is done on closing
1783 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1784 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1785 $numref = $this->ref;
1786 $langs->load("agenda");
1787
1788 // Loop on each product line to add a stock movement
1789 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1790 $sql = "SELECT ed.fk_product, cd.subprice,";
1791 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1792 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1793 $sql .= " ed.cost_price";
1794 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1795 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1796 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1797 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1798
1799 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1800 $resql = $this->db->query($sql);
1801 if ($resql) {
1802 $cpt = $this->db->num_rows($resql);
1803 for ($i = 0; $i < $cpt; $i++) {
1804 $obj = $this->db->fetch_object($resql);
1805
1806 $qty = $obj->qty;
1807
1808 if ($qty <= 0) {
1809 continue;
1810 }
1811
1812 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1813
1814 //var_dump($this->lines[$i]);
1815 $mouvS = new MouvementStock($this->db);
1816 $mouvS->origin = &$this;
1817 $mouvS->setOrigin($this->element, $this->id);
1818
1819 if (empty($obj->batch)) {
1820 // line without batch detail
1821
1822 // 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
1823 $inventorycode = '';
1824 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1825
1826 if ($result < 0) {
1827 $this->error = $mouvS->error;
1828 $this->errors = $mouvS->errors;
1829 $error++;
1830 break;
1831 }
1832 } else {
1833 // line with batch detail
1834
1835 // 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
1836 $inventorycode = '';
1837 $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);
1838 if ($result < 0) {
1839 $this->error = $mouvS->error;
1840 $this->errors = $mouvS->errors;
1841 $error++;
1842 break;
1843 }
1844 }
1845 }
1846 } else {
1847 $this->error = $this->db->lasterror();
1848 $error++;
1849 }
1850 }
1851
1852 if (!$error) {
1853 // Call trigger
1854 $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1855 if ($result < 0) {
1856 $error++;
1857 }
1858 }
1859
1860 if (!$error && $this->origin == 'order_supplier') {
1861 $commande = new CommandeFournisseur($this->db);
1862 $commande->fetch($this->origin_id);
1863 $result = $commande->setStatus($user, 4);
1864 if ($result < 0) {
1865 $error++;
1866 $this->error = $commande->error;
1867 $this->errors = $commande->errors;
1868 }
1869 }
1870 } else {
1871 $error++;
1872 $this->errors[] = $this->db->lasterror();
1873 }
1874
1875 if (!$error) {
1876 $this->db->commit();
1877 return 1;
1878 } else {
1879 $this->db->rollback();
1880 return -1;
1881 }
1882 }
1883
1890 public function setDraft($user)
1891 {
1892 // phpcs:enable
1893 global $conf, $langs;
1894
1895 $error = 0;
1896
1897 // Protection
1898 if ($this->statut <= self::STATUS_DRAFT) {
1899 return 0;
1900 }
1901
1902 if (!((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1903 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))) {
1904 $this->error = 'Permission denied';
1905 return -1;
1906 }
1907
1908 $this->db->begin();
1909
1910 $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1911 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1912 $sql .= " WHERE rowid = ".((int) $this->id);
1913
1914 dol_syslog(__METHOD__, LOG_DEBUG);
1915 if ($this->db->query($sql)) {
1916 // If stock increment is done on closing
1917 if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1918 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1919
1920 $langs->load("agenda");
1921
1922 // Loop on each product line to add a stock movement
1923 // TODO possibilite de receptionner a partir d'une propale ou autre origine
1924 $sql = "SELECT cd.fk_product, cd.subprice,";
1925 $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1926 $sql .= " ed.eatby, ed.sellby, ed.batch,";
1927 $sql .= " ed.cost_price";
1928 $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1929 $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1930 $sql .= " WHERE ed.fk_reception = ".((int) $this->id);
1931 $sql .= " AND cd.rowid = ed.fk_commandefourndet";
1932
1933 dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1934 $resql = $this->db->query($sql);
1935 if ($resql) {
1936 $cpt = $this->db->num_rows($resql);
1937 for ($i = 0; $i < $cpt; $i++) {
1938 $obj = $this->db->fetch_object($resql);
1939
1940 $qty = $obj->qty;
1941
1942
1943 if ($qty <= 0) {
1944 continue;
1945 }
1946 dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1947
1948 //var_dump($this->lines[$i]);
1949 $mouvS = new MouvementStock($this->db);
1950 $mouvS->origin = &$this;
1951 $mouvS->setOrigin($this->element, $this->id);
1952
1953 if (empty($obj->batch)) {
1954 // line without batch detail
1955
1956 // 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
1957 $inventorycode = '';
1958 $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1959 if ($result < 0) {
1960 $this->error = $mouvS->error;
1961 $this->errors = $mouvS->errors;
1962 $error++;
1963 break;
1964 }
1965 } else {
1966 // line with batch detail
1967
1968 // 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
1969 $inventorycode = '';
1970 $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);
1971 if ($result < 0) {
1972 $this->error = $mouvS->error;
1973 $this->errors = $mouvS->errors;
1974 $error++;
1975 break;
1976 }
1977 }
1978 }
1979 } else {
1980 $this->error = $this->db->lasterror();
1981 $error++;
1982 }
1983 }
1984
1985 if (!$error) {
1986 // Call trigger
1987 $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
1988 if ($result < 0) {
1989 $error++;
1990 }
1991 }
1992 if ($this->origin == 'order_supplier') {
1993 if (!empty($this->origin) && $this->origin_id > 0) {
1994 $this->fetch_origin();
1995 $origin = $this->origin;
1996 if ($this->$origin->statut == 4) { // If order source of reception is "partially received"
1997 // Check if there is no more reception validated.
1998 $this->$origin->fetchObjectLinked();
1999 $setStatut = 1;
2000 if (!empty($this->$origin->linkedObjects['reception'])) {
2001 foreach ($this->$origin->linkedObjects['reception'] as $rcption) {
2002 if ($rcption->statut > 0) {
2003 $setStatut = 0;
2004 break;
2005 }
2006 }
2007 //var_dump($this->$origin->receptions);exit;
2008 if ($setStatut) {
2009 $this->$origin->setStatut(3); // ordered
2010 }
2011 }
2012 }
2013 }
2014 }
2015
2016 if (!$error) {
2017 $this->statut = self::STATUS_DRAFT;
2018 $this->status = self::STATUS_DRAFT;
2019 $this->db->commit();
2020 return 1;
2021 } else {
2022 $this->db->rollback();
2023 return -1;
2024 }
2025 } else {
2026 $this->error = $this->db->error();
2027 $this->db->rollback();
2028 return -1;
2029 }
2030 }
2031
2042 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2043 {
2044 global $conf, $langs;
2045
2046 $langs->load("receptions");
2047
2048 if (!dol_strlen($modele)) {
2049 $modele = 'squille';
2050
2051 if ($this->model_pdf) {
2052 $modele = $this->model_pdf;
2053 } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2054 $modele = $conf->global->RECEPTION_ADDON_PDF;
2055 }
2056 }
2057
2058 $modelpath = "core/modules/reception/doc/";
2059
2060 $this->fetch_origin();
2061
2062 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2063 }
2064
2073 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2074 {
2075 $tables = array('reception');
2076
2077 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2078 }
2079
2088 public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2089 {
2090 $tables = array(
2091 'commande_fournisseur_dispatch'
2092 );
2093
2094 return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2095 }
2096}
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:1907
$conf db user
Definition repair.php:125