25require
'../../main.inc.php';
26include_once DOL_DOCUMENT_ROOT.
'/core/class/html.formcompany.class.php';
27include_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
28include_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
29include_once DOL_DOCUMENT_ROOT.
'/product/inventory/class/inventory.class.php';
30include_once DOL_DOCUMENT_ROOT.
'/product/inventory/lib/inventory.lib.php';
31include_once DOL_DOCUMENT_ROOT.
'/product/stock/class/mouvementstock.class.php';
32include_once DOL_DOCUMENT_ROOT.
'/product/stock/class/productlot.class.php';
35$langs->loadLangs(array(
"stocks",
"other",
"productbatch"));
40$action =
GETPOST(
'action',
'aZ09');
41$confirm =
GETPOST(
'confirm',
'alpha');
42$cancel =
GETPOST(
'cancel',
'aZ09');
43$contextpage =
GETPOST(
'contextpage',
'aZ') ?
GETPOST(
'contextpage',
'aZ') :
'inventorycard';
44$backtopage =
GETPOST(
'backtopage',
'alpha');
45$listoffset =
GETPOST(
'listoffset',
'alpha');
46$limit =
GETPOST(
'limit',
'int') > 0 ?
GETPOST(
'limit',
'int') : $conf->liste_limit;
47$page = GETPOSTISSET(
'pageplusone') ? (
GETPOST(
'pageplusone') - 1) :
GETPOST(
"page",
'int');
48if (empty($page) || $page == -1) {
51$offset = $limit * $page;
55$fk_warehouse =
GETPOST(
'fk_warehouse',
'int');
56$fk_product =
GETPOST(
'fk_product',
'int');
57$lineid =
GETPOST(
'lineid',
'int');
58$batch =
GETPOST(
'batch',
'alphanohtml');
59$totalExpectedValuation = 0;
60$totalRealValuation = 0;
61if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
64 $result =
restrictedArea($user,
'stock', $id,
'',
'inventory_advance');
70$diroutputmassaction = $conf->stock->dir_output.
'/temp/massgeneration/'.$user->id;
71$hookmanager->initHooks(array(
'inventorycard'));
74$extrafields->fetch_name_optionals_label($object->table_element);
76$search_array_options = $extrafields->getOptionalsFromPost($object->table_element,
'',
'search_');
79$search_all =
GETPOST(
"search_all",
'alpha');
81foreach ($object->fields as $key => $val) {
82 if (
GETPOST(
'search_'.$key,
'alpha')) {
83 $search[$key] =
GETPOST(
'search_'.$key,
'alpha');
87if (empty($action) && empty($id) && empty($ref)) {
92include DOL_DOCUMENT_ROOT.
'/core/actions_fetchobject.inc.php';
100$paramwithsearch =
'';
101if ($limit > 0 && $limit != $conf->liste_limit) {
102 $paramwithsearch .=
'&limit='.((int) $limit);
106if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
107 $permissiontoadd = $user->rights->stock->creer;
108 $permissiontodelete = $user->rights->stock->supprimer;
110 $permissiontoadd = $user->rights->stock->inventory_advance->write;
111 $permissiontodelete = $user->rights->stock->inventory_advance->write;
127$parameters = array();
128$reshook = $hookmanager->executeHooks(
'doActions', $parameters, $object, $action);
133if (empty($reshook)) {
136 if ($action ==
'cancel_record' && $permissiontoadd) {
137 $object->setCanceled($user);
141 if ($action ==
'update' && !empty($user->rights->stock->mouvement->creer) && $object->status == $object::STATUS_VALIDATED) {
143 $stockmovment->setOrigin($object->element, $object->id);
145 $cacheOfProducts = array();
149 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
150 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.pmp_real';
151 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
152 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
154 $resql = $db->query($sql);
156 $num = $db->num_rows($resql);
158 $totalarray = array();
160 $line = $db->fetch_object($resql);
162 $qty_stock = $line->qty_stock;
163 $qty_view = $line->qty_view;
167 if (isset($cacheOfProducts[$line->fk_product])) {
168 $product_static = $cacheOfProducts[$line->fk_product];
170 $product_static =
new Product($db);
171 $result = $product_static->fetch($line->fk_product,
'',
'',
'', 1, 1, 1);
174 $option .=
',novirtual';
175 $product_static->load_stock($option);
177 $cacheOfProducts[$product_static->id] = $product_static;
181 $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->real;
182 if (isModEnabled(
'productbatch') && $product_static->hasbatch()) {
183 $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->detail_batch[$line->batch]->qty;
187 if (!is_null($qty_view)) {
188 $stock_movement_qty =
price2num($qty_view - $realqtynow,
'MS');
189 if ($stock_movement_qty != 0) {
190 if ($stock_movement_qty < 0) {
198 $inventorycode =
'INV-'.$object->ref;
200 if (!empty($line->pmp_real) && !empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) $price = $line->pmp_real;
202 $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, $price, $langs->trans(
'LabelOfInventoryMovemement', $object->ref), $inventorycode, $datemovement,
'',
'', $line->batch);
203 if ($idstockmove < 0) {
210 $sqlupdate =
"UPDATE ".MAIN_DB_PREFIX.
"inventorydet";
211 $sqlupdate .=
" SET fk_movement = ".((int) $idstockmove);
212 if ($qty_stock != $realqtynow) {
213 $sqlupdate .=
", qty_stock = ".((float) $realqtynow);
215 $sqlupdate .=
" WHERE rowid = ".((int) $line->rowid);
216 $resqlupdate = $db->query($sqlupdate);
217 if (! $resqlupdate) {
224 if (!empty($line->pmp_real) && !empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
225 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product SET pmp = '.((float) $line->pmp_real).
' WHERE rowid = '.((int) $line->fk_product);
226 $resqlpmp = $db->query($sqlpmp);
232 if (!empty($conf->global->MAIN_PRODUCT_PERENTITY_SHARED)) {
233 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product_perentity SET pmp = '.((float) $line->pmp_real).
' WHERE fk_product = '.((int) $line->fk_product).
' AND entity='.$conf->entity;
234 $resqlpmp = $db->query($sqlpmp);
247 $object->setRecorded($user);
262 if ($action ==
'updateinventorylines' && $permissiontoadd) {
263 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
264 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
265 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
266 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
267 $sql .= $db->order(
'id.rowid',
'ASC');
268 $sql .= $db->plimit($limit, $offset);
272 $resql = $db->query($sql);
274 $num = $db->num_rows($resql);
276 $totalarray = array();
280 $line = $db->fetch_object($resql);
281 $lineid = $line->rowid;
286 if (
GETPOST(
"id_".$lineid,
'alpha') !=
'') {
288 $result = $inventoryline->fetch($lineid);
289 if ($qtytoupdate < 0) {
291 setEventMessages($langs->trans(
"FieldCannotBeNegative", $langs->transnoentitiesnoconv(
"RealQty")),
null,
'errors');
294 $inventoryline->qty_stock =
price2num(
GETPOST(
'stock_qty_'.$lineid,
'alpha'),
'MS');
295 $inventoryline->qty_view = $qtytoupdate;
296 $inventoryline->pmp_real =
price2num(
GETPOST(
'realpmp_'.$lineid,
'alpha'),
'MS');
297 $inventoryline->pmp_expected =
price2num(
GETPOST(
'expectedpmp_'.$lineid,
'alpha'),
'MS');
298 $resultupdate = $inventoryline->update($user);
300 } elseif (GETPOSTISSET(
'id_' . $lineid)) {
302 $result = $inventoryline->fetch($lineid);
304 $inventoryline->qty_view =
null;
305 $inventoryline->pmp_real =
price2num(
GETPOST(
'realpmp_'.$lineid,
'alpha'),
'MS');
306 $inventoryline->pmp_expected =
price2num(
GETPOST(
'expectedpmp_'.$lineid,
'alpha'),
'MS');
307 $resultupdate = $inventoryline->update($user);
311 if ($result < 0 || $resultupdate < 0) {
321 $sqlupdate =
"UPDATE ".MAIN_DB_PREFIX.
"inventory";
322 $sqlupdate .=
" SET fk_user_modif = ".((int) $user->id);
323 $sqlupdate .=
" WHERE rowid = ".((int) $object->id);
324 $resqlupdate = $db->query($sqlupdate);
325 if (! $resqlupdate) {
338 $backurlforlist = DOL_URL_ROOT.
'/product/inventory/list.php';
339 $backtopage = DOL_URL_ROOT.
'/product/inventory/inventory.php?id='.$object->id.
'&page='.$page.$paramwithsearch;
342 include DOL_DOCUMENT_ROOT.
'/core/actions_addupdatedelete.inc.php';
345 include DOL_DOCUMENT_ROOT.
'/core/actions_dellink.inc.php';
348 include DOL_DOCUMENT_ROOT.
'/core/actions_printing.inc.php';
356 if (
GETPOST(
'addline',
'alpha')) {
358 if ($fk_warehouse <= 0) {
360 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Warehouse")),
null,
'errors');
362 if ($fk_product <= 0) {
364 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Product")),
null,
'errors');
368 setEventMessages($langs->trans(
"FieldCannotBeNegative", $langs->transnoentitiesnoconv(
"RealQty")),
null,
'errors');
370 if (!$error && isModEnabled(
'productbatch')) {
371 $tmpproduct =
new Product($db);
372 $result = $tmpproduct->fetch($fk_product);
374 if (empty($error) && $tmpproduct->status_batch>0 && empty($batch)) {
376 $langs->load(
"errors");
377 setEventMessages($langs->trans(
"ErrorProductNeedBatchNumber", $tmpproduct->ref),
null,
'errors');
379 if (empty($error) && $tmpproduct->status_batch==2 && !empty($batch) && $qty>1) {
381 $langs->load(
"errors");
382 setEventMessages($langs->trans(
"TooManyQtyForSerialNumber", $tmpproduct->ref, $batch),
null,
'errors');
384 if (empty($error) && empty($tmpproduct->status_batch) && !empty($batch)) {
386 $langs->load(
"errors");
387 setEventMessages($langs->trans(
"ErrorProductDoesNotNeedBatchNumber", $tmpproduct->ref),
null,
'errors');
392 $tmp->fk_inventory = $object->id;
393 $tmp->fk_warehouse = $fk_warehouse;
394 $tmp->fk_product = $fk_product;
395 $tmp->batch = $batch;
397 $tmp->qty_view = $qty;
399 $result = $tmp->create($user);
401 if ($db->lasterrno() ==
'DB_ERROR_RECORD_ALREADY_EXISTS') {
402 $langs->load(
"errors");
403 setEventMessages($langs->trans(
"ErrorRecordAlreadyExists"),
null,
'errors');
409 $_POST[
'batch'] =
'';
410 $_POST[
'qtytoadd'] =
'';
422$form =
new Form($db);
427llxHeader(
'', $langs->trans(
'Inventory'), $help_url);
430if ($object->id <= 0) {
436$res = $object->fetch_optionals();
444if ($action ==
'delete') {
445 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'DeleteInventory'), $langs->trans(
'ConfirmDeleteOrder'),
'confirm_delete',
'', 0, 1);
448if ($action ==
'deleteline') {
449 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&lineid='.$lineid.
'&page='.$page.$paramwithsearch, $langs->trans(
'DeleteLine'), $langs->trans(
'ConfirmDeleteLine'),
'confirm_deleteline',
'', 0, 1);
453if ($action ==
'clone') {
455 $formquestion = array();
456 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'ToClone'), $langs->trans(
'ConfirmCloneMyObject', $object->ref),
'confirm_clone', $formquestion,
'yes', 1);
460if ($action ==
'record') {
461 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&page='.$page.$paramwithsearch, $langs->trans(
'Close'), $langs->trans(
'ConfirmFinish'),
'update',
'', 0, 1);
466if ($action ==
'confirm_cancel') {
467 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'Cancel'), $langs->trans(
'ConfirmCancel'),
'cancel_record',
'', 0, 1);
472$parameters = array(
'formConfirm' => $formconfirm,
'lineid' => $lineid);
473$reshook = $hookmanager->executeHooks(
'formConfirm', $parameters, $object, $action);
474if (empty($reshook)) {
475 $formconfirm .= $hookmanager->resPrint;
476} elseif ($reshook > 0) {
477 $formconfirm = $hookmanager->resPrint;
486$linkback =
'<a href="'.DOL_URL_ROOT.
'/product/inventory/list.php">'.$langs->trans(
"BackToList").
'</a>';
488$morehtmlref =
'<div class="refidno">';
528$morehtmlref .=
'</div>';
531dol_banner_tab($object,
'ref', $linkback, 1,
'ref',
'ref', $morehtmlref);
534print
'<div class="fichecenter">';
535print
'<div class="fichehalfleft">';
536print
'<div class="underbanner clearboth"></div>';
537print
'<table class="border centpercent tableforfield">'.
"\n";
540include DOL_DOCUMENT_ROOT.
'/core/tpl/commonfields_view.tpl.php';
543include DOL_DOCUMENT_ROOT.
'/core/tpl/extrafields_view.tpl.php';
551print
'<div class="clearboth"></div>';
555print
'<form id="formrecord" name="formrecord" method="POST" action="'.$_SERVER[
"PHP_SELF"].
'?page='.$page.
'&id='.$object->id.
'">';
556print
'<input type="hidden" name="token" value="'.newToken().
'">';
557print
'<input type="hidden" name="action" value="updateinventorylines">';
558print
'<input type="hidden" name="id" value="'.$object->id.
'">';
560 print
'<input type="hidden" name="backtopage" value="'.$backtopage.
'">';
565if ($action !=
'record') {
566 print
'<div class="tabsAction">'.
"\n";
567 $parameters = array();
568 $reshook = $hookmanager->executeHooks(
'addMoreActionsButtons', $parameters, $object, $action);
573 if (empty($reshook)) {
574 if ($object->status == Inventory::STATUS_DRAFT) {
575 if ($permissiontoadd) {
576 print
'<a class="butAction" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=confirm_validate&confirm=yes&token='.newToken().
'">'.$langs->trans(
"Validate").
' ('.$langs->trans(
"Start").
')</a>'.
"\n";
578 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'Validate').
' ('.$langs->trans(
"Start").
')</a>'.
"\n";
583 if ($object->status == $object::STATUS_VALIDATED) {
584 if ($permissiontoadd) {
585 print
'<a class="butAction classfortooltip" id="idbuttonmakemovementandclose" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=record&page='.$page.$paramwithsearch.
'&token='.newToken().
'" title="'.
dol_escape_htmltag($langs->trans(
"MakeMovementsAndClose")).
'">'.$langs->trans(
"MakeMovementsAndClose").
'</a>'.
"\n";
587 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'MakeMovementsAndClose').
'</a>'.
"\n";
590 if ($permissiontoadd) {
591 print
'<a class="butActionDelete" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=confirm_cancel&page='.$page.$paramwithsearch.
'&token='.newToken().
'">'.$langs->trans(
"Cancel").
'</a>'.
"\n";
597 if ($object->status != Inventory::STATUS_DRAFT && $object->status != Inventory::STATUS_VALIDATED) {
604if ($object->status == Inventory::STATUS_VALIDATED) {
606 if (!empty($conf->use_javascript_ajax)) {
607 if ($permissiontoadd) {
609 if (isModEnabled(
'barcode') || isModEnabled(
'productbatch')) {
610 print
'<a href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=updatebyscaning&token='.
currentToken().
'" class="marginrightonly paddingright marginleftonly paddingleft">'.
img_picto(
'',
'barcode',
'class="paddingrightonly"').$langs->trans(
"UpdateByScaning").
'</a>';
614 print
'<a id="fillwithexpected" class="marginrightonly paddingright marginleftonly paddingleft" href="#">'.img_picto(
'',
'autofill',
'class="paddingrightonly"').$langs->trans(
'AutofillWithExpected').
'</a>';
616 print
'$( document ).ready(function() {';
617 print
' $("#fillwithexpected").on("click",function fillWithExpected(){
618 $(".expectedqty").each(function(){
619 var object = $(this)[0];
620 var objecttofill = $("#"+object.id+"_input")[0];
621 objecttofill.value = object.innerText;
622 jQuery(".realqty").trigger("change");
624 console.log("Values filled (after click on fillwithexpected)");
625 /* disablebuttonmakemovementandclose(); */
632 print
'<a href="#" id="clearqty" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
'',
'eraser',
'class="paddingrightonly"').$langs->trans(
"ClearQtys").
'</a>';
634 print
'<a class="classfortooltip marginrightonly paddingright marginleftonly paddingleft" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
"Save").
'</a>'.
"\n";
644if ($action ==
'updatebyscaning') {
645 if ($permissiontoadd) {
650 var duplicatedbatchcode = [];
656 function barcodescannerjs(){
657 console.log("We catch inputs in scanner box");
658 jQuery("#scantoolmessage").text();
660 var selectaddorreplace = $("select[name=selectaddorreplace]").val();
661 var barcodemode = $("input[name=barcodemode]:checked").val();
662 var barcodeproductqty = $("input[name=barcodeproductqty]").val();
663 var textarea = $("textarea[name=barcodelist]").val();
664 var textarray = textarea.split(/[\s,;]+/);
666 duplicatedbatchcode = [];
672 textarray = textarray.filter(function(value){
675 if(textarray.some((element) => element != "")){
676 $(".expectedqty").each(function(){
678 console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
679 warehouse = $("#"+id+"_warehouse").attr(\'data-ref\');
680 //console.log(warehouse);
681 productbarcode = $("#"+id+"_product").attr(\'data-barcode\');
682 //console.log(productbarcode);
683 productbatchcode = $("#"+id+"_batch").attr(\'data-batch\');
684 //console.log(productbatchcode);
686 if (barcodemode != "barcodeforproduct") {
687 tabproduct.forEach(product=>{
688 console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
689 if(product.Batch != "" && product.Batch == productbatchcode){
690 console.log("duplicate batch code found for batch code "+productbatchcode);
691 duplicatedbatchcode.push(productbatchcode);
695 productinput = $("#"+id+"_input").val();
696 if(productinput == ""){
699 tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
702 console.log("Loop on each record entered in the textarea");
703 textarray.forEach(function(element,index){
704 console.log("Process record element="+element+" id="+id);
705 var verify_batch = false;
706 var verify_barcode = false;
708 case "barcodeforautodetect":
709 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode",true);
710 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial",true);
712 case "barcodeforproduct":
713 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode");
715 case "barcodeforlotserial":
716 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial");
719 alert(\''.dol_escape_js($langs->trans(
"ErrorWrongBarcodemode")).
' "\'+barcodemode+\'"\');
720 throw \''.
dol_escape_js($langs->trans(
'ErrorWrongBarcodemode')).
' "\'+barcodemode+\'"\';
723 if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
724 errortab2.push(element);
725 } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
726 errortab3.push(element);
727 } else if (verify_batch == true) {
728 console.log("element="+element);
729 console.log(duplicatedbatchcode);
730 if (duplicatedbatchcode.includes(element)) {
731 errortab1.push(element);
736 if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
737 tabproduct.forEach(product => {
739 console.log("We change #"+product.Id+"_input to match input in scanner box");
740 if(product.hasOwnProperty("reelqty")){
741 $.ajax({ url: \''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
742 data: { "token":"'.newToken().
'", "action":"addnewlineproduct", "fk_entrepot":product.Warehouse, "batch":product.Batch, "fk_inventory":'.
dol_escape_js($object->id).
', "fk_product":product.fk_product, "reelqty":product.reelqty},
745 success: function(response) {
746 response = JSON.parse(response);
747 if(response.status == "success"){
748 console.log(response.message);
749 $("<input type=\'text\' value=\'"+product.Qty+"\' />")
750 .attr("id", "id_"+response.id_line+"_input")
751 .attr("name", "id_"+response.id_line)
752 .appendTo("#formrecord");
754 console.error(response.message);
757 error : function(output) {
758 console.error("Error on line creation function");
762 $("#"+product.Id+"_input").val(product.Qty);
766 jQuery("#scantoolmessage").text("'.
dol_escape_js($langs->transnoentities(
"QtyWasAddedToTheScannedBarcode")).
'\n");
767 /* document.forms["formrecord"].submit(); */
769 let stringerror = "";
770 if (Object.keys(errortab1).length > 0) {
771 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorSameBatchNumber')).
': ";
772 errortab1.forEach(element => {
773 stringerror += (element + ", ")
775 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
777 if (Object.keys(errortab2).length > 0) {
778 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCantFindCodeInInventory')).
': ";
779 errortab2.forEach(element => {
780 stringerror += (element + ", ")
782 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
784 if (Object.keys(errortab3).length > 0) {
785 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCodeScannedIsBothProductAndSerial')).
': ";
786 errortab3.forEach(element => {
787 stringerror += (element + ", ")
789 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
791 if (Object.keys(errortab4).length > 0) {
792 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorBarcodeNotFoundForProductWarehouse')).
': ";
793 errortab4.forEach(element => {
794 stringerror += (element + ", ")
796 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
799 jQuery("#scantoolmessage").html(\''.
dol_escape_js($langs->transnoentities(
"ErrorOnElementsInventory")).
'\' + stringerror);
807 function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,mode,autodetect=
false){
808 BarcodeIsInProduct=0;
811 tabproduct.forEach(product => {
812 $.ajax({ url: \
''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
813 data: { "token":"'.newToken().
'", "action":"existbarcode", '.(!empty($object->fk_warehouse)?
'"fk_entrepot":'.$object->fk_warehouse.
', ':
'').(!empty($object->fk_product)?
'"fk_product":'.$object->fk_product.
', ':
'').
'"barcode":element, "product":product, "mode":mode},
816 success: function(response) {
817 response = JSON.parse(response);
818 if (response.status == "success"){
819 console.log(response.message);
821 newproductrow = response.object;
824 if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)){
825 errortab4.push(element);
826 console.error(response.message);
830 error : function(output) {
831 console.error("Error on barcodeserialforproduct function");
834 console.log("Product "+(index+=1)+": "+element);
835 if(mode == "barcode"){
836 testonproduct = product.Barcode
837 }else if (mode == "lotserial"){
838 testonproduct = product.Batch
840 if(testonproduct == element){
841 if(selectaddorreplace == "add"){
842 productqty = parseInt(product.Qty,10);
843 product.Qty = productqty + parseInt(barcodeproductqty,10);
844 }else if(selectaddorreplace == "replace"){
845 if(product.fetched == false){
846 product.Qty = barcodeproductqty
849 productqty = parseInt(product.Qty,10);
850 product.Qty = productqty + parseInt(barcodeproductqty,10);
853 BarcodeIsInProduct+=1;
856 if(BarcodeIsInProduct==0 && newproductrow!=0){
857 tabproduct.push({\'Id\':tabproduct.length-1,\'Warehouse\':newproductrow.fk_warehouse,\'Barcode\':mode=="barcode"?element:null,\'Batch\':mode=="lotserial"?element:null,\'Qty\':barcodeproductqty,\'fetched\':true,\'reelqty\':newproductrow.reelqty,\'fk_product\':newproductrow.fk_product,\'mode\':mode});
860 if(BarcodeIsInProduct > 0){
868 include DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
870 print $formother->getHTMLScannerForm(
"barcodescannerjs",
'all');
875print
'jQuery(document).ready(function() {
876 $("#clearqty").on("click", function() {
877 console.log("Clear all values");
878 /* disablebuttonmakemovementandclose(); */
879 jQuery(".realqty").val("");
880 jQuery(".realqty").trigger("change");
881 return false; /* disable submit */
883 $(".undochangesqty").on("click", function undochangesqty() {
884 console.log("Clear value of inventory line");
886 id = id.split("_")[1];
887 tmpvalue = $("#id_"+id+"_input_tmp").val()
888 $("#id_"+id+"_input")[0].value = tmpvalue;
889 /* disablebuttonmakemovementandclose(); */
890 return false; /* disable submit */
895print
'<div class="fichecenter">';
897print
'<div class="clearboth"></div>';
901print
'<div class="div-table-responsive-no-min">';
902print
'<table id="tablelines" class="noborder noshadow centpercent">';
904print
'<tr class="liste_titre">';
905print
'<td>'.$langs->trans(
"Warehouse").
'</td>';
906print
'<td>'.$langs->trans(
"Product").
'</td>';
907if (isModEnabled(
'productbatch')) {
909 print $langs->trans(
"Batch");
912print
'<td class="right">'.$langs->trans(
"ExpectedQty").
'</td>';
913if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
914 print
'<td class="right">'.$langs->trans(
'PMPExpected').
'</td>';
915 print
'<td class="right">'.$langs->trans(
'ExpectedValuation').
'</td>';
916 print
'<td class="right">'.$form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp")).
'</td>';
917 print
'<td class="right">'.$langs->trans(
'PMPReal').
'</td>';
918 print
'<td class="right">'.$langs->trans(
'RealValuation').
'</td>';
920 print
'<td class="right">';
921 print $form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp"));
924if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
926 print
'<td class="center">';
930 print
'<td class="right">';
937if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
940 print $formproduct->selectWarehouses((GETPOSTISSET(
'fk_warehouse') ?
GETPOST(
'fk_warehouse',
'int') : $object->fk_warehouse),
'fk_warehouse',
'warehouseopen', 1, 0, 0,
'', 0, 0, array(),
'maxwidth300');
943 print $form->select_produits((GETPOSTISSET(
'fk_product') ?
GETPOST(
'fk_product',
'int') : $object->fk_product),
'fk_product',
'', 0, 0, -1, 2,
'', 0, null, 0,
'1', 0,
'maxwidth300');
945 if (isModEnabled(
'productbatch')) {
947 print
'<input type="text" name="batch" class="maxwidth100" value="'.(GETPOSTISSET(
'batch') ?
GETPOST(
'batch') :
'').
'">';
950 print
'<td class="right"></td>';
951 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
952 print
'<td class="right">';
954 print
'<td class="right">';
956 print
'<td class="right">';
957 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
959 print
'<td class="right">';
961 print
'<td class="right">';
964 print
'<td class="right">';
965 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
969 print
'<td class="center">';
970 print
'<input type="submit" class="button paddingright" name="addline" value="'.$langs->trans(
"Add").
'">';
976$sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
977$sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.fk_movement, id.pmp_real, id.pmp_expected';
978$sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
979$sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
980$sql .= $db->order(
'id.rowid',
'ASC');
981$sql .= $db->plimit($limit, $offset);
983$cacheOfProducts = array();
984$cacheOfWarehouses = array();
987$resql = $db->query($sql);
989 $num = $db->num_rows($resql);
991 if (!empty($limit != 0) || $num > $limit || $page) {
992 print_fleche_navigation($page, $_SERVER[
"PHP_SELF"],
'&id='.$object->id.$paramwithsearch, ($num >= $limit),
'<li class="pagination"><span>' . $langs->trans(
"Page") .
' ' . ($page + 1) .
'</span></li>',
'', $limit);
997 $totalarray = array();
999 $obj = $db->fetch_object($resql);
1001 if (isset($cacheOfWarehouses[$obj->fk_warehouse])) {
1002 $warehouse_static = $cacheOfWarehouses[$obj->fk_warehouse];
1004 $warehouse_static =
new Entrepot($db);
1005 $warehouse_static->fetch($obj->fk_warehouse);
1007 $cacheOfWarehouses[$warehouse_static->id] = $warehouse_static;
1012 if (isset($cacheOfProducts[$obj->fk_product])) {
1013 $product_static = $cacheOfProducts[$obj->fk_product];
1015 $product_static =
new Product($db);
1016 $result = $product_static->fetch($obj->fk_product,
'',
'',
'', 1, 1, 1);
1019 $option .=
',novirtual';
1020 $product_static->load_stock($option);
1022 $cacheOfProducts[$product_static->id] = $product_static;
1025 print
'<tr class="oddeven">';
1026 print
'<td id="id_'.$obj->rowid.
'_warehouse" data-ref="'.
dol_escape_htmltag($warehouse_static->ref).
'">';
1027 print $warehouse_static->getNomUrl(1);
1030 print $product_static->getNomUrl(1).
' - '.$product_static->label;
1033 if (isModEnabled(
'productbatch')) {
1034 print
'<td id="id_'.$obj->rowid.
'_batch" data-batch="'.
dol_escape_htmltag($obj->batch).
'">';
1036 $res = $batch_static->fetch(0, $product_static->id, $obj->batch);
1038 print $batch_static->getNomUrl(1);
1046 print
'<td class="right expectedqty" id="id_'.$obj->rowid.
'" title="Stock viewed at last update: '.$obj->qty_stock.
'">';
1047 $valuetoshow = $obj->qty_stock;
1050 if (($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) && !
getDolGlobalString(
'DISABLE_QTY_OVERWRITE')) {
1051 if (isModEnabled(
'productbatch') && $product_static->hasbatch()) {
1052 $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->detail_batch[$obj->batch]->qty;
1054 $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->real;
1058 print
'<input type="hidden" name="stock_qty_'.$obj->rowid.
'" value="'.$valuetoshow.
'">';
1062 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
1066 if ($qty_view !=
'') {
1070 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1072 if (!empty($obj->pmp_expected)) $pmp_expected = $obj->pmp_expected;
1073 else $pmp_expected = $product_static->pmp;
1074 $pmp_valuation = $pmp_expected * $valuetoshow;
1075 print
'<td class="right">';
1076 print
price($pmp_expected);
1077 print
'<input type="hidden" name="expectedpmp_'.$obj->rowid.
'" value="'.$pmp_expected.
'"/>';
1079 print
'<td class="right">';
1080 print
price($pmp_valuation);
1083 print
'<td class="right">';
1084 print
'<a id="undochangesqty_'.$obj->rowid.
'" href="#" class="undochangesqty reposition marginrightonly" title="'.
dol_escape_htmltag($langs->trans(
"Clear")).
'">';
1085 print
img_picto(
'',
'eraser',
'class="opacitymedium"');
1087 print
'<input type="text" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input" value="'.$qty_view.
'">';
1091 print
'<td class="right">';
1092 if (!empty($obj->pmp_real) || (
string) $obj->pmp_real ===
'0') {
1093 $pmp_real = $obj->pmp_real;
1095 $pmp_real = $product_static->pmp;
1097 $pmp_valuation_real = $pmp_real * $qty_view;
1098 print
'<input type="text" class="maxwidth75 right realpmp'.$obj->fk_product.
'" name="realpmp_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input_pmp" value="'.
price2num($pmp_real).
'">';
1100 print
'<td class="right">';
1101 print
'<input type="text" class="maxwidth75 right realvaluation'.$obj->fk_product.
'" name="realvaluation_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input_real_valuation" value="'.$pmp_valuation_real.
'">';
1104 $totalExpectedValuation += $pmp_valuation;
1105 $totalRealValuation += $pmp_valuation_real;
1107 print
'<td class="right">';
1108 print
'<a id="undochangesqty_'.$obj->rowid.
'" href="#" class="undochangesqty reposition marginrightonly" title="'.
dol_escape_htmltag($langs->trans(
"Clear")).
'">';
1109 print
img_picto(
'',
'eraser',
'class="opacitymedium"');
1111 print
'<input type="text" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input" value="'.$qty_view.
'">';
1116 print
'<td class="right">';
1117 print
'<a class="reposition" href="'.DOL_URL_ROOT.
'/product/inventory/inventory.php?id='.$object->id.
'&lineid='.$obj->rowid.
'&action=deleteline&page='.$page.$paramwithsearch.
'&token='.newToken().
'">'.
img_delete().
'</a>';
1118 $qty_tmp =
price2num(
GETPOST(
"id_".$obj->rowid.
"_input_tmp",
'MS')) >= 0 ?
GETPOST(
"id_".$obj->rowid.
"_input_tmp") : $qty_view;
1119 print
'<input type="hidden" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'_input_tmp" id="id_'.$obj->rowid.
'_input_tmp" value="'.$qty_tmp.
'">';
1122 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1124 if (!empty($obj->pmp_expected)) $pmp_expected = $obj->pmp_expected;
1125 else $pmp_expected = $product_static->pmp;
1126 $pmp_valuation = $pmp_expected * $valuetoshow;
1127 print
'<td class="right">';
1128 print
price($pmp_expected);
1130 print
'<td class="right">';
1131 print
price($pmp_valuation);
1134 print
'<td class="right nowraponall">';
1135 print $obj->qty_view;
1139 print
'<td class="right">';
1140 if (!empty($obj->pmp_real)) $pmp_real = $obj->pmp_real;
1141 else $pmp_real = $product_static->pmp;
1142 $pmp_valuation_real = $pmp_real * $obj->qty_view;
1143 print
price($pmp_real);
1145 print
'<td class="right">';
1146 print
price($pmp_valuation_real);
1148 print
'<td class="nowraponall right">';
1150 $totalExpectedValuation += $pmp_valuation;
1151 $totalRealValuation += $pmp_valuation_real;
1153 print
'<td class="right nowraponall">';
1154 print $obj->qty_view;
1158 if ($obj->fk_movement > 0) {
1160 $stockmovment->fetch($obj->fk_movement);
1161 print $stockmovment->getNomUrl(1,
'movements');
1172if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1173 print
'<tr class="liste_total">';
1174 print
'<td colspan="4">'.$langs->trans(
"Total").
'</td>';
1175 print
'<td class="right" colspan="2">'.price($totalExpectedValuation).
'</td>';
1176 print
'<td class="right" id="totalRealValuation" colspan="3">'.price($totalRealValuation).
'</td>';
1184if ($object->status == $object::STATUS_VALIDATED) {
1185 print
'<center><input id="submitrecord" type="submit" class="button button-save" name="save" value="'.$langs->trans(
"Save").
'"></center>';
1205print
'<script type="text/javascript">
1206 $(document).ready(function() {
1208 $(".paginationnext:last").click(function(e){
1209 var form = $("#formrecord");
1210 var actionURL = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page).$paramwithsearch.
'";
1213 data: form.serialize(),
1215 success: function(result){
1216 window.location.href = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page + 1).$paramwithsearch.
'";
1221 $(".paginationprevious:last").click(function(e){
1222 var form = $("#formrecord");
1223 var actionURL = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page).$paramwithsearch.
'";
1226 data: form.serialize(),
1228 success: function(result){
1229 window.location.href = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page - 1).$paramwithsearch.
'";
1233 $("#idbuttonmakemovementandclose").click(function(e){
1234 var form = $("#formrecord");
1235 var actionURL = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page).$paramwithsearch.
'";
1238 data: form.serialize(),
1240 success: function(result){
1241 window.location.href = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page - 1).$paramwithsearch.
'&action=record";
1248if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1250<script
type=
"text/javascript">
1251$(
'.realqty').on(
'change',
function () {
1252 let realqty = $(
this).closest(
'tr').find(
'.realqty').val();
1253 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1254 let realpmp = $(inputPmp).val();
1255 if (!isNaN(realqty) && !isNaN(realpmp)) {
1256 let realval = realqty * realpmp;
1257 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1259 updateTotalValuation();
1262$(
'input[class*=realpmp]').on(
'change',
function () {
1263 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1264 let realqty = $(inputQtyReal).val();
1265 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1266 console.log(inputPmp);
1267 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1268 let realpmp = $(inputPmp).val();
1269 if (!isNaN(realpmp)) {
1270 $(
'.'+realPmpClassname).val(realpmp);
1272 if (!isNaN(realqty)) {
1273 let realval = realqty * realpmp;
1274 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1276 $(
'.realqty').trigger(
'change');
1277 updateTotalValuation();
1281$(
'input[name^=realvaluation]').on(
'change',
function () {
1282 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1283 let realqty = $(inputQtyReal).val();
1284 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1285 let inputRealValuation = $(
this).closest(
'tr').find(
'input[name^=realvaluation]');
1286 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1287 let realvaluation = $(inputRealValuation).val();
1288 if (!isNaN(realvaluation) && !isNaN(realqty) && realvaluation !==
'' && realqty !==
'' && realqty !== 0) {
1289 let realpmp = realvaluation / realqty
1290 $(
'.'+realPmpClassname).val(realpmp);
1291 $(
'.realqty').trigger(
'change');
1292 updateTotalValuation();
1296function updateTotalValuation() {
1298 $(
'input[name^=realvaluation]').each(
function( index ) {
1299 let val = $(
this).val();
1300 if(!isNaN(val)) total += parseFloat($(
this).val());
1302 let currencyFractionDigits =
new Intl.NumberFormat(
'fr-FR', {
1305 }).resolvedOptions().maximumFractionDigits;
1306 $(
'#totalRealValuation').html(total.toLocaleString(
'fr-FR', {
1307 maximumFractionDigits: currencyFractionDigits
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader()
Empty header.
Class to manage warehouses.
Class to manage stock movements.
Class to manage products or services.
Class with list of lots and properties.
dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
Show tab footer of a card.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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...
inventoryPrepareHead(&$inventory, $title='Inventory', $get='')
Define head array for tabs of inventory tools setup pages.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
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.