From fafff6d9a474864c9579a049dd2318af4e648a58 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 31 Mar 2021 15:07:18 +0200 Subject: [PATCH] Debug feature to import stock movements from a file. --- htdocs/langs/en_US/stocks.lang | 3 +- htdocs/product/stock/massstockmove.php | 441 ++++++++++++------------- 2 files changed, 205 insertions(+), 239 deletions(-) diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index bb18c960aae..449c1598ec8 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -247,7 +247,8 @@ 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... -InfoTemplateImport=Uploaded file needs to have this format (* are mandatory fields) : Product_id* | Source_Warehouse_id* | Target_Warehouse_id* | Quantity* | Batch_id +OrSelectAStockMovementFileToImport=Or select a stock movement file to import +InfoTemplateImport=Uploaded file needs to have this format (* are mandatory fields):
Source_Warehouse_id* | Target_Warehouse_id* | Product_id* | Quantity* | Lot/serial number
CSV character separator must be %s LabelOfInventoryMovemement=Inventory %s ReOpen=Reopen ConfirmFinish=Confirm closing diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index a89e234acfb..bf1291ac428 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -86,21 +86,7 @@ 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 ($action == 'addline' && !empty($user->rights->stock->mouvement->creer)) { if (!($id_product > 0)) { $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors'); @@ -162,7 +148,7 @@ if ($action == 'addline') { } } -if ($action == 'delline' && $idline != '') { +if ($action == 'delline' && $idline != '' && !empty($user->rights->stock->mouvement->creer)) { if (!empty($listofdata[$idline])) { unset($listofdata[$idline]); } @@ -173,7 +159,7 @@ if ($action == 'delline' && $idline != '') { } } -if ($action == 'createmovements') { +if ($action == 'createmovements' && !empty($user->rights->stock->mouvement->creer)) { $error = 0; if (!GETPOST("label")) { @@ -308,75 +294,107 @@ 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']; - $id_sw = $data[$i][1]['val']; - $id_tw = $data[$i][2]['val']; - $qty = $data[$i][3]['val']; - $batch = $data[$i][4]['val']; +if ($action == 'importCSV' && !empty($user->rights->stock->mouvement->creer)) { + dol_mkdir($conf->stock->dir_temp); + $nowyearmonth = dol_print_date(dol_now(), '%Y%m%d%H%M%S'); - 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'); - } + $fullpath = $conf->stock->dir_temp."/".$user->id.'-csvfiletotimport.csv'; + if (dol_move_uploaded_file($_FILES['userfile']['tmp_name'], $fullpath, 1) > 0) { + dol_syslog("File ".$fullpath." was added for import"); + } else { + $error++; + $langs->load("errors"); + setEventMessages($langs->trans("ErrorFailedToSaveFile"), 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)) { + if (!$error) { + $importcsv = new ImportCsv($db, 'massstocklist'); + //print $importcsv->separator; + + $nblinesrecord = $importcsv->import_get_nb_of_lines($fullpath)-1; + $importcsv->import_open_file($fullpath); + $labelsrecord = $importcsv->import_read_record(); + + if ($nblinesrecord <= 1) { + setEventMessages($langs->trans("BadNumberOfLinesMustHaveAtLeastOneLinePlusTitle"), null, 'errors'); + } else { + $i=0; + $data = array(); + while (($i < $nblinesrecord) && !$error) { + $data[] = $importcsv->import_read_record(); + if (count($data[$i]) == 1) { + // Only 1 empty line + unset($data); + $i++; + continue; + } + //var_dump($data); + + $id_sw = $data[$i][0]['val']; + $id_tw = $data[$i][1]['val']; + $id_product = $data[$i][2]['val']; + $qty = $data[$i][3]['val']; + $batch = $data[$i][4]['val']; + + // TODO If product is a ref (not numeric or starts with "ref:..."), retreive the id of product from the ref + if (!($id_product > 0)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors'); + } + // TODO If warehouse is a ref (not numeric or starts with "ref:..."), retreive the id of product from the ref + if (!($id_sw > 0)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WarehouseSource")), null, 'errors'); + } + // TODO If warehouse is a ref (not numeric or starts with "ref:..."), retreive the id of product from the ref + 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("ErrorTryToMakeMoveOnProductRequiringBatchData", $producttmp->ref), null, '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']; + $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); } } } + } - $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']; - $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); - } - } $_SESSION['massstockmove'] = json_encode($listofdata); } @@ -401,6 +419,8 @@ if ($action == 'confirm_deletefile' && $confirm == 'yes') { Header('Location: '.$_SERVER["PHP_SELF"]); exit; } + + /* * View */ @@ -426,119 +446,18 @@ $buttonrecord = $langs->trans("RecordMovement"); $titletoaddnoent = $langs->transnoentitiesnoconv("Select"); $buttonrecordnoent = $langs->transnoentitiesnoconv("RecordMovement"); print ''.$langs->trans("SelectProductInAndOutWareHouse", $titletoaddnoent, $buttonrecordnoent).'
'; -print '
'."\n"; - -// Form to add a line -print '
'; -print ''; -print ''; - - -print '
'; -print ''; -//print '
'; - -$param = ''; - -print '
'; -print getTitleFieldOfList($langs->trans('WarehouseSource'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); -print getTitleFieldOfList($langs->trans('WarehouseTarget'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); -print getTitleFieldOfList($langs->trans('ProductRef'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); -if ($conf->productbatch->enabled) { - print getTitleFieldOfList($langs->trans('Batch'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); -} -print getTitleFieldOfList($langs->trans('Qty'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'center tagtd maxwidthonsmartphone '); -print getTitleFieldOfList('', 0); -print ''; - - -print ''; -// From warehouse -print ''; -// To warehouse -print ''; -// Product -print ''; -// Batch number -if ($conf->productbatch->enabled) { - print ''; -} -// Qty -print ''; -// Button to add line -print ''; - -print ''; - - -foreach ($listofdata as $key => $val) { - $productstatic->fetch($val['id_product']); - $warehousestatics->fetch($val['id_sw']); - $warehousestatict->fetch($val['id_tw']); - - print ''; - print ''; - print ''; - print ''; - if ($conf->productbatch->enabled) { - print ''; - } - print ''; - print ''; - - print ''; -} - -print '
'; -print img_picto($langs->trans("WarehouseSource"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses($id_sw, 'id_sw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200'); -print ''; -print img_picto($langs->trans("WarehouseTarget"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses($id_tw, 'id_tw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200'); -print ''; -$filtertype = 0; -if (!empty($conf->global->STOCK_SUPPORTS_SERVICES)) { - $filtertype = ''; -} -if ($conf->global->PRODUIT_LIMIT_SIZE <= 0) { - $limit = ''; -} else { - $limit = $conf->global->PRODUIT_LIMIT_SIZE; -} - -print img_picto($langs->trans("Product"), 'product', 'class="paddingright"'); -print $form->select_produits($id_product, 'productid', $filtertype, $limit, 0, -1, 2, '', 1, array(), 0, '1', 0, 'minwidth200imp maxwidth300', 1, '', null, 1); -print ''; - print img_picto($langs->trans("LotSerial"), 'lot', 'class="paddingright"'); - print ''; - print '
'; - print $warehousestatics->getNomUrl(1); - print ''; - print $warehousestatict->getNomUrl(1); - print ''; - print $productstatic->getNomUrl(1).' - '.$productstatic->label; - print ''; - print $val['batch']; - print ''.$val['qty'].''.img_delete($langs->trans("Remove")).'
'; -print '
'; - -print '
'; print '
'; +// Form to upload a file print '
'; print ''; +print ''; print ''; print ''; -$s = $langs->trans("ChooseFileToImport", '{s1}'); -$s = str_replace('{s1}', img_picto('', 'next'), $s); -print $s; -print '

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


'; +print $form->textwithpicto($langs->trans('OrSelectAStockMovementFileToImport'), $langs->transnoentitiesnoconv("InfoTemplateImport", $importcsv->separator)); +print ''; -print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table -print ''; - -print '
'; print '     '; $out = (empty($conf->global->MAIN_UPLOAD_DOC) ? ' disabled' : ''); print ''; @@ -594,52 +513,94 @@ if (!empty($conf->global->MAIN_UPLOAD_DOC)) { $out .= ' ('.$langs->trans("UploadDisabled").')'; } print $out; + +print ''; + +print '

'; + +// Form to add a line +print '
'; +print ''; +print ''; + + +print '
'; +print ''; + +$param = ''; + +print ''; +print getTitleFieldOfList($langs->trans('WarehouseSource'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); +print getTitleFieldOfList($langs->trans('WarehouseTarget'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); +print getTitleFieldOfList($langs->trans('ProductRef'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); +if ($conf->productbatch->enabled) { + print getTitleFieldOfList($langs->trans('Batch'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'tagtd maxwidthonsmartphone '); +} +print getTitleFieldOfList($langs->trans('Qty'), 0, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'center tagtd maxwidthonsmartphone '); +print getTitleFieldOfList('', 0); +print ''; + +print ''; +// From warehouse +print ''; -print "\n"; +// To warehouse +print ''; +// Product +print ''; +// Batch number +if ($conf->productbatch->enabled) { + print ''; +} +// Qty +print ''; +// Button to add line +print ''; - // Search available files to import - $i = 0; - foreach ($filearray as $key => $val) { - $file = $val['name']; +print ''; - // readdir return value in ISO and we want UTF8 in memory - if (!utf8_check($file)) { - $file = utf8_encode($file); - } +foreach ($listofdata as $key => $val) { + $productstatic->fetch($val['id_product']); + $warehousestatics->fetch($val['id_sw']); + $warehousestatict->fetch($val['id_tw']); - if (preg_match('/^\./', $file)) { - continue; - } - - $modulepart = 'import'; - $urlsource = $_SERVER["PHP_SELF"].'&filetoimport='.urlencode($filetoimport); - $relativepath = $file; - - print ''; - print ''; + print ''; + print ''; + print ''; + print ''; + if ($conf->productbatch->enabled) { print ''; - // Affiche taille fichier - print ''; - // Affiche date fichier - print ''; - // Del button - print ''; - // Action button - print ''; - print ''; } + print ''; + print ''; + print ''; } print '
'; +print img_picto($langs->trans("WarehouseSource"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses($id_sw, 'id_sw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200'); print '
'; +print img_picto($langs->trans("WarehouseTarget"), 'stock', 'class="paddingright"').$formproduct->selectWarehouses($id_tw, 'id_tw', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, array(), 'minwidth200imp maxwidth200'); +print ''; +$filtertype = 0; +if (!empty($conf->global->STOCK_SUPPORTS_SERVICES)) { + $filtertype = ''; +} +if ($conf->global->PRODUIT_LIMIT_SIZE <= 0) { + $limit = ''; +} else { + $limit = $conf->global->PRODUIT_LIMIT_SIZE; +} -// 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; +print img_picto($langs->trans("Product"), 'product', 'class="paddingright"'); +print $form->select_produits($id_product, 'productid', $filtertype, $limit, 0, -1, 2, '', 1, array(), 0, '1', 0, 'minwidth200imp maxwidth300', 1, '', null, 1); +print ''; + print img_picto($langs->trans("LotSerial"), 'lot', 'class="paddingright"'); + print ''; + print '
'.img_mime($file).'
'; + print $warehousestatics->getNomUrl(1); + print ''; + print $warehousestatict->getNomUrl(1); + print ''; + print $productstatic->getNomUrl(1).' - '.$productstatic->label; + print ''; - print ''; - print $file; - print ''; + print $val['batch']; 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 '
'.$val['qty'].''.img_delete($langs->trans("Remove")).'
'; @@ -649,32 +610,36 @@ print ''; print '
'; +// Form to validate all movements +if (count($listofdata)) { + print '
'; + print ''; + print ''; -print ''; -print ''; -print ''; + // Button to record mass movement + $codemove = (GETPOSTISSET("codemove") ? GETPOST("codemove", 'alpha') : dol_print_date(dol_now(), '%Y%m%d%H%M%S')); + $labelmovement = GETPOST("label") ? GETPOST('label') : $langs->trans("StockTransfer").' '.dol_print_date($now, '%Y-%m-%d %H:%M'); -// Button to record mass movement -$codemove = (GETPOSTISSET("codemove") ? GETPOST("codemove", 'alpha') : dol_print_date(dol_now(), '%Y%m%d%H%M%S')); -$labelmovement = GETPOST("label") ? GETPOST('label') : $langs->trans("StockTransfer").' '.dol_print_date($now, '%Y-%m-%d %H:%M'); + print '
'; + print ''.$langs->trans("InventoryCode").': '; + print '   '; + print ''; + print $langs->trans("MovementLabel").': '; + print '
'; + print '
'; -print '
'; -print ''.$langs->trans("InventoryCode").': '; -print '   '; -print ''; -print $langs->trans("MovementLabel").': '; -print '
'; -print '
'; + print '
'; -print '
'; + print '
'; + print '
'; -print '
'; -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();