25 require
'../../main.inc.php';
26 include_once DOL_DOCUMENT_ROOT.
'/core/class/html.formcompany.class.php';
27 include_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
28 include_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
29 include_once DOL_DOCUMENT_ROOT.
'/product/inventory/class/inventory.class.php';
30 include_once DOL_DOCUMENT_ROOT.
'/product/inventory/lib/inventory.lib.php';
31 include_once DOL_DOCUMENT_ROOT.
'/product/stock/class/mouvementstock.class.php';
32 include_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;
48 if (empty($page) || $page == -1) {
51 $offset = $limit * $page;
52 $pageprev = $page - 1;
53 $pagenext = $page + 1;
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;
61 if (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');
81 foreach ($object->fields as $key => $val) {
82 if (
GETPOST(
'search_'.$key,
'alpha')) {
83 $search[$key] =
GETPOST(
'search_'.$key,
'alpha');
87 if (empty($action) && empty($id) && empty($ref)) {
92 include DOL_DOCUMENT_ROOT.
'/core/actions_fetchobject.inc.php';
100 $param =
'&id='.$object->id;
101 if ($limit > 0 && $limit != $conf->liste_limit) {
102 $param .=
'&limit='.urlencode($limit);
104 $paramwithsearch = $param;
107 if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
108 $permissiontoadd = $user->rights->stock->creer;
109 $permissiontodelete = $user->rights->stock->supprimer;
111 $permissiontoadd = $user->rights->stock->inventory_advance->write;
112 $permissiontodelete = $user->rights->stock->inventory_advance->write;
128 $parameters = array();
129 $reshook = $hookmanager->executeHooks(
'doActions', $parameters, $object, $action);
134 if (empty($reshook)) {
137 if ($action ==
'cancel_record' && $permissiontoadd) {
138 $object->setCanceled($user);
142 if ($action ==
'update' && !empty($user->rights->stock->mouvement->creer) && $object->status == $object::STATUS_VALIDATED) {
144 $stockmovment->setOrigin($object->element, $object->id);
146 $cacheOfProducts = array();
150 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
151 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.pmp_real';
152 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
153 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
155 $resql = $db->query($sql);
157 $num = $db->num_rows(
$resql);
159 $totalarray = array();
161 $line = $db->fetch_object(
$resql);
163 $qty_stock = $line->qty_stock;
164 $qty_view = $line->qty_view;
168 if (isset($cacheOfProducts[$line->fk_product])) {
169 $product_static = $cacheOfProducts[$line->fk_product];
171 $product_static =
new Product($db);
172 $result = $product_static->fetch($line->fk_product,
'',
'',
'', 1, 1, 1);
175 $option .=
',novirtual';
176 $product_static->load_stock($option);
178 $cacheOfProducts[$product_static->id] = $product_static;
182 $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->real;
183 if (
isModEnabled(
'productbatch') && $product_static->hasbatch()) {
184 $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->detail_batch[$line->batch]->qty;
188 if (!is_null($qty_view)) {
189 $stock_movement_qty =
price2num($qty_view - $realqtynow,
'MS');
190 if ($stock_movement_qty != 0) {
191 if ($stock_movement_qty < 0) {
199 $inventorycode =
'INV-'.$object->ref;
201 if (!empty($line->pmp_real) && !empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) $price = $line->pmp_real;
203 $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);
204 if ($idstockmove < 0) {
211 $sqlupdate =
"UPDATE ".MAIN_DB_PREFIX.
"inventorydet";
212 $sqlupdate .=
" SET fk_movement = ".((int) $idstockmove);
213 if ($qty_stock != $realqtynow) {
214 $sqlupdate .=
", qty_stock = ".((float) $realqtynow);
216 $sqlupdate .=
" WHERE rowid = ".((int) $line->rowid);
217 $resqlupdate = $db->query($sqlupdate);
218 if (! $resqlupdate) {
225 if (!empty($line->pmp_real) && !empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
226 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product SET pmp = '.((
float) $line->pmp_real).
' WHERE rowid = '.((int) $line->fk_product);
227 $resqlpmp = $db->query($sqlpmp);
233 if (!empty($conf->global->MAIN_PRODUCT_PERENTITY_SHARED)) {
234 $sqlpmp =
'UPDATE '.MAIN_DB_PREFIX.
'product_perentity SET pmp = '.((
float) $line->pmp_real).
' WHERE fk_product = '.((int) $line->fk_product).
' AND entity='.$conf->entity;
235 $resqlpmp = $db->query($sqlpmp);
248 $object->setRecorded($user);
263 if ($action ==
'updateinventorylines' && $permissiontoadd) {
264 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
265 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
266 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
267 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
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);
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');
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'] =
'';
431 if ($object->id <= 0) {
437 $res = $object->fetch_optionals();
440 print
dol_get_fiche_head($head,
'inventory', $langs->trans(
"Inventory"), -1,
'stock');
445 if ($action ==
'delete') {
446 $formconfirm =
$form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'DeleteInventory'), $langs->trans(
'ConfirmDeleteOrder'),
'confirm_delete',
'', 0, 1);
449 if ($action ==
'deleteline') {
450 $formconfirm =
$form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&lineid='.$lineid.
'&page='.$page.$paramwithsearch, $langs->trans(
'DeleteLine'), $langs->trans(
'ConfirmDeleteLine'),
'confirm_deleteline',
'', 0, 1);
454 if ($action ==
'clone') {
456 $formquestion = array();
457 $formconfirm =
$form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'ToClone'), $langs->trans(
'ConfirmCloneMyObject', $object->ref),
'confirm_clone', $formquestion,
'yes', 1);
461 if ($action ==
'record') {
462 $formconfirm =
$form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'Close'), $langs->trans(
'ConfirmFinish'),
'update',
'', 0, 1);
467 if ($action ==
'confirm_cancel') {
468 $formconfirm =
$form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.$object->id, $langs->trans(
'Cancel'), $langs->trans(
'ConfirmCancel'),
'cancel_record',
'', 0, 1);
473 $parameters = array(
'formConfirm' =>
$formconfirm,
'lineid' => $lineid);
474 $reshook = $hookmanager->executeHooks(
'formConfirm', $parameters, $object, $action);
475 if (empty($reshook)) {
477 } elseif ($reshook > 0) {
487 $linkback =
'<a href="'.DOL_URL_ROOT.
'/product/inventory/list.php">'.$langs->trans(
"BackToList").
'</a>';
489 $morehtmlref =
'<div class="refidno">';
529 $morehtmlref .=
'</div>';
532 dol_banner_tab($object,
'ref', $linkback, 1,
'ref',
'ref', $morehtmlref);
535 print
'<div class="fichecenter">';
536 print
'<div class="fichehalfleft">';
537 print
'<div class="underbanner clearboth"></div>';
538 print
'<table class="border centpercent tableforfield">'.
"\n";
541 include DOL_DOCUMENT_ROOT.
'/core/tpl/commonfields_view.tpl.php';
544 include DOL_DOCUMENT_ROOT.
'/core/tpl/extrafields_view.tpl.php';
552 print
'<div class="clearboth"></div>';
556 print
'<form id="formrecord" name="formrecord" method="POST" action="'.$_SERVER[
"PHP_SELF"].
'?page='.$page.
'&id='.$object->id.
'">';
557 print
'<input type="hidden" name="token" value="'.newToken().
'">';
558 print
'<input type="hidden" name="action" value="updateinventorylines">';
559 print
'<input type="hidden" name="id" value="'.$object->id.
'">';
561 print
'<input type="hidden" name="backtopage" value="'.$backtopage.
'">';
566 if ($action !=
'record') {
567 print
'<div class="tabsAction">'.
"\n";
568 $parameters = array();
569 $reshook = $hookmanager->executeHooks(
'addMoreActionsButtons', $parameters, $object, $action);
574 if (empty($reshook)) {
575 if ($object->status == Inventory::STATUS_DRAFT) {
576 if ($permissiontoadd) {
577 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";
579 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'Validate').
' ('.$langs->trans(
"Start").
')</a>'.
"\n";
584 if ($object->status == $object::STATUS_VALIDATED) {
585 if ($permissiontoadd) {
586 print
'<a class="butAction classfortooltip" id="idbuttonmakemovementandclose" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=record&token='.
newToken().
'" title="'.
dol_escape_htmltag($langs->trans(
"MakeMovementsAndClose")).
'">'.$langs->trans(
"MakeMovementsAndClose").
'</a>'.
"\n";
588 print
'<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
'MakeMovementsAndClose').
'</a>'.
"\n";
591 if ($permissiontoadd) {
592 print
'<a class="butActionDelete" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=confirm_cancel&token='.
newToken().
'">'.$langs->trans(
"Cancel").
'</a>'.
"\n";
598 if ($object->status != Inventory::STATUS_DRAFT && $object->status != Inventory::STATUS_VALIDATED) {
605 if ($object->status == Inventory::STATUS_VALIDATED) {
607 if (!empty($conf->use_javascript_ajax)) {
608 if ($permissiontoadd) {
611 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>';
615 print
'<a id="fillwithexpected" class="marginrightonly paddingright marginleftonly paddingleft" href="#">'.img_picto(
'',
'autofill',
'class="paddingrightonly"').$langs->trans(
'AutofillWithExpected').
'</a>';
617 print
'$( document ).ready(function() {';
618 print
' $("#fillwithexpected").on("click",function fillWithExpected(){
619 $(".expectedqty").each(function(){
620 var object = $(this)[0];
621 var objecttofill = $("#"+object.id+"_input")[0];
622 objecttofill.value = object.innerText;
623 jQuery(".realqty").trigger("change");
625 console.log("Values filled (after click on fillwithexpected)");
626 /* disablebuttonmakemovementandclose(); */
633 print
'<a href="#" id="clearqty" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
'',
'eraser',
'class="paddingrightonly"').$langs->trans(
"ClearQtys").
'</a>';
635 print
'<a class="classfortooltip marginrightonly paddingright marginleftonly paddingleft" href="#" title="'.dol_escape_htmltag($langs->trans(
"NotEnoughPermissions")).
'">'.$langs->trans(
"Save").
'</a>'.
"\n";
645 if ($action ==
'updatebyscaning') {
646 if ($permissiontoadd) {
651 var duplicatedbatchcode = [];
657 function barcodescannerjs(){
658 console.log("We catch inputs in scanner box");
659 jQuery("#scantoolmessage").text();
661 var selectaddorreplace = $("select[name=selectaddorreplace]").val();
662 var barcodemode = $("input[name=barcodemode]:checked").val();
663 var barcodeproductqty = $("input[name=barcodeproductqty]").val();
664 var textarea = $("textarea[name=barcodelist]").val();
665 var textarray = textarea.split(/[\s,;]+/);
667 duplicatedbatchcode = [];
673 textarray = textarray.filter(function(value){
676 if(textarray.some((element) => element != "")){
677 $(".expectedqty").each(function(){
679 console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
680 warehouse = $("#"+id+"_warehouse").attr(\'data-ref\');
681 //console.log(warehouse);
682 productbarcode = $("#"+id+"_product").attr(\'data-barcode\');
683 //console.log(productbarcode);
684 productbatchcode = $("#"+id+"_batch").attr(\'data-batch\');
685 //console.log(productbatchcode);
687 if (barcodemode != "barcodeforproduct") {
688 tabproduct.forEach(product=>{
689 console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
690 if(product.Batch != "" && product.Batch == productbatchcode){
691 console.log("duplicate batch code found for batch code "+productbatchcode);
692 duplicatedbatchcode.push(productbatchcode);
696 productinput = $("#"+id+"_input").val();
697 if(productinput == ""){
700 tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
703 console.log("Loop on each record entered in the textarea");
704 textarray.forEach(function(element,index){
705 console.log("Process record element="+element+" id="+id);
706 var verify_batch = false;
707 var verify_barcode = false;
709 case "barcodeforautodetect":
710 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode",true);
711 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial",true);
713 case "barcodeforproduct":
714 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode");
716 case "barcodeforlotserial":
717 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial");
720 alert(\''.dol_escape_js($langs->trans(
"ErrorWrongBarcodemode")).
' "\'+barcodemode+\'"\');
721 throw \''.
dol_escape_js($langs->trans(
'ErrorWrongBarcodemode')).
' "\'+barcodemode+\'"\';
724 if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
725 errortab2.push(element);
726 } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
727 errortab3.push(element);
728 } else if (verify_batch == true) {
729 console.log("element="+element);
730 console.log(duplicatedbatchcode);
731 if (duplicatedbatchcode.includes(element)) {
732 errortab1.push(element);
737 if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
738 tabproduct.forEach(product => {
740 console.log("We change #"+product.Id+"_input to match input in scanner box");
741 if(product.hasOwnProperty("reelqty")){
742 $.ajax({ url: \''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
743 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},
746 success: function(response) {
747 response = JSON.parse(response);
748 if(response.status == "success"){
749 console.log(response.message);
750 $("<input type=\'text\' value=\'"+product.Qty+"\' />")
751 .attr("id", "id_"+response.id_line+"_input")
752 .attr("name", "id_"+response.id_line)
753 .appendTo("#formrecord");
755 console.error(response.message);
758 error : function(output) {
759 console.error("Error on line creation function");
763 $("#"+product.Id+"_input").val(product.Qty);
767 jQuery("#scantoolmessage").text("'.
dol_escape_js($langs->transnoentities(
"QtyWasAddedToTheScannedBarcode")).
'\n");
768 /* document.forms["formrecord"].submit(); */
770 let stringerror = "";
771 if (Object.keys(errortab1).length > 0) {
772 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorSameBatchNumber')).
': ";
773 errortab1.forEach(element => {
774 stringerror += (element + ", ")
776 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
778 if (Object.keys(errortab2).length > 0) {
779 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCantFindCodeInInventory')).
': ";
780 errortab2.forEach(element => {
781 stringerror += (element + ", ")
783 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
785 if (Object.keys(errortab3).length > 0) {
786 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCodeScannedIsBothProductAndSerial')).
': ";
787 errortab3.forEach(element => {
788 stringerror += (element + ", ")
790 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
792 if (Object.keys(errortab4).length > 0) {
793 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorBarcodeNotFoundForProductWarehouse')).
': ";
794 errortab4.forEach(element => {
795 stringerror += (element + ", ")
797 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
800 jQuery("#scantoolmessage").html(\''.
dol_escape_js($langs->transnoentities(
"ErrorOnElementsInventory")).
'\' + stringerror);
808 function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,mode,autodetect=
false){
809 BarcodeIsInProduct=0;
812 tabproduct.forEach(product => {
813 $.ajax({ url: \
''.DOL_URL_ROOT.
'/product/inventory/ajax/searchfrombarcode.php\',
814 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},
817 success: function(response) {
818 response = JSON.parse(response);
819 if (response.status == "success"){
820 console.log(response.message);
822 newproductrow = response.object;
825 if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)){
826 errortab4.push(element);
827 console.error(response.message);
831 error : function(output) {
832 console.error("Error on barcodeserialforproduct function");
835 console.log("Product "+(index+=1)+": "+element);
836 if(mode == "barcode"){
837 testonproduct = product.Barcode
838 }else if (mode == "lotserial"){
839 testonproduct = product.Batch
841 if(testonproduct == element){
842 if(selectaddorreplace == "add"){
843 productqty = parseInt(product.Qty,10);
844 product.Qty = productqty + parseInt(barcodeproductqty,10);
845 }else if(selectaddorreplace == "replace"){
846 if(product.fetched == false){
847 product.Qty = barcodeproductqty
850 productqty = parseInt(product.Qty,10);
851 product.Qty = productqty + parseInt(barcodeproductqty,10);
854 BarcodeIsInProduct+=1;
857 if(BarcodeIsInProduct==0 && newproductrow!=0){
858 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});
861 if(BarcodeIsInProduct > 0){
869 include DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
871 print $formother->getHTMLScannerForm(
"barcodescannerjs",
'all');
876 print
'jQuery(document).ready(function() {
877 $("#clearqty").on("click", function() {
878 console.log("Clear all values");
879 /* disablebuttonmakemovementandclose(); */
880 jQuery(".realqty").val("");
881 jQuery(".realqty").trigger("change");
882 return false; /* disable submit */
884 $(".undochangesqty").on("click", function undochangesqty() {
885 console.log("Clear value of inventory line");
887 id = id.split("_")[1];
888 tmpvalue = $("#id_"+id+"_input_tmp").val()
889 $("#id_"+id+"_input")[0].value = tmpvalue;
890 /* disablebuttonmakemovementandclose(); */
891 return false; /* disable submit */
896 print
'<div class="fichecenter">';
898 print
'<div class="clearboth"></div>';
902 print
'<div class="div-table-responsive-no-min">';
903 print
'<table id="tablelines" class="noborder noshadow centpercent">';
905 print
'<tr class="liste_titre">';
906 print
'<td>'.$langs->trans(
"Warehouse").
'</td>';
907 print
'<td>'.$langs->trans(
"Product").
'</td>';
910 print $langs->trans(
"Batch");
913 print
'<td class="right">'.$langs->trans(
"ExpectedQty").
'</td>';
914 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
915 print
'<td class="right">'.$langs->trans(
'PMPExpected').
'</td>';
916 print
'<td class="right">'.$langs->trans(
'ExpectedValuation').
'</td>';
917 print
'<td class="right">'.$form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp")).
'</td>';
918 print
'<td class="right">'.$langs->trans(
'PMPReal').
'</td>';
919 print
'<td class="right">'.$langs->trans(
'RealValuation').
'</td>';
921 print
'<td class="right">';
922 print
$form->textwithpicto($langs->trans(
"RealQty"), $langs->trans(
"InventoryRealQtyHelp"));
925 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
927 print
'<td class="center">';
931 print
'<td class="right">';
938 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
941 print $formproduct->selectWarehouses((
GETPOSTISSET(
'fk_warehouse') ?
GETPOST(
'fk_warehouse',
'int') : $object->fk_warehouse),
'fk_warehouse',
'warehouseopen', 1, 0, 0,
'', 0, 0, array(),
'maxwidth300');
944 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');
948 print
'<input type="text" name="batch" class="maxwidth100" value="'.(GETPOSTISSET(
'batch') ?
GETPOST(
'batch') :
'').
'">';
951 print
'<td class="right"></td>';
952 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
953 print
'<td class="right">';
955 print
'<td class="right">';
957 print
'<td class="right">';
958 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
960 print
'<td class="right">';
962 print
'<td class="right">';
965 print
'<td class="right">';
966 print
'<input type="text" name="qtytoadd" class="maxwidth75" value="">';
970 print
'<td class="center">';
971 print
'<input type="submit" class="button paddingright" name="addline" value="'.$langs->trans(
"Add").
'">';
977 $sql =
'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
978 $sql .=
' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.fk_movement, id.pmp_real, id.pmp_expected';
979 $sql .=
' FROM '.MAIN_DB_PREFIX.
'inventorydet as id';
980 $sql .=
' WHERE id.fk_inventory = '.((int) $object->id);
981 $sql .= $db->order(
'id.rowid',
'ASC');
982 $sql .= $db->plimit($limit, $offset);
984 $cacheOfProducts = array();
985 $cacheOfWarehouses = array();
988 $resql = $db->query($sql);
990 $num = $db->num_rows(
$resql);
992 if (!empty($limit != 0) || $num > $limit || $page) {
993 print_fleche_navigation($page, $_SERVER[
"PHP_SELF"], $paramwithsearch, ($num >= $limit),
'<li class="pagination"><span>' . $langs->trans(
"Page") .
' ' . ($page + 1) .
'</span></li>',
'', $limit);
998 $totalarray = array();
1000 $obj = $db->fetch_object(
$resql);
1002 if (isset($cacheOfWarehouses[$obj->fk_warehouse])) {
1003 $warehouse_static = $cacheOfWarehouses[$obj->fk_warehouse];
1005 $warehouse_static =
new Entrepot($db);
1006 $warehouse_static->fetch($obj->fk_warehouse);
1008 $cacheOfWarehouses[$warehouse_static->id] = $warehouse_static;
1013 if (isset($cacheOfProducts[$obj->fk_product])) {
1014 $product_static = $cacheOfProducts[$obj->fk_product];
1016 $product_static =
new Product($db);
1017 $result = $product_static->fetch($obj->fk_product,
'',
'',
'', 1, 1, 1);
1020 $option .=
',novirtual';
1021 $product_static->load_stock($option);
1023 $cacheOfProducts[$product_static->id] = $product_static;
1026 print
'<tr class="oddeven">';
1027 print
'<td id="id_'.$obj->rowid.
'_warehouse" data-ref="'.
dol_escape_htmltag($warehouse_static->ref).
'">';
1028 print $warehouse_static->getNomUrl(1);
1031 print $product_static->getNomUrl(1).
' - '.$product_static->label;
1035 print
'<td id="id_'.$obj->rowid.
'_batch" data-batch="'.
dol_escape_htmltag($obj->batch).
'">';
1037 $res = $batch_static->fetch(0, $product_static->id, $obj->batch);
1039 print $batch_static->getNomUrl(1);
1047 print
'<td class="right expectedqty" id="id_'.$obj->rowid.
'" title="Stock viewed at last update: '.$obj->qty_stock.
'">';
1048 $valuetoshow = $obj->qty_stock;
1050 if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
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">';
1094 if (!empty($obj->pmp_real)) $pmp_real = $obj->pmp_real;
1095 else $pmp_real = $product_static->pmp;
1096 $pmp_valuation_real = $pmp_real * $qty_view;
1097 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).
'">';
1099 print
'<td class="right">';
1100 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.
'">';
1103 $totalExpectedValuation += $pmp_valuation;
1104 $totalRealValuation += $pmp_valuation_real;
1106 print
'<td class="right">';
1107 print
'<a id="undochangesqty_'.$obj->rowid.
'" href="#" class="undochangesqty reposition marginrightonly" title="'.
dol_escape_htmltag($langs->trans(
"Clear")).
'">';
1108 print
img_picto(
'',
'eraser',
'class="opacitymedium"');
1110 print
'<input type="text" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'" id="id_'.$obj->rowid.
'_input" value="'.$qty_view.
'">';
1115 print
'<td class="right">';
1116 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>';
1117 $qty_tmp =
price2num(
GETPOST(
"id_".$obj->rowid.
"_input_tmp",
'MS')) >= 0 ?
GETPOST(
"id_".$obj->rowid.
"_input_tmp") : $qty_view;
1118 print
'<input type="hidden" class="maxwidth50 right realqty" name="id_'.$obj->rowid.
'_input_tmp" id="id_'.$obj->rowid.
'_input_tmp" value="'.$qty_tmp.
'">';
1121 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1123 if (!empty($obj->pmp_expected)) $pmp_expected = $obj->pmp_expected;
1124 else $pmp_expected = $product_static->pmp;
1125 $pmp_valuation = $pmp_expected * $valuetoshow;
1126 print
'<td class="right">';
1127 print
price($pmp_expected);
1129 print
'<td class="right">';
1130 print
price($pmp_valuation);
1133 print
'<td class="right nowraponall">';
1134 print $obj->qty_view;
1138 print
'<td class="right">';
1139 if (!empty($obj->pmp_real)) $pmp_real = $obj->pmp_real;
1140 else $pmp_real = $product_static->pmp;
1141 $pmp_valuation_real = $pmp_real * $obj->qty_view;
1142 print
price($pmp_real);
1144 print
'<td class="right">';
1145 print
price($pmp_valuation_real);
1147 print
'<td class="nowraponall right">';
1149 $totalExpectedValuation += $pmp_valuation;
1150 $totalRealValuation += $pmp_valuation_real;
1152 print
'<td class="right nowraponall">';
1153 print $obj->qty_view;
1157 if ($obj->fk_movement > 0) {
1159 $stockmovment->fetch($obj->fk_movement);
1160 print $stockmovment->getNomUrl(1,
'movements');
1171 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1172 print
'<tr class="liste_total">';
1173 print
'<td colspan="4">'.$langs->trans(
"Total").
'</td>';
1174 print
'<td class="right" colspan="2">'.price($totalExpectedValuation).
'</td>';
1175 print
'<td class="right" id="totalRealValuation" colspan="3">'.price($totalRealValuation).
'</td>';
1183 if ($object->status == $object::STATUS_VALIDATED) {
1184 print
'<center><input id="submitrecord" type="submit" class="button button-save" name="save" value="'.$langs->trans(
"Save").
'"></center>';
1204 print
'<script type="text/javascript">
1205 $(document).ready(function() {
1207 $(".paginationnext:last").click(function(e){
1208 var form = $("#formrecord");
1209 var actionURL = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page).$paramwithsearch.
'";
1212 data: form.serialize(),
1214 success: function(result){
1215 window.location.href = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page + 1).$paramwithsearch.
'";
1220 $(".paginationprevious:last").click(function(e){
1221 var form = $("#formrecord");
1222 var actionURL = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page).$paramwithsearch.
'";
1225 data: form.serialize(),
1227 success: function(result){
1228 window.location.href = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page - 1).$paramwithsearch.
'";
1232 $("#idbuttonmakemovementandclose").click(function(e){
1233 var form = $("#formrecord");
1234 var actionURL = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page).$paramwithsearch.
'";
1237 data: form.serialize(),
1239 success: function(result){
1240 window.location.href = "'.$_SERVER[
'PHP_SELF'].
"?page=".($page - 1).$paramwithsearch.
'&action=record";
1247 if (!empty($conf->global->INVENTORY_MANAGE_REAL_PMP)) {
1249 <script
type=
"text/javascript">
1250 $(
'.realqty').on(
'change',
function () {
1251 let realqty = $(
this).closest(
'tr').find(
'.realqty').val();
1252 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1253 let realpmp = $(inputPmp).val();
1254 if (!isNaN(realqty) && !isNaN(realpmp)) {
1255 let realval = realqty * realpmp;
1256 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1258 updateTotalValuation();
1261 $(
'input[class*=realpmp]').on(
'change',
function () {
1262 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1263 let realqty = $(inputQtyReal).val();
1264 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1265 console.log(inputPmp);
1266 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1267 let realpmp = $(inputPmp).val();
1268 if (!isNaN(realpmp)) {
1269 $(
'.'+realPmpClassname).val(realpmp);
1271 if (!isNaN(realqty)) {
1272 let realval = realqty * realpmp;
1273 $(
this).closest(
'tr').find(
'input[name^=realvaluation]').val(realval.toFixed(2));
1275 $(
'.realqty').trigger(
'change');
1276 updateTotalValuation();
1280 $(
'input[name^=realvaluation]').on(
'change',
function () {
1281 let inputQtyReal = $(
this).closest(
'tr').find(
'.realqty');
1282 let realqty = $(inputQtyReal).val();
1283 let inputPmp = $(
this).closest(
'tr').find(
'input[class*=realpmp]');
1284 let inputRealValuation = $(
this).closest(
'tr').find(
'input[name^=realvaluation]');
1285 let realPmpClassname = $(inputPmp).attr(
'class').match(/[\w-]*realpmp[\w-]*/g)[0];
1286 let realvaluation = $(inputRealValuation).val();
1287 if (!isNaN(realvaluation) && !isNaN(realqty) && realvaluation !==
'' && realqty !==
'' && realqty !== 0) {
1288 let realpmp = realvaluation / realqty
1289 $(
'.'+realPmpClassname).val(realpmp);
1290 $(
'.realqty').trigger(
'change');
1291 updateTotalValuation();
1295 function updateTotalValuation() {
1297 $(
'input[name^=realvaluation]').each(
function( index ) {
1298 let val = $(
this).val();
1299 if(!isNaN(val)) total += parseFloat($(
this).val());
1301 let currencyFractionDigits =
new Intl.NumberFormat(
'fr-FR', {
1304 }).resolvedOptions().maximumFractionDigits;
1305 $(
'#totalRealValuation').html(total.toLocaleString(
'fr-FR', {
1306 maximumFractionDigits: currencyFractionDigits
if(GETPOST('button_removefilter_x', 'alpha')||GETPOST('button_removefilter.x', 'alpha')||GETPOST('button_removefilter', 'alpha')) if(GETPOST('button_search_x', 'alpha')||GETPOST('button_search.x', 'alpha')||GETPOST('button_search', 'alpha')) if($action=="save" &&empty($cancel)) $help_url
View.
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.
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
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.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tabs of a record.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
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.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='')
Function to show navigation arrows into lists.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
isModEnabled($module)
Is Dolibarr module enabled.
inventoryPrepareHead(&$inventory, $title='Inventory', $get='')
Define head array for tabs of inventory tools setup pages.
$formconfirm
if ($action == 'delbookkeepingyear') {
div float
Buy price without taxes.
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.