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;
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);
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' && $user->hasRight(
'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;
201 $price = $line->pmp_real;
204 $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);
205 if ($idstockmove < 0) {
212 $sqlupdate =
"UPDATE ".MAIN_DB_PREFIX.
"inventorydet";
213 $sqlupdate .=
" SET fk_movement = ".((int) $idstockmove);
214 if ($qty_stock != $realqtynow) {
215 $sqlupdate .=
", qty_stock = ".((float) $realqtynow);
217 $sqlupdate .=
" WHERE rowid = ".((int) $line->rowid);
218 $resqlupdate = $db->query($sqlupdate);
219 if (! $resqlupdate) {
227 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product SET pmp = '.((float) $line->pmp_real).
' WHERE rowid = '.((int) $line->fk_product);
228 $resqlpmp = $db->query($sqlpmp);
235 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product_perentity SET pmp = '.((float) $line->pmp_real).
' WHERE fk_product = '.((int) $line->fk_product).
' AND entity='.$conf->entity;
236 $resqlpmp = $db->query($sqlpmp);
249 $object->setRecorded($user);
264 if ($action ==
'updateinventorylines' && $permissiontoadd) {
265 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
266 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
267 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
268 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
269 $sql .= $db->order(
'id.rowid',
'ASC');
270 $sql .= $db->plimit($limit, $offset);
274 $resql = $db->query($sql);
276 $num = $db->num_rows($resql);
278 $totalarray = array();
282 $line = $db->fetch_object($resql);
283 $lineid = $line->rowid;
288 if (
GETPOST(
"id_".$lineid,
'alpha') !=
'') {
290 $result = $inventoryline->fetch($lineid);
291 if ($qtytoupdate < 0) {
293 setEventMessages($langs->trans(
"FieldCannotBeNegative", $langs->transnoentitiesnoconv(
"RealQty")),
null,
'errors');
296 $inventoryline->qty_stock =
price2num(
GETPOST(
'stock_qty_'.$lineid,
'alpha'),
'MS');
297 $inventoryline->qty_view = $qtytoupdate;
298 $inventoryline->pmp_real =
price2num(
GETPOST(
'realpmp_'.$lineid,
'alpha'),
'MS');
299 $inventoryline->pmp_expected =
price2num(
GETPOST(
'expectedpmp_'.$lineid,
'alpha'),
'MS');
300 $resultupdate = $inventoryline->update($user);
302 } elseif (GETPOSTISSET(
'id_' . $lineid)) {
304 $result = $inventoryline->fetch($lineid);
306 $inventoryline->qty_view =
null;
307 $inventoryline->pmp_real =
price2num(
GETPOST(
'realpmp_'.$lineid,
'alpha'),
'MS');
308 $inventoryline->pmp_expected =
price2num(
GETPOST(
'expectedpmp_'.$lineid,
'alpha'),
'MS');
309 $resultupdate = $inventoryline->update($user);
313 if ($result < 0 || $resultupdate < 0) {
323 $sqlupdate =
"UPDATE ".MAIN_DB_PREFIX.
"inventory";
324 $sqlupdate .=
" SET fk_user_modif = ".((int) $user->id);
325 $sqlupdate .=
" WHERE rowid = ".((int) $object->id);
326 $resqlupdate = $db->query($sqlupdate);
327 if (! $resqlupdate) {
340 $backurlforlist = DOL_URL_ROOT.
'/product/inventory/list.php';
341 $backtopage = DOL_URL_ROOT.
'/product/inventory/inventory.php?id='.$object->id.
'&page='.$page.$paramwithsearch;
344 include DOL_DOCUMENT_ROOT.
'/core/actions_addupdatedelete.inc.php';
347 include DOL_DOCUMENT_ROOT.
'/core/actions_dellink.inc.php';
350 include DOL_DOCUMENT_ROOT.
'/core/actions_printing.inc.php';
358 if (
GETPOST(
'addline',
'alpha')) {
360 if ($fk_warehouse <= 0) {
362 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Warehouse")),
null,
'errors');
364 if ($fk_product <= 0) {
366 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Product")),
null,
'errors');
370 setEventMessages($langs->trans(
"FieldCannotBeNegative", $langs->transnoentitiesnoconv(
"RealQty")),
null,
'errors');
372 if (!$error && isModEnabled(
'productbatch')) {
373 $tmpproduct =
new Product($db);
374 $result = $tmpproduct->fetch($fk_product);
376 if (empty($error) && $tmpproduct->status_batch>0 && empty($batch)) {
378 $langs->load(
"errors");
379 setEventMessages($langs->trans(
"ErrorProductNeedBatchNumber", $tmpproduct->ref),
null,
'errors');
381 if (empty($error) && $tmpproduct->status_batch==2 && !empty($batch) && $qty>1) {
383 $langs->load(
"errors");
384 setEventMessages($langs->trans(
"TooManyQtyForSerialNumber", $tmpproduct->ref, $batch),
null,
'errors');
386 if (empty($error) && empty($tmpproduct->status_batch) && !empty($batch)) {
388 $langs->load(
"errors");
389 setEventMessages($langs->trans(
"ErrorProductDoesNotNeedBatchNumber", $tmpproduct->ref),
null,
'errors');
394 $tmp->fk_inventory = $object->id;
395 $tmp->fk_warehouse = $fk_warehouse;
396 $tmp->fk_product = $fk_product;
397 $tmp->batch = $batch;
399 $tmp->qty_view = $qty;
401 $result = $tmp->create($user);
403 if ($db->lasterrno() ==
'DB_ERROR_RECORD_ALREADY_EXISTS') {
404 $langs->load(
"errors");
405 setEventMessages($langs->trans(
"ErrorRecordAlreadyExists"),
null,
'errors');
411 $_POST[
'batch'] =
'';
412 $_POST[
'qtytoadd'] =
'';
424$form =
new Form($db);
429llxHeader(
'', $langs->trans(
'Inventory'), $help_url);
432if ($object->id <= 0) {
438$res = $object->fetch_optionals();
446if ($action ==
'delete') {
447 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'DeleteInventory'), $langs->trans(
'ConfirmDeleteOrder'),
'confirm_delete',
'', 0, 1);
450if ($action ==
'deleteline') {
451 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&lineid='.$lineid.
'&page='.$page.$paramwithsearch, $langs->trans(
'DeleteLine'), $langs->trans(
'ConfirmDeleteLine'),
'confirm_deleteline',
'', 0, 1);
455if ($action ==
'clone') {
457 $formquestion = array();
458 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'ToClone'), $langs->trans(
'ConfirmCloneMyObject', $object->ref),
'confirm_clone', $formquestion,
'yes', 1);
462if ($action ==
'record') {
463 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&page='.$page.$paramwithsearch, $langs->trans(
'Close'), $langs->trans(
'ConfirmFinish'),
'update',
'', 0, 1);
468if ($action ==
'confirm_cancel') {
469 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'Cancel'), $langs->trans(
'ConfirmCancel'),
'cancel_record',
'', 0, 1);
473if ($action ==
'validate') {
474 $form =
new Form($db);
476 if (
getDolGlobalInt(
'INVENTORY_INCLUDE_SUB_WAREHOUSE') && !empty($object->fk_warehouse)) {
477 $formquestion = array(
478 array(
'type' =>
'checkbox',
'name' =>
'include_sub_warehouse',
'label' => $langs->trans(
"IncludeSubWarehouse"),
'value' => 1,
'size' =>
'10'),
480 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'ValidateInventory'), $langs->trans(
'IncludeSubWarehouseExplanation'),
'confirm_validate', $formquestion,
'', 1);
485$parameters = array(
'formConfirm' => $formconfirm,
'lineid' => $lineid);
486$reshook = $hookmanager->executeHooks(
'formConfirm', $parameters, $object, $action);
487if (empty($reshook)) {
488 $formconfirm .= $hookmanager->resPrint;
489} elseif ($reshook > 0) {
490 $formconfirm = $hookmanager->resPrint;
499$linkback =
'<a href="'.DOL_URL_ROOT.
'/product/inventory/list.php">'.$langs->trans(
"BackToList").
'</a>';
501$morehtmlref =
'<div class="refidno">';
541$morehtmlref .=
'</div>';
544dol_banner_tab($object,
'ref', $linkback, 1,
'ref',
'ref', $morehtmlref);
547print
'<div class="fichecenter">';
548print
'<div class="fichehalfleft">';
549print
'<div class="underbanner clearboth"></div>';
550print
'<table class="border centpercent tableforfield">'.
"\n";
553include DOL_DOCUMENT_ROOT.
'/core/tpl/commonfields_view.tpl.php';
556include DOL_DOCUMENT_ROOT.
'/core/tpl/extrafields_view.tpl.php';
564print
'<div class="clearboth"></div>';
568print
'<form id="formrecord" name="formrecord" method="POST" action="'.$_SERVER[
"PHP_SELF"].
'?page='.$page.
'&id='.$object->id.
'">';
569print
'<input type="hidden" name="token" value="'.newToken().
'">';
570print
'<input type="hidden" name="action" value="updateinventorylines">';
571print
'<input type="hidden" name="id" value="'.$object->id.
'">';
573 print
'<input type="hidden" name="backtopage" value="'.$backtopage.
'">';
578if ($action !=
'record') {
579 print
'<div class="tabsAction">'.
"\n";
580 $parameters = array();
581 $reshook = $hookmanager->executeHooks(
'addMoreActionsButtons', $parameters, $object, $action);
586 if (empty($reshook)) {
587 if ($object->status == Inventory::STATUS_DRAFT) {
588 if ($permissiontoadd) {
589 if (
getDolGlobalInt(
'INVENTORY_INCLUDE_SUB_WAREHOUSE') && !empty($object->fk_warehouse)) {
590 print
'<a class="butAction" href="'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&action=validate&token='.newToken().
'">'.$langs->trans(
"Validate").
' ('.$langs->trans(
"Start").
')</a>';
592 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>';
595 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'Validate').
' ('.$langs->trans(
"Start").
')</a>'.
"\n";
600 if ($object->status == $object::STATUS_VALIDATED) {
601 if ($permissiontoadd) {
602 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";
604 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'MakeMovementsAndClose').
'</a>'.
"\n";
607 if ($permissiontoadd) {
608 print
'<a class="butActionDelete" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=confirm_cancel&page='.$page.$paramwithsearch.
'&token='.newToken().
'">'.$langs->trans(
"Cancel").
'</a>'.
"\n";
614 if ($object->status != Inventory::STATUS_DRAFT && $object->status != Inventory::STATUS_VALIDATED) {
621if ($object->status == Inventory::STATUS_VALIDATED) {
623 if (!empty($conf->use_javascript_ajax)) {
624 if ($permissiontoadd) {
626 if (isModEnabled(
'barcode') || isModEnabled(
'productbatch')) {
627 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>';
631 print
'<a id="fillwithexpected" class="marginrightonly paddingright marginleftonly paddingleft" href="#">'.img_picto(
'',
'autofill',
'class="paddingrightonly"').$langs->trans(
'AutofillWithExpected').
'</a>';
633 print
'$( document ).ready(function() {';
634 print
' $("#fillwithexpected").on("click",function fillWithExpected(){
635 $(".expectedqty").each(function(){
636 var object = $(this)[0];
637 var objecttofill = $("#"+object.id+"_input")[0];
638 objecttofill.value = object.innerText;
639 jQuery(".realqty").trigger("change");
641 console.log("Values filled (after click on fillwithexpected)");
642 /* disablebuttonmakemovementandclose(); */
649 print
'<a href="#" id="clearqty" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
'',
'eraser',
'class="paddingrightonly"').$langs->trans(
"ClearQtys").
'</a>';
651 print
'<a class="classfortooltip marginrightonly paddingright marginleftonly paddingleft" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
"Save").
'</a>'.
"\n";
661if ($action ==
'updatebyscaning') {
662 if ($permissiontoadd) {
667 var duplicatedbatchcode = [];
673 function barcodescannerjs(){
674 console.log("We catch inputs in scanner box");
675 jQuery("#scantoolmessage").text();
677 var selectaddorreplace = $("select[name=selectaddorreplace]").val();
678 var barcodemode = $("input[name=barcodemode]:checked").val();
679 var barcodeproductqty = $("input[name=barcodeproductqty]").val();
680 var textarea = $("textarea[name=barcodelist]").val();
681 var textarray = textarea.split(/[\s,;]+/);
683 duplicatedbatchcode = [];
689 textarray = textarray.filter(function(value){
692 if(textarray.some((element) => element != "")){
693 $(".expectedqty").each(function(){
695 console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
696 warehouse = $("#"+id+"_warehouse").attr(\'data-ref\');
697 //console.log(warehouse);
698 productbarcode = $("#"+id+"_product").attr(\'data-barcode\');
699 //console.log(productbarcode);
700 productbatchcode = $("#"+id+"_batch").attr(\'data-batch\');
701 //console.log(productbatchcode);
703 if (barcodemode != "barcodeforproduct") {
704 tabproduct.forEach(product=>{
705 console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
706 if(product.Batch != "" && product.Batch == productbatchcode){
707 console.log("duplicate batch code found for batch code "+productbatchcode);
708 duplicatedbatchcode.push(productbatchcode);
712 productinput = $("#"+id+"_input").val();
713 if(productinput == ""){
716 tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
719 console.log("Loop on each record entered in the textarea");
720 textarray.forEach(function(element,index){
721 console.log("Process record element="+element+" id="+id);
722 var verify_batch = false;
723 var verify_barcode = false;
725 case "barcodeforautodetect":
726 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode",true);
727 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial",true);
729 case "barcodeforproduct":
730 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode");
732 case "barcodeforlotserial":
733 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial");
736 alert(\''.dol_escape_js($langs->trans(
"ErrorWrongBarcodemode")).
' "\'+barcodemode+\'"\');
737 throw \''.
dol_escape_js($langs->trans(
'ErrorWrongBarcodemode')).
' "\'+barcodemode+\'"\';
740 if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
741 errortab2.push(element);
742 } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
743 errortab3.push(element);
744 } else if (verify_batch == true) {
745 console.log("element="+element);
746 console.log(duplicatedbatchcode);
747 if (duplicatedbatchcode.includes(element)) {
748 errortab1.push(element);
753 if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
754 tabproduct.forEach(product => {
756 console.log("We change #"+product.Id+"_input to match input in scanner box");
757 if(product.hasOwnProperty("reelqty")){
758 $.ajax({ url: \''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
759 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},
762 success: function(response) {
763 response = JSON.parse(response);
764 if(response.status == "success"){
765 console.log(response.message);
766 $("<input type=\'text\' value=\'"+product.Qty+"\' />")
767 .attr("id", "id_"+response.id_line+"_input")
768 .attr("name", "id_"+response.id_line)
769 .appendTo("#formrecord");
771 console.error(response.message);
774 error : function(output) {
775 console.error("Error on line creation function");
779 $("#"+product.Id+"_input").val(product.Qty);
783 jQuery("#scantoolmessage").text("'.
dol_escape_js($langs->transnoentities(
"QtyWasAddedToTheScannedBarcode")).
'\n");
784 /* document.forms["formrecord"].submit(); */
786 let stringerror = "";
787 if (Object.keys(errortab1).length > 0) {
788 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorSameBatchNumber')).
': ";
789 errortab1.forEach(element => {
790 stringerror += (element + ", ")
792 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
794 if (Object.keys(errortab2).length > 0) {
795 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCantFindCodeInInventory')).
': ";
796 errortab2.forEach(element => {
797 stringerror += (element + ", ")
799 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
801 if (Object.keys(errortab3).length > 0) {
802 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCodeScannedIsBothProductAndSerial')).
': ";
803 errortab3.forEach(element => {
804 stringerror += (element + ", ")
806 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
808 if (Object.keys(errortab4).length > 0) {
809 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorBarcodeNotFoundForProductWarehouse')).
': ";
810 errortab4.forEach(element => {
811 stringerror += (element + ", ")
813 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
816 jQuery("#scantoolmessage").html(\''.
dol_escape_js($langs->transnoentities(
"ErrorOnElementsInventory")).
'\' + stringerror);
824 function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,mode,autodetect=
false){
825 BarcodeIsInProduct=0;
828 tabproduct.forEach(product => {
829 $.ajax({ url: \
''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
830 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},
833 success: function(response) {
834 response = JSON.parse(response);
835 if (response.status == "success"){
836 console.log(response.message);
838 newproductrow = response.object;
841 if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)){
842 errortab4.push(element);
843 console.error(response.message);
847 error : function(output) {
848 console.error("Error on barcodeserialforproduct function");
851 console.log("Product "+(index+=1)+": "+element);
852 if(mode == "barcode"){
853 testonproduct = product.Barcode
854 }else if (mode == "lotserial"){
855 testonproduct = product.Batch
857 if(testonproduct == element){
858 if(selectaddorreplace == "add"){
859 productqty = parseInt(product.Qty,10);
860 product.Qty = productqty + parseInt(barcodeproductqty,10);
861 }else if(selectaddorreplace == "replace"){
862 if(product.fetched == false){
863 product.Qty = barcodeproductqty
866 productqty = parseInt(product.Qty,10);
867 product.Qty = productqty + parseInt(barcodeproductqty,10);
870 BarcodeIsInProduct+=1;
873 if(BarcodeIsInProduct==0 && newproductrow!=0){
874 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});
877 if(BarcodeIsInProduct > 0){
885 include DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
887 print $formother->getHTMLScannerForm(
"barcodescannerjs",
'all');
892print
'jQuery(document).ready(function() {
893 $("#clearqty").on("click", function() {
894 console.log("Clear all values");
895 /* disablebuttonmakemovementandclose(); */
896 jQuery(".realqty").val("");
897 jQuery(".realqty").trigger("change");
898 return false; /* disable submit */
900 $(".undochangesqty").on("click", function undochangesqty() {
901 console.log("Clear value of inventory line");
903 id = id.split("_")[1];
904 tmpvalue = $("#id_"+id+"_input_tmp").val()
905 $("#id_"+id+"_input")[0].value = tmpvalue;
906 /* disablebuttonmakemovementandclose(); */
907 return false; /* disable submit */
912print
'<div class="fichecenter">';
914print
'<div class="clearboth"></div>';
918print
'<div class="div-table-responsive-no-min">';
919print
'<table id="tablelines" class="noborder noshadow centpercent">';
921print
'<tr class="liste_titre">';
922print
'<td>'.$langs->trans(
"Warehouse").
'</td>';
923print
'<td>'.$langs->trans(
"Product").
'</td>';
924if (isModEnabled(
'productbatch')) {
926 print $langs->trans(
"Batch");
929print
'<td class="right">'.$langs->trans(
"ExpectedQty").
'</td>';
931 print
'<td class="right">'.$langs->trans(
'PMPExpected').
'</td>';
932 print
'<td class="right">'.$langs->trans(
'ExpectedValuation').
'</td>';
933 print
'<td class="right">'.$form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp")).
'</td>';
934 print
'<td class="right">'.$langs->trans(
'PMPReal').
'</td>';
935 print
'<td class="right">'.$langs->trans(
'RealValuation').
'</td>';
937 print
'<td class="right">';
938 print $form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp"));
941if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
943 print
'<td class="center">';
947 print
'<td class="right">';
954if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
957 print $formproduct->selectWarehouses((GETPOSTISSET(
'fk_warehouse') ?
GETPOST(
'fk_warehouse',
'int') : $object->fk_warehouse),
'fk_warehouse',
'warehouseopen', 1, 0, 0,
'', 0, 0, array(),
'maxwidth300');
960 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');
962 if (isModEnabled(
'productbatch')) {
964 print
'<input type="text" name="batch" class="maxwidth100" value="'.(GETPOSTISSET(
'batch') ?
GETPOST(
'batch') :
'').
'">';
967 print
'<td class="right"></td>';
969 print
'<td class="right">';
971 print
'<td class="right">';
973 print
'<td class="right">';
974 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
976 print
'<td class="right">';
978 print
'<td class="right">';
981 print
'<td class="right">';
982 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
986 print
'<td class="center">';
987 print
'<input type="submit" class="button paddingright" name="addline" value="'.$langs->trans(
"Add").
'">';
993$sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
994$sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.fk_movement, id.pmp_real, id.pmp_expected';
995$sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
996$sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
997$sql .= $db->order(
'id.rowid',
'ASC');
998$sql .= $db->plimit($limit, $offset);
1000$cacheOfProducts = array();
1001$cacheOfWarehouses = array();
1004$resql = $db->query($sql);
1006 $num = $db->num_rows($resql);
1008 if (!empty($limit != 0) || $num > $limit || $page) {
1009 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);
1014 $totalarray = array();
1016 $obj = $db->fetch_object($resql);
1018 if (isset($cacheOfWarehouses[$obj->fk_warehouse])) {
1019 $warehouse_static = $cacheOfWarehouses[$obj->fk_warehouse];
1021 $warehouse_static =
new Entrepot($db);
1022 $warehouse_static->fetch($obj->fk_warehouse);
1024 $cacheOfWarehouses[$warehouse_static->id] = $warehouse_static;
1029 if (isset($cacheOfProducts[$obj->fk_product])) {
1030 $product_static = $cacheOfProducts[$obj->fk_product];
1032 $product_static =
new Product($db);
1033 $result = $product_static->fetch($obj->fk_product,
'',
'',
'', 1, 1, 1);
1036 $option .=
',novirtual';
1037 $product_static->load_stock($option);
1039 $cacheOfProducts[$product_static->id] = $product_static;
1042 print
'<tr class="oddeven">';
1043 print
'<td id="id_'.$obj->rowid.
'_warehouse" data-ref="'.
dol_escape_htmltag($warehouse_static->ref).
'">';
1044 print $warehouse_static->getNomUrl(1);
1047 print $product_static->getNomUrl(1).
' - '.$product_static->label;
1050 if (isModEnabled(
'productbatch')) {
1051 print
'<td id="id_'.$obj->rowid.
'_batch" data-batch="'.
dol_escape_htmltag($obj->batch).
'">';
1053 $res = $batch_static->fetch(0, $product_static->id, $obj->batch);
1055 print $batch_static->getNomUrl(1);
1063 print
'<td class="right expectedqty" id="id_'.$obj->rowid.
'" title="Stock viewed at last update: '.$obj->qty_stock.
'">';
1064 $valuetoshow = $obj->qty_stock;
1066 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
1067 if (isModEnabled(
'productbatch') && $product_static->hasbatch()) {
1068 $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->detail_batch[$obj->batch]->qty ?? 0;
1070 $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->real ?? 0;
1074 print
'<input type="hidden" name="stock_qty_'.$obj->rowid.
'" value="'.$valuetoshow.
'">';
1078 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
1082 if ($qty_view !=
'') {
1088 if (!empty($obj->pmp_expected)) {
1089 $pmp_expected = $obj->pmp_expected;
1091 $pmp_expected = $product_static->pmp;
1093 $pmp_valuation = $pmp_expected * $valuetoshow;
1094 print
'<td class="right">';
1095 print
price($pmp_expected);
1096 print
'<input type="hidden" name="expectedpmp_'.$obj->rowid.
'" value="'.$pmp_expected.
'"/>';
1098 print
'<td class="right">';
1099 print
price($pmp_valuation);
1102 print
'<td class="right">';
1103 print
'<a id="undochangesqty_'.$obj->rowid.
'" href="#" class="undochangesqty reposition marginrightonly" title="'.
dol_escape_htmltag($langs->trans(
"Clear")).
'">';
1104 print
img_picto(
'',
'eraser',
'class="opacitymedium"');
1106 print
'<input type="text" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input" value="'.$qty_view.
'">';
1110 print
'<td class="right">';
1111 if (!empty($obj->pmp_real) || (
string) $obj->pmp_real ===
'0') {
1112 $pmp_real = $obj->pmp_real;
1114 $pmp_real = $product_static->pmp;
1116 $pmp_valuation_real = $pmp_real * $qty_view;
1117 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).
'">';
1119 print
'<td class="right">';
1120 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.
'">';
1123 $totalExpectedValuation += $pmp_valuation;
1124 $totalRealValuation += $pmp_valuation_real;
1126 print
'<td class="right">';
1127 print
'<a id="undochangesqty_'.$obj->rowid.
'" href="#" class="undochangesqty reposition marginrightonly" title="'.
dol_escape_htmltag($langs->trans(
"Clear")).
'">';
1128 print
img_picto(
'',
'eraser',
'class="opacitymedium"');
1130 print
'<input type="text" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input" value="'.$qty_view.
'">';
1135 print
'<td class="right">';
1136 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>';
1137 $qty_tmp =
price2num(
GETPOST(
"id_".$obj->rowid.
"_input_tmp",
'MS')) >= 0 ?
GETPOST(
"id_".$obj->rowid.
"_input_tmp") : $qty_view;
1138 print
'<input type="hidden" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'_input_tmp" id="id_'.$obj->rowid.
'_input_tmp" value="'.$qty_tmp.
'">';
1143 if (!empty($obj->pmp_expected)) {
1144 $pmp_expected = $obj->pmp_expected;
1146 $pmp_expected = $product_static->pmp;
1148 $pmp_valuation = $pmp_expected * $valuetoshow;
1149 print
'<td class="right">';
1150 print
price($pmp_expected);
1152 print
'<td class="right">';
1153 print
price($pmp_valuation);
1156 print
'<td class="right nowraponall">';
1157 print $obj->qty_view;
1161 print
'<td class="right">';
1162 if (!empty($obj->pmp_real)) {
1163 $pmp_real = $obj->pmp_real;
1165 $pmp_real = $product_static->pmp;
1167 $pmp_valuation_real = $pmp_real * $obj->qty_view;
1168 print
price($pmp_real);
1170 print
'<td class="right">';
1171 print
price($pmp_valuation_real);
1173 print
'<td class="nowraponall right">';
1175 $totalExpectedValuation += $pmp_valuation;
1176 $totalRealValuation += $pmp_valuation_real;
1178 print
'<td class="right nowraponall">';
1179 print $obj->qty_view;
1183 if ($obj->fk_movement > 0) {
1185 $stockmovment->fetch($obj->fk_movement);
1186 print $stockmovment->getNomUrl(1,
'movements');
1198 print
'<tr class="liste_total">';
1199 print
'<td colspan="4">'.$langs->trans(
"Total").
'</td>';
1200 print
'<td class="right" colspan="2">'.price($totalExpectedValuation).
'</td>';
1201 print
'<td class="right" id="totalRealValuation" colspan="3">'.price($totalRealValuation).
'</td>';
1209if ($object->status == $object::STATUS_VALIDATED) {
1210 print
'<center><input id="submitrecord" type="submit" class="button button-save" name="save" value="'.$langs->trans(
"Save").
'"></center>';
1230print
'<script type="text/javascript">
1231 $(document).ready(function() {
1233 $(".paginationnext:last").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.
'";
1246 $(".paginationprevious:last").click(function(e){
1247 var form = $("#formrecord");
1248 var actionURL = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page).$paramwithsearch.
'";
1251 data: form.serialize(),
1253 success: function(result){
1254 window.location.href = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page - 1).$paramwithsearch.
'";
1258 $("#idbuttonmakemovementandclose").click(function(e){
1259 var form = $("#formrecord");
1260 var actionURL = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page).$paramwithsearch.
'";
1263 data: form.serialize(),
1265 success: function(result){
1266 window.location.href = "'.$_SERVER[
'PHP_SELF'].
'?id='.$object->id.
'&page='.($page - 1).$paramwithsearch.
'&action=record";
1275<script
type=
"text/javascript">
1276$(
'.realqty').on(
'change',
function () {
1277 let realqty = $(
this).closest(
'tr').find(
'.realqty').val();
1278 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1279 let realpmp = $(inputPmp).val();
1280 if (!isNaN(realqty) && !isNaN(realpmp)) {
1281 let realval = realqty * realpmp;
1282 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1284 updateTotalValuation();
1287$(
'input[class*=realpmp]').on(
'change',
function () {
1288 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1289 let realqty = $(inputQtyReal).val();
1290 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1291 console.log(inputPmp);
1292 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1293 let realpmp = $(inputPmp).val();
1294 if (!isNaN(realpmp)) {
1295 $(
'.'+realPmpClassname).val(realpmp);
1297 if (!isNaN(realqty)) {
1298 let realval = realqty * realpmp;
1299 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1301 $(
'.realqty').trigger(
'change');
1302 updateTotalValuation();
1306$(
'input[name^=realvaluation]').on(
'change',
function () {
1307 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1308 let realqty = $(inputQtyReal).val();
1309 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1310 let inputRealValuation = $(
this).closest(
'tr').find(
'input[name^=realvaluation]');
1311 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1312 let realvaluation = $(inputRealValuation).val();
1313 if (!isNaN(realvaluation) && !isNaN(realqty) && realvaluation !==
'' && realqty !==
'' && realqty !== 0) {
1314 let realpmp = realvaluation / realqty
1315 $(
'.'+realPmpClassname).val(realpmp);
1316 $(
'.realqty').trigger(
'change');
1317 updateTotalValuation();
1321function updateTotalValuation() {
1323 $(
'input[name^=realvaluation]').each(
function( index ) {
1324 let val = $(
this).val();
1325 if(!isNaN(val)) total += parseFloat($(
this).val());
1327 let currencyFractionDigits =
new Intl.NumberFormat(
'fr-FR', {
1330 }).resolvedOptions().maximumFractionDigits;
1331 $(
'#totalRealValuation').html(total.toLocaleString(
'fr-FR', {
1332 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.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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.