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