dolibarr 25.0.0-alpha
massstockmove.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2013-2022 Laurent Destaileur <ely@users.sourceforge.net>
3 * Copyright (C) 2014 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
29// Load Dolibarr environment
30require '../../main.inc.php';
38require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
40require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
42require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
44require_once DOL_DOCUMENT_ROOT.'/core/modules/import/import_csv.modules.php';
45require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/core/lib/import.lib.php';
47
48$confirm = GETPOST('confirm', 'alpha');
49$filetoimport = GETPOST('filetoimport');
50
51// Load translation files required by the page
52$langs->loadLangs(array('products', 'stocks', 'orders', 'productbatch'));
53
54//init Hook
55$hookmanager->initHooks(array('massstockmove'));
56
57// Security check
58if ($user->socid) {
59 $socid = $user->socid;
60}
61$result = restrictedArea($user, 'produit|service');
62
63//checks if a product has been ordered
64
65$action = GETPOST('action', 'aZ09');
66$id_product = GETPOSTINT('productid');
67$id_sw = GETPOSTINT('id_sw');
68$id_tw = GETPOSTINT('id_tw');
69$batch = GETPOST('batch');
70$qty = GETPOST('qty');
71$idline = GETPOST('idline');
72
73// Load variable for pagination
74$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
75$sortfield = GETPOST('sortfield', 'aZ09comma');
76$sortorder = GETPOST('sortorder', 'aZ09comma');
77$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
78if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
79 // If $page is not defined, or '' or -1 or if we click on clear filters
80 $page = 0;
81}
82$offset = $limit * $page;
83$pageprev = $page - 1;
84$pagenext = $page + 1;
85
86if (!$sortfield) {
87 $sortfield = 'p.ref';
88}
89if (!$sortorder) {
90 $sortorder = 'ASC';
91}
92
93if (GETPOST('init')) {
94 unset($_SESSION['massstockmove']);
95}
96$listofdata = array();
97if (!empty($_SESSION['massstockmove'])) {
98 $listofdata = json_decode($_SESSION['massstockmove'], true);
99 if (!is_array($listofdata)) {
100 $listofdata = array();
101 }
102}
103
104$error = 0;
105
106$permissiontodelete = $user->hasRight('stock', 'mouvement', 'creer');
107
108
109/*
110 * Actions
111 */
112
113if ($action == 'addline' && $user->hasRight('stock', 'mouvement', 'creer')) {
114 if (!($id_sw > 0)) {
115 //$error++;
116 //setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseSource")), null, 'errors');
117 if ($id_sw < 0) {
118 $id_sw = 0;
119 }
120 }
121 if (!($id_tw > 0)) {
122 $error++;
123 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseTarget")), null, 'errors');
124 }
125 if ($id_sw > 0 && $id_tw == $id_sw) {
126 $error++;
127 $langs->load("errors");
128 setEventMessages($langs->trans("ErrorWarehouseMustDiffers"), null, 'errors');
129 }
130 if (!($id_product > 0)) {
131 $error++;
132 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors');
133 }
134 if (!$qty) {
135 $error++;
136 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Qty")), null, 'errors');
137 }
138
139 // Check a batch number is provided if product need it
140 $producttmp = null;
141 if (!$error) {
142 $producttmp = new Product($db);
143 $producttmp->fetch($id_product);
144 if ($producttmp->hasbatch()) {
145 if (empty($batch)) {
146 $error++;
147 $langs->load("errors");
148 setEventMessages($langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $producttmp->ref), null, 'errors');
149 }
150 }
151 }
152
153 // TODO Check qty is ok for stock move. Note qty may not be enough yet, but we make a check now to report a warning.
154 // What is more important is to have qty when doing action 'createmovements'
155 if (!$error && $producttmp !== null) {
156 // Warning, don't forget lines already added into the $_SESSION['massstockmove']
157 if ($producttmp->hasbatch()) {
158 } else {
159 }
160 }
161
162 if (!$error) {
163 if (count(array_keys($listofdata)) > 0) {
164 $id = max(array_keys($listofdata)) + 1;
165 } else {
166 $id = 1;
167 }
168 $listofdata[$id] = array('id' => $id, 'id_product' => $id_product, 'qty' => $qty, 'id_sw' => $id_sw, 'id_tw' => $id_tw, 'batch' => $batch);
169 $_SESSION['massstockmove'] = json_encode($listofdata);
170
171 //unset($id_sw);
172 //unset($id_tw);
173 unset($id_product);
174 unset($batch);
175 unset($qty);
176 }
177}
178
179if ($action == 'delline' && $idline != '' && $user->hasRight('stock', 'mouvement', 'creer')) {
180 if (!empty($listofdata[$idline])) {
181 unset($listofdata[$idline]);
182 }
183 if (count($listofdata) > 0) {
184 $_SESSION['massstockmove'] = json_encode($listofdata);
185 } else {
186 unset($_SESSION['massstockmove']);
187 }
188}
189
190if ($action == 'createmovements' && $user->hasRight('stock', 'mouvement', 'creer')) {
191 $error = 0;
192
193 if (!GETPOST("label")) {
194 $error++;
195 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MovementLabel")), null, 'errors');
196 }
197
198 $db->begin();
199
200 if (!$error) {
201 $product = new Product($db);
202
203 foreach ($listofdata as $key => $val) { // Loop on each movement to do
204 $id = $val['id'];
205 $id_product = $val['id_product'];
206 $id_sw = $val['id_sw'];
207 $id_tw = $val['id_tw'];
208 $qty = price2num($val['qty']);
209 $batch = $val['batch'];
210 $dlc = -1; // They are loaded later from serial
211 $dluo = -1; // They are loaded later from serial
212
213 if (!$error && $id_sw != $id_tw && is_numeric($qty) && $id_product) {
214 $result = $product->fetch($id_product);
215
216 $product->load_stock('novirtual'); // Load array product->stock_warehouse
217
218 // Define value of products moved
219 $pricesrc = 0;
220 if (!empty($product->pmp)) {
221 $pricesrc = (float) $product->pmp;
222 }
223 $pricedest = $pricesrc;
224
225 //print 'price src='.$pricesrc.', price dest='.$pricedest;exit;
226
227 if (!isModEnabled('productbatch') || !$product->hasbatch()) { // If product does not need lot/serial
228 // Remove stock if source warehouse defined
229 if ($id_sw > 0) {
230 $result1 = $product->correct_stock(
231 $user,
232 $id_sw,
233 (float) $qty,
234 1,
235 GETPOST("label"),
236 $pricesrc,
237 GETPOST("codemove")
238 );
239 if ($result1 < 0) {
240 $error++;
241 setEventMessages($product->error, $product->errors, 'errors');
242 }
243 }
244
245 // Add stock
246 $result2 = $product->correct_stock(
247 $user,
248 $id_tw,
249 (float) $qty,
250 0,
251 GETPOST("label"),
252 $pricedest,
253 GETPOST("codemove")
254 );
255 if ($result2 < 0) {
256 $error++;
257 setEventMessages($product->error, $product->errors, 'errors');
258 }
259 } else {
260 $arraybatchinfo = $product->loadBatchInfo($batch);
261 if (count($arraybatchinfo) > 0) {
262 $firstrecord = array_shift($arraybatchinfo);
263 $dlc = $firstrecord['eatby'];
264 $dluo = $firstrecord['sellby'];
265 } else {
266 $dlc = '';
267 $dluo = '';
268 }
269
270 // Remove stock
271 if ($id_sw > 0) {
272 $result1 = $product->correct_stock_batch(
273 $user,
274 $id_sw,
275 (float) $qty,
276 1,
277 GETPOST("label"),
278 $pricesrc,
279 $dlc,
280 $dluo,
281 $batch,
282 GETPOST("codemove")
283 );
284 if ($result1 < 0) {
285 $error++;
286 setEventMessages($product->error, $product->errors, 'errors');
287 }
288 }
289
290 // Add stock
291 $result2 = $product->correct_stock_batch(
292 $user,
293 $id_tw,
294 (float) $qty,
295 0,
296 GETPOST("label"),
297 $pricedest,
298 $dlc,
299 $dluo,
300 $batch,
301 GETPOST("codemove")
302 );
303 if ($result2 < 0) {
304 $error++;
305 setEventMessages($product->error, $product->errors, 'errors');
306 }
307 }
308 } else {
309 // dol_print_error(null,"Bad value saved into sessions");
310 $error++;
311 }
312 }
313 }
314
315 if (!$error) {
316 unset($_SESSION['massstockmove']);
317
318 $db->commit();
319 setEventMessages($langs->trans("StockMovementRecorded"), null, 'mesgs');
320 header("Location: ".DOL_URL_ROOT.'/product/stock/list.php'); // Redirect to avoid pb when using back
321 exit;
322 } else {
323 $db->rollback();
324 setEventMessages($langs->trans("Error"), null, 'errors');
325 }
326}
327
328if ($action == 'importCSV' && $user->hasRight('stock', 'mouvement', 'creer')) {
329 dol_mkdir($conf->stock->dir_temp);
330 $nowyearmonth = dol_print_date(dol_now(), '%Y%m%d%H%M%S');
331
332 $fullpath = $conf->stock->dir_temp."/".$user->id.'-csvfiletotimport.csv';
333 $resultupload = dol_move_uploaded_file($_FILES['userfile']['tmp_name'], $fullpath, 1);
334 if (is_numeric($resultupload) && $resultupload > 0) {
335 dol_syslog("File ".$fullpath." was added for import");
336 } else {
337 $error++;
338 $langs->load("errors");
339 if ($resultupload === 'ErrorDirNotWritable') {
340 setEventMessages($langs->trans("ErrorFailedToSaveFile").' - '.$langs->trans($resultupload, $fullpath), null, 'errors');
341 } else {
342 setEventMessages($langs->trans("ErrorFailedToSaveFile"), null, 'errors');
343 }
344 }
345
346 if (!$error) {
347 $importcsv = new ImportCsv($db, 'massstocklist');
348 //print $importcsv->separator;
349
350 $nblinesrecord = $importcsv->import_get_nb_of_lines($fullpath) - 1;
351 $importcsv->import_open_file($fullpath);
352 $labelsrecord = $importcsv->import_read_record();
353
354 if ($nblinesrecord < 1) {
355 $langs->load("errors");
356 setEventMessages($langs->trans("ErrorBadNumberOfLinesMustHaveAtLeastOneLinePlusTitle"), null, 'errors');
357 } else {
358 $i = 0;
359 $data = array();
360 $productstatic = new Product($db);
361 $warehousestatics = new Entrepot($db);
362 $warehousestatict = new Entrepot($db);
363
364 // Loop on each line in CSV file
365 while (($i < $nblinesrecord) && !$error) {
366 $newrecord = $importcsv->import_read_record();
367
368 $data[$i] = $newrecord;
369 if (count($data[$i]) == 1) {
370 // Only 1 empty line
371 unset($data[$i]);
372 $i++;
373 continue;
374 }
375
376 $tmp_id_sw = $data[$i][0]['val'];
377 $tmp_id_tw = $data[$i][1]['val'];
378 $tmp_id_product = $data[$i][2]['val'];
379 $tmp_qty = $data[$i][3]['val'];
380 $tmp_batch = $data[$i][4]['val'];
381
382 $errorforproduct = 0;
383 $isidorref = 'ref';
384 if (!is_numeric($tmp_id_product) && $tmp_id_product != '' && preg_match('/^id:/i', $tmp_id_product)) {
385 $isidorref = 'id';
386 }
387 $tmp_id_product = preg_replace('/^(id|ref):/i', '', $tmp_id_product);
388
389 if ($isidorref === 'ref') {
390 $tmp_id_product = preg_replace('/^ref:/', '', $tmp_id_product);
391 $result = fetchref($productstatic, $tmp_id_product);
392 if ($result === -2) {
393 $error++;
394 $errorforproduct = 1;
395 $langs->load("errors");
396 setEventMessages($langs->trans("ErrorMultipleRecordFoundFromRef", $tmp_id_product), null, 'errors');
397 } elseif ($result <= 0) {
398 $error++;
399 $errorforproduct = 1;
400 $langs->load("errors");
401 setEventMessages($langs->trans("ErrorRefNotFound", $tmp_id_product), null, 'errors');
402 }
403 $tmp_id_product = $result;
404 }
405 $data[$i][2]['val'] = $tmp_id_product;
406 if (!$errorforproduct && !($tmp_id_product > 0)) {
407 $error++;
408 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors');
409 }
410
411 if ($tmp_id_sw !== '') {
412 // For source, we allow empty value
413 $errorforwarehouses = 0;
414 $isidorref = 'ref';
415 if (!is_numeric($tmp_id_sw) && $tmp_id_sw != '' && preg_match('/^id:/i', $tmp_id_sw)) {
416 $isidorref = 'id';
417 }
418 $tmp_id_sw = preg_replace('/^(id|ref):/i', '', $tmp_id_sw);
419 if ($isidorref === 'ref') {
420 $tmp_id_sw = preg_replace('/^ref:/', '', $tmp_id_sw);
421 $result = fetchref($warehousestatics, $tmp_id_sw);
422 if ($result === -2) {
423 $error++;
424 $errorforwarehouses = 1;
425 $langs->load("errors");
426 setEventMessages($langs->trans("ErrorMultipleRecordFoundFromRef", $tmp_id_sw), null, 'errors');
427 } elseif ($result <= 0) {
428 $error++;
429 $errorforwarehouses = 1;
430 $langs->load("errors");
431 setEventMessages($langs->trans("ErrorRefNotFound", $tmp_id_sw), null, 'errors');
432 }
433 $tmp_id_sw = $result;
434 }
435 $data[$i][0]['val'] = $tmp_id_sw;
436 if (!$errorforwarehouses && !($tmp_id_sw > 0)) {
437 $error++;
438 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseSource")), null, 'errors');
439 }
440 }
441
442 $errorforwarehouset = 0;
443 $isidorref = 'ref';
444 if (!is_numeric($tmp_id_tw) && $tmp_id_tw != '' && preg_match('/^id:/i', $tmp_id_tw)) {
445 $isidorref = 'id';
446 }
447 $tmp_id_tw = preg_replace('/^(id|ref):/i', '', $tmp_id_tw);
448 if ($isidorref === 'ref') {
449 $tmp_id_tw = preg_replace('/^ref:/', '', $tmp_id_tw);
450 $result = fetchref($warehousestatict, $tmp_id_tw);
451 if ($result === -2) {
452 $error++;
453 $errorforwarehouset = 1;
454 $langs->load("errors");
455 setEventMessages($langs->trans("ErrorMultipleRecordFoundFromRef", $tmp_id_tw), null, 'errors');
456 } elseif ($result <= 0) {
457 $error++;
458 $errorforwarehouset = 1;
459 $langs->load("errors");
460 setEventMessages($langs->trans("ErrorRefNotFound", $tmp_id_tw), null, 'errors');
461 }
462 $tmp_id_tw = $result;
463 }
464 $data[$i][1]['val'] = $tmp_id_tw;
465 if (!$errorforwarehouset && !($tmp_id_tw > 0)) {
466 $error++;
467 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseTarget")), null, 'errors');
468 }
469
470 // If a source is provided and same than target
471 if ($tmp_id_sw > 0 && $tmp_id_tw == $tmp_id_sw) {
472 $error++;
473 $langs->load("errors");
474 setEventMessages($langs->trans("ErrorWarehouseMustDiffers"), null, 'errors');
475 }
476 if (!$tmp_qty) {
477 $error++;
478 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Qty")), null, 'errors');
479 }
480 if (!is_numeric($tmp_qty)) {
481 $error++;
482 setEventMessages('Qty need to be numeric value only', null, 'errors');
483 }
484
485 // Check a batch number is provided if product need it
486 if (!$error) {
487 $producttmp = new Product($db);
488 $producttmp->fetch($tmp_id_product);
489 if ($producttmp->hasbatch()) {
490 if (empty($tmp_batch)) {
491 $error++;
492 $langs->load("errors");
493 setEventMessages($langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $producttmp->ref), null, 'errors');
494 }
495 }
496 }
497
498 $i++;
499 }
500
501 if (!$error) {
502 foreach ($data as $key => $value) {
503 if (count(array_keys($listofdata)) > 0) {
504 $id = max(array_keys($listofdata)) + 1;
505 } else {
506 $id = 1;
507 }
508 $tmp_id_sw = $data[$key][0]['val'];
509 $tmp_id_tw = $data[$key][1]['val'];
510 $tmp_id_product = $data[$key][2]['val'];
511 $tmp_qty = $data[$key][3]['val'];
512 $tmp_batch = $data[$key][4]['val'];
513 $listofdata[$key] = array('id' => $key, 'id_sw' => $tmp_id_sw, 'id_tw' => $tmp_id_tw, 'id_product' => $tmp_id_product, 'qty' => $tmp_qty, 'batch' => $tmp_batch);
514 }
515 }
516 }
517 }
518 if ($error) {
519 $listofdata = array();
520 }
521
522 $_SESSION['massstockmove'] = json_encode($listofdata);
523}
524
525if ($action == 'confirm_deletefile' && $confirm == 'yes' && $permissiontodelete) {
526 $langs->load("other");
527
528 $file = $conf->stock->dir_temp.'/'.GETPOST('urlfile');
529 $ret = dol_delete_file($file, 1);
530 if ($ret) {
531 setEventMessages($langs->trans("FileWasRemoved", GETPOST('urlfile')), null, 'mesgs');
532 } else {
533 setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), null, 'errors');
534 }
535 header('Location: '.$_SERVER["PHP_SELF"]);
536 exit;
537}
538
539
540/*
541 * View
542 */
543
544$now = dol_now();
545$error = 0;
546
547$form = new Form($db);
548$formproduct = new FormProduct($db);
549$productstatic = new Product($db);
550$warehousestatics = new Entrepot($db);
551$warehousestatict = new Entrepot($db);
552
553$help_url = 'EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks|DE:Modul_Bestände';
554
555$title = $langs->trans('MassMovement');
556
557llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-product page-stock_massstomove');
558
559print load_fiche_titre($langs->trans("MassStockTransferShort"), '', 'stock');
560
561$titletoadd = $langs->trans("Select");
562$buttonrecord = $langs->trans("RecordMovements");
563$titletoaddnoent = $langs->transnoentitiesnoconv("Select");
564$buttonrecordnoent = $langs->transnoentitiesnoconv("RecordMovements");
565print '<span class="opacitymedium">'.$langs->trans("SelectProductInAndOutWareHouse", $titletoaddnoent, $buttonrecordnoent).'</span>';
566
567print '<br>';
568//print '<br>';
569
570// Form to upload a file
571print '<form name="userfile" action="'.$_SERVER["PHP_SELF"].'" enctype="multipart/form-data" method="POST">';
572print '<input type="hidden" name="token" value="'.newToken().'">';
573print '<input type="hidden" name="action" value="importCSV">';
574if (!empty($conf->dol_optimize_smallscreen)) {
575 print '<br>';
576}
577print '<span class="opacitymedium">';
578print $langs->trans("or");
579print ' ';
580$importcsv = new ImportCsv($db, 'massstocklist');
581print $form->textwithpicto($langs->trans('SelectAStockMovementFileToImport'), $langs->transnoentitiesnoconv("InfoTemplateImport", $importcsv->separator));
582print '</span>';
583
584$maxfilesizearray = getMaxFileSizeArray();
585$maxmin = $maxfilesizearray['maxmin'];
586if ($maxmin > 0) {
587 print '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
588}
589print '<input type="file" name="userfile" size="20" maxlength="80"> &nbsp; &nbsp; ';
590$out = (!getDolGlobalString('MAIN_UPLOAD_DOC') ? ' disabled' : '');
591print '<input type="submit" class="button small smallpaddingimp" value="'.$langs->trans("ImportFromCSV").'"'.$out.' name="sendit">';
592$out = '';
593if (getDolGlobalString('MAIN_UPLOAD_DOC')) {
594 $max = getDolGlobalString('MAIN_UPLOAD_DOC'); // In Kb
595 $maxphp = @ini_get('upload_max_filesize'); // In unknown
596 if (preg_match('/k$/i', $maxphp)) {
597 $maxphp = preg_replace('/k$/i', '', $maxphp);
598 $maxphp = (int) $maxphp * 1;
599 }
600 if (preg_match('/m$/i', $maxphp)) {
601 $maxphp = preg_replace('/m$/i', '', $maxphp);
602 $maxphp = (int) $maxphp * 1024;
603 }
604 if (preg_match('/g$/i', $maxphp)) {
605 $maxphp = preg_replace('/g$/i', '', $maxphp);
606 $maxphp = (int) $maxphp * 1024 * 1024;
607 }
608 if (preg_match('/t$/i', $maxphp)) {
609 $maxphp = preg_replace('/t$/i', '', $maxphp);
610 $maxphp = (int) $maxphp * 1024 * 1024 * 1024;
611 }
612 $maxphp2 = @ini_get('post_max_size'); // In unknown
613 if (preg_match('/k$/i', $maxphp2)) {
614 $maxphp2 = preg_replace('/k$/i', '', $maxphp2);
615 $maxphp2 = (int) $maxphp2 * 1;
616 }
617 if (preg_match('/m$/i', $maxphp2)) {
618 $maxphp2 = preg_replace('/m$/i', '', $maxphp2);
619 $maxphp2 = (int) $maxphp2 * 1024;
620 }
621 if (preg_match('/g$/i', $maxphp2)) {
622 $maxphp2 = preg_replace('/g$/i', '', $maxphp2);
623 $maxphp2 = (int) $maxphp2 * 1024 * 1024;
624 }
625 if (preg_match('/t$/i', $maxphp2)) {
626 $maxphp2 = preg_replace('/t$/i', '', $maxphp2);
627 $maxphp2 = (int) $maxphp2 * 1024 * 1024 * 1024;
628 }
629 // Now $max and $maxphp and $maxphp2 are in Kb
630 $maxmin = $max;
631 $maxphptoshow = $maxphptoshowparam = '';
632 if ($maxphp > 0) {
633 $maxmin = min($max, $maxphp);
634 $maxphptoshow = $maxphp;
635 $maxphptoshowparam = 'upload_max_filesize';
636 }
637 if ($maxphp2 > 0) {
638 $maxmin = min($max, $maxphp2);
639 if ($maxphp2 < $maxphp) {
640 $maxphptoshow = $maxphp2;
641 $maxphptoshowparam = 'post_max_size';
642 }
643 }
644
645 $langs->load('other');
646 $out .= ' ';
647 $out .= info_admin($langs->trans("ThisLimitIsDefinedInSetup", $max, $maxphptoshow), 1);
648} else {
649 $out .= ' ('.$langs->trans("UploadDisabled").')';
650}
651print $out;
652
653print '</form>';
654
655print '<br><br>';
656
657// Form to add a line
658print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formulaire">';
659print '<input type="hidden" name="token" value="'.newToken().'">';
660print '<input type="hidden" name="action" value="addline">';
661
662
663print '<div class="div-table-responsive-no-min">';
664print '<table class="liste noborder centpercent">';
665
666$param = '';
667
668print '<tr class="liste_titre">';
669print getTitleFieldOfList($langs->trans('WarehouseSource'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone ');
670print getTitleFieldOfList($langs->trans('WarehouseTarget'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone ');
671print getTitleFieldOfList($langs->trans('Product'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone ');
672if (isModEnabled('productbatch')) {
673 print getTitleFieldOfList($langs->trans('Batch'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone ');
674}
675print getTitleFieldOfList($langs->trans('Qty'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right tagtd maxwidthonsmartphone ');
676print getTitleFieldOfList('', 0);
677print '</tr>';
678
679print '<tr class="oddeven">';
680// From warehouse
681print '<td class="nowraponall">';
682print img_picto($langs->trans("WarehouseSource"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses(is_null($id_sw) ? '' : $id_sw, 'id_sw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200');
683print '</td>';
684// To warehouse
685print '<td class="nowraponall">';
686print img_picto($langs->trans("WarehouseTarget"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses(is_null($id_sw) ? '' : $id_tw, 'id_tw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200');
687print '</td>';
688// Product
689print '<td class="nowraponall">';
690$filtertype = 0;
691if (getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
692 $filtertype = '';
693}
694if (getDolGlobalInt('PRODUIT_LIMIT_SIZE') <= 0) {
695 $limit = 0;
696} else {
697 $limit = getDolGlobalInt('PRODUIT_LIMIT_SIZE');
698}
699print img_picto($langs->trans("Product"), 'product', 'class="paddingright"');
700print $form->select_produits((isset($id_product) ? $id_product : 0), 'productid', $filtertype, $limit, 0, -1, 2, '', 1, array(), 0, '1', 0, 'minwidth200imp maxwidth300', 1, '', null, 1);
701print '</td>';
702// Batch number
703if (isModEnabled('productbatch')) {
704 print '<td class="nowraponall">';
705 print img_picto($langs->trans("LotSerial"), 'lot', 'class="paddingright"');
706 print '<input type="text" name="batch" class="flat maxwidth75" value="'.dol_escape_htmltag((isset($batch) ? $batch : '')).'">';
707 print '</td>';
708}
709// Qty
710print '<td class="right"><input type="text" class="flat maxwidth50 right" name="qty" value="'.price2num((float) (isset($qty) ? $qty : 0), 'MS').'"></td>';
711// Button to add line
712print '<td class="right"><input type="submit" class="button" name="addline" value="'.dol_escape_htmltag($titletoadd).'"></td>';
713
714print '</tr>';
715
716foreach ($listofdata as $key => $val) {
717 $productstatic->id = 0;
718 $productstatic->fetch($val['id_product']);
719
720 $warehousestatics->id = 0;
721 if ($val['id_sw'] > 0) {
722 $warehousestatics->fetch($val['id_sw']);
723 }
724 $warehousestatict->id = 0;
725 if ($val['id_tw'] > 0) {
726 $warehousestatict->fetch($val['id_tw']);
727 }
728
729 if ($productstatic->id <= 0) {
730 $error++;
731 setEventMessages($langs->trans("ObjectNotFound", $langs->transnoentitiesnoconv("Product").' (id='.$val['id_product'].')'), null, 'errors');
732 }
733 if ($warehousestatics->id < 0) { // We accept 0 for source warehouse id
734 $error++;
735 setEventMessages($langs->trans("ObjectNotFound", $langs->transnoentitiesnoconv("WarehouseSource").' (id='.$val['id_sw'].')'), null, 'errors');
736 }
737 if ($warehousestatict->id <= 0) {
738 $error++;
739 setEventMessages($langs->trans("ObjectNotFound", $langs->transnoentitiesnoconv("WarehouseTarget").' (id='.$val['id_tw'].')'), null, 'errors');
740 }
741
742 if (!$error) {
743 print '<tr class="oddeven">';
744 print '<td>';
745 if ($warehousestatics->id > 0) {
746 print $warehousestatics->getNomUrl(1);
747 } else {
748 print '<span class="opacitymedium">';
749 print $langs->trans("None");
750 print '</span>';
751 }
752 print '</td>';
753 print '<td>';
754 print $warehousestatict->getNomUrl(1);
755 print '</td>';
756 print '<td>';
757 print $productstatic->getNomUrl(1).' - '.dol_escape_htmltag($productstatic->label);
758 print '</td>';
759 if (isModEnabled('productbatch')) {
760 print '<td>';
761 print dol_escape_htmltag($val['batch']);
762 print '</td>';
763 }
764 print '<td class="right">'.price2num((float) $val['qty'], 'MS').'</td>';
765 print '<td class="right"><a href="'.$_SERVER["PHP_SELF"].'?action=delline&token='.newToken().'&idline='.$val['id'].'">'.img_delete($langs->trans("Remove")).'</a></td>';
766 print '</tr>';
767 }
768}
769
770print '</table>';
771print '</div>';
772
773print '</form>';
774
775print '<br>';
776
777// Form to validate all movements
778if (count($listofdata)) {
779 print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formulaire2" class="formconsumeproduce">';
780 print '<input type="hidden" name="token" value="'.newToken().'">';
781 print '<input type="hidden" name="action" value="createmovements">';
782
783 // Button to record mass movement
784 $codemove = (GETPOSTISSET("codemove") ? GETPOST("codemove", 'alpha') : getDolGlobalString('STOCK_MASSSTOCKMOVE_CODE_PREFIX', 'MSM-').dol_print_date(dol_now(), '%Y%m%d%H%M%S'));
785 $labelmovement = GETPOST("label") ? GETPOST('label') : $langs->trans("MassStockTransferShort").' '.dol_print_date($now, '%Y-%m-%d %H:%M');
786
787 print '<div class="center">';
788 print '<span class="fieldrequired">'.$langs->trans("InventoryCode").':</span> ';
789 print '<input type="text" name="codemove" class="maxwidth300" value="'.dol_escape_htmltag($codemove).'"> &nbsp; ';
790 print '<span class="clearbothonsmartphone"></span>';
791 print $langs->trans("MovementLabel").': ';
792 print '<input type="text" name="label" class="minwidth300" value="'.dol_escape_htmltag($labelmovement).'"><br>';
793 print '<br>';
794
795 print '<div class="center"><input type="submit" class="button" name="valid" value="'.dol_escape_htmltag($buttonrecord).'"></div>';
796
797 print '<br>';
798 print '</div>';
799
800 print '</form>';
801}
802
803if ($action == 'delete') {
804 print $form->formconfirm($_SERVER["PHP_SELF"].'?urlfile='.urlencode(GETPOST('urlfile')).'&step=3'.$param, $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', 0, 1);
805}
806
807// End of page
808llxFooter();
809$db->close();
810
811
819function startsWith($haystack, $needle)
820{
821 $length = strlen($needle);
822 return substr($haystack, 0, $length) === $needle;
823}
824
832function fetchref($static_object, $tmp_ref)
833{
834 if (startsWith($tmp_ref, 'ref:')) {
835 $tmp_ref = str_replace('ref:', '', $tmp_ref);
836 }
837 $static_object->id = 0;
838 $static_object->fetch(0, $tmp_ref);
839 return $static_object->id;
840}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage warehouses.
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to import CSV files.
Class to manage products or services.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $nohook=0, $keyforsourcefile='addedfile', $upload_dir='', $mode=0)
Check validity of a file upload from an GUI page, and move it to its final destination.
dol_now($mode='gmt')
Return date for now.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
Definition html.lib.php:172
startsWith($haystack, $needle)
Verify if $haystack startswith $needle.
fetchref($static_object, $tmp_ref)
Fetch object with ref.
getMaxFileSizeArray()
Return the max allowed for file upload.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.