From bd092cc84d58e5d6eb63129c7492e158ed1f7456 Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Wed, 31 Mar 2021 12:04:37 +0200 Subject: [PATCH 1/2] Close #16761 : new mass stock transfer --- htdocs/langs/en_US/stocks.lang | 4 +- htdocs/product/stock/massstockmove.php | 237 ++++++++++++++++++++++++- 2 files changed, 239 insertions(+), 2 deletions(-) diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 8e949661c49..586595baa94 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -242,4 +242,6 @@ InventoryRealQtyHelp=Set value to 0 to reset qty
Keep field empty, or remove UpdateByScaning=Update by scaning UpdateByScaningProductBarcode=Update by scan (product barcode) UpdateByScaningLot=Update by scan (lot|serial barcode) -DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement. \ No newline at end of file +DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement. +ImportFromCSV=Import CSV list of movement +ChooseFileToImport=Upload file then click on the %s icon to select file as source import file... \ No newline at end of file diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index 5a5808b04f3..39962e1bd49 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -30,6 +30,12 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/modules/import/import_csv.modules.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/import.lib.php'; + +$confirm = GETPOST('confirm', 'alpha'); +$filetoimport = GETPOST('filetoimport'); // Load translation files required by the page $langs->loadLangs(array('products', 'stocks', 'orders', 'productbatch')); @@ -80,6 +86,20 @@ if (!empty($_SESSION['massstockmove'])) { * Actions */ + +if (GETPOST('sendit') && !empty($conf->global->MAIN_UPLOAD_DOC)) { + dol_mkdir($conf->stock->dir_temp); + $nowyearmonth = dol_print_date(dol_now(), '%Y%m%d%H%M%S'); + + $fullpath = $conf->stock->dir_temp."/".$nowyearmonth.'-'.$_FILES['userfile']['name']; + if (dol_move_uploaded_file($_FILES['userfile']['tmp_name'], $fullpath, 1) > 0) { + dol_syslog("File ".$fullpath." was added for import"); + } else { + $langs->load("errors"); + setEventMessages($langs->trans("ErrorFailedToSaveFile"), null, 'errors'); + } +} + if ($action == 'addline') { if (!($id_product > 0)) { $error++; @@ -288,8 +308,99 @@ if ($action == 'createmovements') { } } +if ($action == 'importCSV') { + $importcsv = new ImportCsv($db, 'massstocklist'); + $dir = $conf->stock->dir_temp; + $fullpath = $dir.'/'.$filetoimport; + $nblinesrecord = $importcsv->import_get_nb_of_lines($fullpath)-1; + $importcsv->import_open_file($fullpath); + $labelsrecord = $importcsv->import_read_record(); + $i=0; + $data = array(); + while ($i < $nblinesrecord) { + $data[] = $importcsv->import_read_record(); + $id_product = $data[$i][0]['val']; + $qty = $data[$i][1]['val']; + $id_sw = $data[$i][2]['val']; + $id_tw = $data[$i][3]['val']; + $batch = $data[$i][4]['val']; + if (!($id_product > 0)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors'); + } + if (!($id_sw > 0)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseSource")), null, 'errors'); + } + if (!($id_tw > 0)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseTarget")), null, 'errors'); + } + if ($id_sw > 0 && $id_tw == $id_sw) { + $error++; + $langs->load("errors"); + setEventMessages($langs->trans("ErrorWarehouseMustDiffers"), null, 'errors'); + } + if (!$qty) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Qty")), null, 'errors'); + } + // Check a batch number is provided if product need it + if (!$error) { + $producttmp = new Product($db); + $producttmp->fetch($id_product); + if ($producttmp->hasbatch()) { + if (empty($batch)) { + $error++; + $langs->load("errors"); + setEventMessages($langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $producttmp->ref), null, 'errors'); + } + } + } + + $i++; + } + if (!$error) { + foreach ($data as $key => $value) { + if (count(array_keys($listofdata)) > 0) { + $id = max(array_keys($listofdata)) + 1; + } else { + $id = 1; + } + $id_product = $data[$key][0]['val']; + $qty = $data[$key][1]['val']; + $id_sw = $data[$key][2]['val']; + $id_tw = $data[$key][3]['val']; + $batch = $data[$key][4]['val']; + $listofdata[$key] = array('id'=>$key, 'id_product'=>$id_product, 'qty'=>$qty, 'id_sw'=>$id_sw, 'id_tw'=>$id_tw, 'batch'=>$batch); + } + } + $_SESSION['massstockmove'] = json_encode($listofdata); +} + +if ($action == 'confirm_deletefile' && $confirm == 'yes') { + $langs->load("other"); + + $param = '&datatoimport='.urlencode($datatoimport).'&format='.urlencode($format); + if ($excludefirstline) { + $param .= '&excludefirstline='.urlencode($excludefirstline); + } + if ($endatlinenb) { + $param .= '&endatlinenb='.urlencode($endatlinenb); + } + + $file = $conf->stock->dir_temp.'/'.GETPOST('urlfile'); // Do not use urldecode here ($_GET and $_REQUEST are already decoded by PHP). + $ret = dol_delete_file($file); + if ($ret) { + setEventMessages($langs->trans("FileWasRemoved", GETPOST('urlfile')), null, 'mesgs'); + } else { + setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), null, 'errors'); + } + Header('Location: '.$_SERVER["PHP_SELF"]); + exit; +} /* * View */ @@ -411,6 +522,128 @@ print ''; print ''; +print '
'; + +print '
'; +print ''; +print ''; +print ''; +$s = $langs->trans("ChooseFileToImport", '{s1}'); +$s = str_replace('{s1}', img_picto('', 'next'), $s); +print $s; +print '

'; + +print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table +print ''; + +print ''; +print "\n"; + +// Search available imports +$filearray = dol_dir_list($conf->stock->dir_temp, 'files', 0, '', '', 'name', SORT_DESC); +if (count($filearray) > 0) { + $dir = $conf->stock->dir_temp; + + // Search available files to import + $i = 0; + foreach ($filearray as $key => $val) { + $file = $val['name']; + + // readdir return value in ISO and we want UTF8 in memory + if (!utf8_check($file)) { + $file = utf8_encode($file); + } + + if (preg_match('/^\./', $file)) { + continue; + } + + $modulepart = 'import'; + $urlsource = $_SERVER["PHP_SELF"].'&filetoimport='.urlencode($filetoimport); + $relativepath = $file; + + print ''; + print ''; + print ''; + // Affiche taille fichier + print ''; + // Affiche date fichier + print ''; + // Del button + print ''; + // Action button + print ''; + print ''; + } +} + +print '
'; +print '     '; +$out = (empty($conf->global->MAIN_UPLOAD_DOC) ? ' disabled' : ''); +print ''; +$out = ''; +if (!empty($conf->global->MAIN_UPLOAD_DOC)) { + $max = $conf->global->MAIN_UPLOAD_DOC; // In Kb + $maxphp = @ini_get('upload_max_filesize'); // In unknown + if (preg_match('/k$/i', $maxphp)) { + $maxphp = $maxphp * 1; + } + if (preg_match('/m$/i', $maxphp)) { + $maxphp = $maxphp * 1024; + } + if (preg_match('/g$/i', $maxphp)) { + $maxphp = $maxphp * 1024 * 1024; + } + if (preg_match('/t$/i', $maxphp)) { + $maxphp = $maxphp * 1024 * 1024 * 1024; + } + $maxphp2 = @ini_get('post_max_size'); // In unknown + if (preg_match('/k$/i', $maxphp2)) { + $maxphp2 = $maxphp2 * 1; + } + if (preg_match('/m$/i', $maxphp2)) { + $maxphp2 = $maxphp2 * 1024; + } + if (preg_match('/g$/i', $maxphp2)) { + $maxphp2 = $maxphp2 * 1024 * 1024; + } + if (preg_match('/t$/i', $maxphp2)) { + $maxphp2 = $maxphp2 * 1024 * 1024 * 1024; + } + // Now $max and $maxphp and $maxphp2 are in Kb + $maxmin = $max; + $maxphptoshow = $maxphptoshowparam = ''; + if ($maxphp > 0) { + $maxmin = min($max, $maxphp); + $maxphptoshow = $maxphp; + $maxphptoshowparam = 'upload_max_filesize'; + } + if ($maxphp2 > 0) { + $maxmin = min($max, $maxphp2); + if ($maxphp2 < $maxphp) { + $maxphptoshow = $maxphp2; + $maxphptoshowparam = 'post_max_size'; + } + } + + $langs->load('other'); + $out .= ' '; + $out .= info_admin($langs->trans("ThisLimitIsDefinedInSetup", $max, $maxphptoshow), 1); +} else { + $out .= ' ('.$langs->trans("UploadDisabled").')'; +} +print $out; +print '
'.img_mime($file).''; + print ''; + print $file; + print ''; + print ''.dol_print_size(dol_filesize($dir.'/'.$file)).''.dol_print_date(dol_filemtime($dir.'/'.$file), 'dayhour').''.img_delete().''; + print ''.img_picto($langs->trans("NewImport"), 'next', 'class="fa-15x"').''; + print '
'; +print '
'; + +print '
'; print '
'; @@ -437,7 +670,9 @@ print '
'; print ''; print ''; - +if ($action == 'delete') { + print $form->formconfirm($_SERVER["PHP_SELF"].'?urlfile='.urlencode(GETPOST('urlfile')).'&step=3'.$param, $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', 0, 1); +} // End of page llxFooter(); $db->close(); From 633b8f1431c574e4b06e543ba51fa13dd13ac944 Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Wed, 31 Mar 2021 12:18:31 +0200 Subject: [PATCH 2/2] Close #16761 : Info template import --- htdocs/langs/en_US/stocks.lang | 3 ++- htdocs/product/stock/massstockmove.php | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 586595baa94..e3d3ae4e0ae 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -244,4 +244,5 @@ UpdateByScaningProductBarcode=Update by scan (product barcode) UpdateByScaningLot=Update by scan (lot|serial barcode) DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement. ImportFromCSV=Import CSV list of movement -ChooseFileToImport=Upload file then click on the %s icon to select file as source import file... \ No newline at end of file +ChooseFileToImport=Upload file then click on the %s icon to select file as source import file... +InfoTemplateImport=Uploaded file needs to have this format (* are mandatory fields) : Product_id* | Source_Warehouse_id* | Target_Warehouse_id* | Quantity* | Batch_id \ No newline at end of file diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index 39962e1bd49..a89e234acfb 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -320,9 +320,9 @@ if ($action == 'importCSV') { while ($i < $nblinesrecord) { $data[] = $importcsv->import_read_record(); $id_product = $data[$i][0]['val']; - $qty = $data[$i][1]['val']; - $id_sw = $data[$i][2]['val']; - $id_tw = $data[$i][3]['val']; + $id_sw = $data[$i][1]['val']; + $id_tw = $data[$i][2]['val']; + $qty = $data[$i][3]['val']; $batch = $data[$i][4]['val']; if (!($id_product > 0)) { @@ -370,9 +370,9 @@ if ($action == 'importCSV') { $id = 1; } $id_product = $data[$key][0]['val']; - $qty = $data[$key][1]['val']; - $id_sw = $data[$key][2]['val']; - $id_tw = $data[$key][3]['val']; + $id_sw = $data[$key][1]['val']; + $id_tw = $data[$key][2]['val']; + $qty = $data[$key][3]['val']; $batch = $data[$key][4]['val']; $listofdata[$key] = array('id'=>$key, 'id_product'=>$id_product, 'qty'=>$qty, 'id_sw'=>$id_sw, 'id_tw'=>$id_tw, 'batch'=>$batch); } @@ -531,6 +531,8 @@ print ''; $s = $langs->trans("ChooseFileToImport", '{s1}'); $s = str_replace('{s1}', img_picto('', 'next'), $s); print $s; +print '

'; +print $langs->trans('InfoTemplateImport'); print '


'; print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table