Rev 37786 | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed
$(function () {$(document).on('click', ".warehouse-create-purchase-order", function () {loadCreatePurchase("main-content");});$(document).on('click', ".warehouse-receive-new-invoice", function () {loadNewReceiveInvoice("main-content");});$(document).on('click', ".warehouse-grn-request", function () {loadGrnRequest("main-content");});$(document).on('click', ".warehouse-debit-note", function () {loadDebitNote("main-content");});$(document).on('click', ".warehouse-invoices", function () {loadWarehouseInvoices("main-content");});// Sidebar entry for the PO stock-cover approval queue (Niranjan Kala's queue).// Add this <li> in your sidebar template to expose the entry:// <li><a class="warehouse-po-stock-approvals" href="javascript:void(0);">Stock Approvals</a></li>// Server-side access is gated to APPROVER_EMAILS in PoStockApprovalController.$(document).on('click', ".warehouse-po-stock-approvals", function () {loadPoStockApprovals("main-content");});$(document).on('click', '.dateWiseInvoices', function () {var startDate = getDatesFromPicker('input[name="invoice-duration"]').startDate;var endDate = getDatesFromPicker('input[name="invoice-duration"]').endDatevar vendorId = $('#vendorId').val();// doGetAjaxRequestHandler(context + "/getWarehouseInvoicesByVendor?vendorId=" + vendorId + "&startDate=" + startDate + "&endDate=" + endDate, function (response) {// $('.invoicesviewcontainer').html(response);doGetAjaxRequestHandler(context + "/getWarehouseInvoicesBydateWise?dateWiseInvoices=" + "&startDate=" + startDate + "&endDate=" + endDate, function (response) {$('.invoicesviewcontainer').html(response);});});$(document).on('click', '.generateInvoicesExcel', function () {var startDate = getDatesFromPicker('input[name="invoice-duration"]').startDate;var endDate = getDatesFromPicker('input[name="invoice-duration"]').endDate;doAjaxGetDownload(context + "/generateDatewiseInvoiceExcel?startDate=" + startDate + "&endDate=" + endDate, "PurchaseInvoicesReport.xlsx");});$(document).on('input','#invoice',function () {if (confirm('Document has been selected, Do you want to upload ?')) {var fileSelector = $('#invoice')[0];if (fileSelector != undefined&& fileSelector.files[0] != undefined) {var url = context + '/document-upload';console.log(url);var file = this.files[0];doAjaxUploadRequestHandler(url,'POST',file,function (response) {console.log(response);var documentId = response.response.document_id;console.log("documentId : "+ documentId);localStorage.setItem("invoiceDocument",documentId);});}} else {// Do nothing!}});$(document).on('click', ".createReceiveInvoice", function () {var invoiceDate = $('#actualDate').val();var warehouseId = $('#warehouseMap').val();var numItems = $('#numberofItems').val();var supplierId = $('#vendorId').val();var totalValue = $('#totalValue').val();var invoiceNumber = $('#invoiceNumber').val();//var invoiceDoc = $('#invoiceDoc').val();var invoice = $('#invoice').val();var invoiceDoc = localStorage.getItem("invoiceDocument");console.log(invoiceDoc)if (invoiceDate === "" && warehouseId === "" && numItems === "" && supplierId === "" && totalValue === "" && invoiceNumber === "" && invoiceDoc === "") {alert("Field can't be empty");return false;}if (invoiceDate === "") {alert("Please select date");return false;}if (supplierId === "") {alert("Please choose supplier/Vendor");return false;}if (invoiceNumber === "") {alert("Please fill Invoice Number.");return false;}if (warehouseId === "") {alert("Please choose warehouse");return false;}if (numItems === "") {alert("Please fill number of items.");return false;}if (totalValue === "") {alert("Please fill Total Value.");return false;}console.log('invoice doc', invoiceDoc);if (invoiceDoc === "") {alert("Please upload invoice.");return false;}var newReceiveInvoice = {};newReceiveInvoice['invoiceDate'] = invoiceDate;newReceiveInvoice['warehouseId'] = warehouseId;newReceiveInvoice['numItems'] = numItems;newReceiveInvoice['supplierId'] = supplierId;newReceiveInvoice['totalValue'] = totalValue;newReceiveInvoice['invoiceNumber'] = invoiceNumber;newReceiveInvoice['invoiceDoc'] = invoiceDoc;if (confirm("Are you sure you want to add new Invoice")) {doPostAjaxRequestWithJsonHandler(`${context}/newReceiveInvoice`, JSON.stringify(newReceiveInvoice), function (response) {if (response == 'true') {alert("Successfully added");localStorage.removeItem("invoiceDocument");loadNewReceiveInvoice("main-content");}});}});var purchaseItemIds;$(document).on('click', ".purchaseorderitemview", function () {purchaseItemIds = [];var vendorId = $('#vendorId').val();var warehouseId = $('#warehouseMap').val();var poDate = $('#poDate').val();console.log(warehouseId);if (warehouseId === null && vendorId === "") {alert("Field can't be empty");return;}if (poDate === '') {alert("PO Date is required");return;}if (warehouseId === null && vendorId === "") {alert("Field can't be empty");return;}if (warehouseId === null) {alert("please select warehouse");return;}if (vendorId === "") {alert("please select vendor");return;}doGetAjaxRequestHandler(context + "/addPurchaseItemView?warehouseId=" + warehouseId + "&vendorId=" + vendorId, function (response) {$(".createpurchaseordercontainer").html(response);});});$(document).on('change', '#poItemsFile', function () {var file = this.files[0];if (!file) return;var vendorId = $('#vendorId').val();var poDate = $('#poDate').val();var formData = new FormData();formData.append("file", file);$.ajax({url: context + '/uploadPurchaseOrderItems?vendorId=' + vendorId + '&poDate=' + poDate,type: 'POST',data: formData,dataType: 'json',cache: false,contentType: false,processData: false,success: function (items) {// Clear existing rows$('#purchase-order-table tbody').empty();purchaseItemIds = [];for (var i = 0; i < items.length; i++) {var item = items[i];var $row = $('<tr>' +'<td><input type="text" class="form-control typeaheaditem" autocomplete="off" value="' + item.modelName + '" readonly/></td>' +'<td><div class="mv-qty-cell"><input type="number" class="form-control qty" name="qty" value="' + item.amendedQty + '"/><span class="mv-avail-icon" style="display:none;" tabindex="0">i</span></div><div class="mv-qty-warn" style="display:none;"></div></td>' +'<td><input type="number" class="form-control transferPrice" readonly tabindex="-1" value="' + item.transferPrice + '" data-itemid="' + item.itemId + '"/></td>' +'<td><input type="number" class="form-control totalValue" tabindex="-1" readonly value="' + item.totalValue + '"/></td>' +'<td><input class="form-control btn btn-primary removeInPurchaseOrder" tabindex="-1" type="button" value="Remove"></td>' +'</tr>');$('#purchase-order-table tbody').append($row);// A bulk-uploaded row gets the same breakdown as one picked by hand; absent for outside vendors,// in which case this leaves the row alone.renderMovementAvailability($row, item.availability);purchaseItemIds.push(item.itemId);}updateTotalValue();alert("Uploaded " + items.length + " items successfully");},error: function (xhr) {var msg = "Upload failed";try {var err = JSON.parse(xhr.responseText);if (err.message) msg = err.message;if (err.code) msg = err.code;} catch (e) {}alert(msg);}});// Reset file input so same file can be re-uploaded$(this).val('');});$(document).on('click', ".addRowInPurchaseOrder", function () {var totalQty = 0;var totalValue = 0;$("#purchase-order-table > tbody > tr").each(function () {var itemId = $(this).find(".transferPrice").data('itemid');var qty = $(this).find(".qty").val();var transferPrice = $(this).find(".transferPrice").val();if (qty !== undefined && qty !== "") {qty = parseInt(qty);totalQty += qty;if (transferPrice !== undefined && transferPrice !== "") {transferPrice = parseFloat(transferPrice);totalValue += qty * transferPrice;}}});var $html = $(`<tr><td><input type="text" class="form-control typeaheaditem" autocomplete="off" placeholder="Search Model"/></td><td><div class="mv-qty-cell"><input type="number" class="form-control qty" name="qty" placeholder="Quantity"/><span class="mv-avail-icon" style="display:none;" tabindex="0">i</span></div><div class="mv-qty-warn" style="display:none;"></div></td><td><input type="number" class="form-control transferPrice" readonly tabindex="-1" placeholder="Transfer Price"/></td><td><input type="number" class="form-control totalValue" tabindex="-1" readonly placeholder="Total Value"/></td><td><input class="form-control btn btn-primary removeInPurchaseOrder" tabindex="-1" type="button" value="Remove"></td></tr>`);var vendorId = $('#vendorId').val();// Scoped to the PO items table's tbody — bare $('tbody') would also match// the modal's flagged-items tbody, and jQuery would move $html into the// modal instead of the visible table, breaking the autocomplete wiring.$('#purchase-order-table tbody').append($html);getVendorItemAheadOptions($html.find('.typeaheaditem'), vendorId, function (selectedItem) {let poDate = $('#poDate').val();var itemId = selectedItem.itemId;var description = selectedItem.itemDescription;if (itemId) {doGetAjaxRequestHandler(`${context}/getPricing?vendorId=${vendorId}&itemId=${itemId}&onDate=${poDate}`, function (response) {if (response !== "null") {if (response !== 'false') {var jsonObj = JSON.parse(response);$html.find('.transferPrice').val(jsonObj.transferPrice);renderMovementAvailability($html, jsonObj);$html.find('input.qty').focus();// Update the total value for this rowlet qty = $html.find('.qty').val();let transferPrice = jsonObj.transferPrice;if (qty && transferPrice) {let total = qty * transferPrice;$html.find('.totalValue').val(total);updateTotalValue();}addItem($html, itemId);} else {$html.find('.typeaheaditem').val('');alert(`Category not Define(eg- HID, SLOW MOVING, FAST MOVING),First define the category of ${description} and Try again`);}} else {$html.find('.typeaheaditem').val('');alert(`No approved price for ${description} on ${poDate}. Get the vendor pricing approved first, then try again.`);}});} else {return false;}});});$(document).on('input', '.qty', function () {var $row = $(this).closest('tr');var qty = parseInt($(this).val());checkMovableQty($row, qty);var transferPrice = parseFloat($row.find('.transferPrice').val());if (!isNaN(qty) && !isNaN(transferPrice)) {let total = qty * transferPrice;$row.find('.totalValue').val(total);}updateTotalValue();});function updateTotalValue() {var grandTotal = 0;var totalQty = 0;$('tbody > tr').each(function () {var rowTotal = parseFloat($(this).find('.totalValue').val());var rowQty = parseInt($(this).find('.qty').val());if (!isNaN(rowTotal)) {grandTotal += rowTotal;}if (!isNaN(rowQty)) {totalQty += rowQty;}});$('#totalQuantity').text(totalQty);$('#grandTotal').text(grandTotal.toFixed(2));}function addItem(container, itemId) {oldItemId = container.find('.transferPrice').data('itemid');if (typeof (oldItemId) === "undefined") {let itemIndex = purchaseItemIds.indexOf(itemId);if (itemIndex >= 0) {alert("Item already added");container.find('.typeaheaditem').val("");return false;} else {purchaseItemIds.push(itemId);container.find('.transferPrice').data('itemid', itemId);$('.addRowInPurchaseOrder').click();}} else {if (oldItemId !== itemId) {let oldIndex = purchaseItemIds.indexOf(oldItemId);if (oldIndex > -1) {purchaseItemIds.splice(oldIndex, 1);}purchaseItemIds.push(itemId);container.find('.transferPrice').data('itemid', itemId);} else {return false;}}return true;}$(document).on('click', '.createSendPurchaseOrder', function () {createPO(true);});$(document).on('click', '.createPurchaseOrder', function () {createPO(false);});$(document).on('change', '.qty', function () {var row = $(this).closest("tr");var qty = $(row).find(".qty").val();var transferPrice = $(row).find(".transferPrice").val();var totalVal = transferPrice * qty;var totalValue = $(row).find(".totalValue").val(totalVal);var purchaseOrderValue = 0;$("table > tbody > tr").each(function () {var totalValue = $(this).find(".totalValue").val();purchaseOrderValue += totalValue;});});$(document).on('click', '.warehouse-open-purchase-order', function () {loadOpenPurchase("main-content");});$(document).on('click', '.editViewPurchaseOrder', function () {var purchaseOrderId = $(this).data('poid');doGetAjaxRequestHandler(context + "/getEditPOByPurchaseId?purchaseId=" + purchaseOrderId, function (response) {$('#warehouseEditPurchaseContainer .modal-content').html(response);});});$(document).on('click', '.editPurchaseOrder', function () {var purchaseOrderId = $(this).data('poid');editPurchase(false, purchaseOrderId);});$(document).on('click', '.editSendPurchaseOrder', function () {var purchaseOrderId = $(this).data('poid');editPurchase(true, purchaseOrderId);});$(document).on('click', '.closePurchaseOrder', function () {var purchaseOrderId = $(this).data('poid');if (confirm("Are you sure you want to close the PO") == true) {doPostAjaxRequestHandler(context + "/closePuchaseOrder?purchaseId=" + purchaseOrderId,function (response) {console.log(response);if (response == 'true') {alert("successfully closed");loadOpenPurchase("main-content")}});}});$(document).on('click', '.warehouse-view-purchase-order', function () {doGetAjaxRequestHandler(context + "/viewPurchaseOrder", function (response) {$('#' + 'main-content').html(response);});});$(document).on('click', '.dateWisePo', function () {var startDate = getDatesFromPicker('input[name="duration"]').startDate;var endDate = getDatesFromPicker('input[name="duration"]').endDate;doGetAjaxRequestHandler(context + "/getPurchaseOrders?startDate=" + startDate + "&endDate=" + endDate, function (response) {$('.purchaseorderviewcontainer').html(response);});});$(document).on('click', '.viewPurchaseOrderlineItem', function () {var purchaseOrderId = $(this).data('poid');doGetAjaxRequestHandler(context + "/getWarehouseLineItemByPurchaseId?purchaseId=" + purchaseOrderId, function (response) {$('#warehouseLineItem .modal-content').html(response);});});$(document).on("click", '.removeInPurchaseOrder', function () {var row = $(this).closest("tr");let itemId = row.find('.transferPrice').data('itemid');let itemIndex = purchaseItemIds.indexOf(itemId);if (itemIndex > -1) { // only splice array when item is foundpurchaseItemIds.splice(itemIndex, 1); // 2nd parameter means remove one item only}row.remove();});$(document).on('click', ".addRowForInvoiceItem", function () {var numitems = $(this).data("numitems");var invoiceId = $(this).data("invoiceid");var invoiceItemIds = [];var totalQty = 0;$("#invoice-order-table > tbody > tr").each(function () {var itemId = $(this).find(".rate").data('itemid');var qty = $(this).find(".qty").val();if (qty !== undefined) {totalQty += qty;}if (itemId !== undefined) {invoiceItemIds.push(itemId);}});var $html = $('<tr> <td> <input type="text" class="form-control typeaheaditem" autocomplete="off" placeholder="Search Model"/> </td>' +'<td> <input type="number" class="form-control qty" name = "qty" placeholder="Quantity"/> </td>' +'<td> <input type="number" class="form-control rate" placeholder="Rate"/> </td>' +'<td> <input class="form-control btn btn-primary removeInInvoiceItem" type="button" value="Remove"></td> </tr> ');getItemAheadOptions($html.find('.typeaheaditem'), true, function (selectedItem) {var itemId = selectedItem.itemId;if (invoiceItemIds.indexOf(itemId) > -1) {alert("Item already added");$html.find('.typeaheaditem').val("")return false;}$html.find('.rate').data('itemid', itemId)$html.find('.removeInInvoiceItem').data('invoiceid', invoiceId)$html.find('.removeInInvoiceItem').data('itemid', itemId)}, true);if (totalQty < numitems) {$('#invoice-order-table tbody').append($html);}});$(document).on('click', '.saveInvoiceDetail', function () {var numItems = $(this).data("numitems");var invoiceId = $(this).data("invoiceid");var warehouseId = $(this).data("warehouseid");var supplierId = $(this).data("supplierid");getInvoiceItemData(numItems, invoiceId, warehouseId, supplierId, false);});$(document).on('click', '.validateInvoiceDetail', function () {var numItems = $(this).data("numitems");var invoiceId = $(this).data("invoiceid");var warehouseId = $(this).data("warehouseid");var supplierId = $(this).data("supplierid");getInvoiceItemData(numItems, invoiceId, warehouseId, supplierId, true);});$(document).on("click", '.removeInInvoiceItem', function () {var row = $(this).closest("tr");var invoiceId = $(this).data("invoiceid");var itemId = $(this).data("itemid");if (invoiceId != undefined && itemId != undefined) {doDeleteAjaxRequestHandler(context + "/removeInvoiceItem?invoiceId="+ invoiceId + "&itemId=" + itemId, function (response) {if (response == "true") {alert("Item removed successfully");}});}row.remove();});$(document).on('click', '.purchaseGrn', function () {var grnItemArray = [];var invoiceId = $(this).data("invoiceid");$("#invoice-purchase-validate > tbody > tr").each(function () {var grnItemJson = {};var itemId = $(this).find(".itemId").text();var qty = $(this).find(".qty").text();var poId = $(this).find(".purchaseId").text();var excessQty = $(this).find(".excessQty").text();var itemType = $(this).find(".itemType").text();console.log(itemType);if (itemType == "Serialized") {var serialNumbers = $(this).find(".imeis-to-grn").tagsinput('items');grnItemJson['serialNumbers'] = serialNumbers;if (excessQty > 0) {var returnSerialNumbers = $(this).find(".imeis-to-grn-return").tagsinput('items');grnItemJson['returnSerialNumbers'] = returnSerialNumbers}}console.log(serialNumbers);grnItemJson['itemId'] = itemId;grnItemJson['serialNumbers'] = serialNumbersgrnItemJson['qty'] = qtygrnItemJson['poId'] = poIdgrnItemJson['invoiceId'] = invoiceIdgrnItemJson['itemType'] = itemTypegrnItemJson['excessQty'] = excessQtygrnItemJson['returnSerialNumbers'] = returnSerialNumbersgrnItemArray.push(grnItemJson)console.log(grnItemArray)});for (var i = 0; i < grnItemArray.length; i++) {var itemType = grnItemArray[i].itemType;if (itemType == "Serialized") {var excessQty = grnItemArray[i].excessQtyvar serialNumbers = grnItemArray[i].serialNumbers.lengthvar qty = grnItemArray[i].qtyif (serialNumbers != qty) {alert("serial number is not matched with qty")return false;}if (excessQty > 0) {var returnSerialNumbers = grnItemArray[i].returnSerialNumbers.lengthif (returnSerialNumbers != excessQty) {alert("Return serial number is not matched with excess qty")return false;}}}}console.log(JSON.stringify(grnItemArray))if (confirm("Are you sure you want to grn purchase order") == true) {doPostAjaxRequestWithJsonHandler(context+ "/createGrn", JSON.stringify(grnItemArray), function (response) {if (response == 'true') {alert("successfully done");loadNewReceiveInvoice("main-content");}});}});$(document).on('click', '.grnRequest', function () {var grnRequestJson = {};var grnRequestItemArray = [];var invoiceId = $(this).data("invoiceid");$("#invoice-purchase-validate > tbody > tr").each(function () {var grnItemJson = {};var itemId = $.trim($(this).find(".itemId").text());var poId = $.trim($(this).find(".purchaseId").text());var qty = parseInt($(this).find(".qty").text());var excessQty = parseInt($(this).find(".excessQty").text());var priceMismatch = $.trim($(this).find(".priceMismatch").text());console.log(priceMismatch)if (priceMismatch !== "" || excessQty > 0) {console.log(typeof priceMismatch)console.log(excessQty)grnItemJson['itemId'] = itemId;grnItemJson['qty'] = qty;grnItemJson['excessQty'] = excessQtyif (priceMismatch === '') {grnItemJson['mismatch'] = 'QtyMismatch';} else {grnItemJson['mismatch'] = priceMismatch}grnItemJson['poId'] = poIdgrnRequestItemArray.push(grnItemJson)}});console.log(grnRequestItemArray)grnRequestJson['invoiceId'] = invoiceId;grnRequestJson['warehousePurchaseModel'] = grnRequestItemArrayif (confirm("Are you sure you want to raise grn request")) {doPostAjaxRequestWithJsonHandler(context+ "/grnRequest", JSON.stringify(grnRequestJson), function (response) {if (response == 'true') {alert("successfully done");loadNewReceiveInvoice("main-content");}});}});$(document).on('click', '.resolvedPriceMismatchRequest', function () {var id = $(this).data("id");var invoiceId = $(this).data("invoiceid");var requestId = $(this).data("requestid");var mismatch = $(this).data("mismatch");if (mismatch == "InvoiceMismatch") {if (confirm("Are you sure you want to resolve invoice mismatch")) {doGetAjaxRequestHandler(context + "/resolvedMismatchRequest?id=" + id, function (response) {if (response == 'true') {getGrnRequestItems(requestId, invoiceId)}});}} else if (mismatch == "PoMismatch") {// Say what the replacement PO is for and what to do if it closes before the stock lands, rather than// leaving someone to discover that a GRN has quietly stopped matching.if (confirm("Resolve this price mismatch?\n\nThe current PO line is discarded and a replacement "+ "purchase order is raised on the invoice date at the corrected price. This invoice is then "+ "received against that PO.\n\nIt closes automatically after 6 days; if the stock arrives "+ "later, it needs a fresh PO.") == true) {doGetAjaxRequestHandler(context + "/resolvedMismatchRequest?id=" + id, function (response) {if (response == 'true') {getGrnRequestItems(requestId, invoiceId)}});}}});$(document).on('click', '.resolvedQtyMismatchRequest', function () {var id = $(this).data("id");var invoiceId = $(this).data("invoiceid");var requestId = $(this).data("requestid");var mismatch = $(this).data("mismatch");var $row = $(this).closest("tr");var requiredQty = $row.find(".requiredQty").val();console.log(requiredQty);if (mismatch == "QtyMismatch") {if (confirm("Are you sure you want to resolve qty mismatch") == true) {doGetAjaxRequestHandler(context + "/resolvedMismatchRequest?id=" + id + "&requiredQty=" + requiredQty, function (response) {if (response == 'true') {getGrnRequestItems(requestId, invoiceId)}});}}});});function loadCreatePurchase(domId) {doGetAjaxRequestHandler(context + "/warehousePurchaseOrder", function (response) {$('#' + domId).html(response);});}function loadOpenPurchase(domId) {doGetAjaxRequestHandler(context + "/getOpenPurchaseOrder", function (response) {$('#' + domId).html(response);});}function loadNewReceiveInvoice(domId) {doGetAjaxRequestHandler(context + "/newReceiveInvoice", function (response) {$('#' + domId).html(response);});}function loadGrnRequest(domId) {doGetAjaxRequestHandler(context + "/grnRequest", function (response) {$('#' + domId).html(response);});}function loadDebitNote(domId) {doGetAjaxRequestHandler(context + "/getDebitNote", function (response) {$('#' + domId).html(response);});}$(document).on('click', '.dateWiseDebitNote', function () {var startDate = getDatesFromPicker('input[name="date-duration"]').startDate;var endDate = getDatesFromPicker('input[name="date-duration"]').endDate;doGetAjaxRequestHandler(context + "/getDateWiseDebitNote?startDate=" + startDate + "&endDate=" + endDate, function (response) {$('#warehouse-datewise-debitnote-container').html(response);});});$(document).on('click', '.generateVendorDebitNoteExcel', function () {var startDate = getDatesFromPicker('input[name="date-duration"]').startDate;var endDate = getDatesFromPicker('input[name="date-duration"]').endDate;doAjaxGetDownload(context + "/generateVendorDebitNoteExcel?startDate=" + startDate + "&endDate=" + endDate, "VendorDebitNoteReport.csv");});function loadWarehouseInvoices(domId) {doGetAjaxRequestHandler(context + "/getWarehouseInvoices", function (response) {$('#' + domId).html(response);});}// Loads the PO stock-cover approvals queue into the main content pane.// Access is server-gated (403-ish empty view if the caller isn't an approver).function loadPoStockApprovals(domId) {doGetAjaxRequestHandler(context + "/po/stock-approvals", function (response) {$('#' + domId).html(response);});}function createPO(sendPo) {var purchaseOrder = {};var vendorId = $('#vendorId').val();var warehouseId = $('#warehouseMap').val();var poDate = $('#poDate').val();var items = []$("#purchase-order-table > tbody > tr").each(function () {var purchaseOrderJson = {};var itemId = $(this).find(".transferPrice").data('itemid');var qty = $(this).find(".qty").val();var transferPrice = $(this).find(".transferPrice").val();var totalValue = $(this).find(".totalValue").val();purchaseOrderJson['itemId'] = itemId;purchaseOrderJson['amendedQty'] = qtypurchaseOrderJson['totalValue'] = totalValuepurchaseOrderJson['transferPrice'] = transferPriceitems.push(purchaseOrderJson)});for (var i = 0; i < items.length; i++) {console.log(items[i])var itemId = items[i].itemIdvar qty = items[i].amendedQtyvar transferPrice = items[i].transferPricevar totalValue = items[i].totalValueif (itemId === "" && qty === "" && transferPrice === "" && totalValue === "") {alert("Field can't be empty");return false;}if (itemId === "") {alert("please select item");return false;}if (qty === "") {alert("please choose qty");return false;}if (transferPrice === "") {alert("Transfer Price can't be empty");return false;}if (totalValue === "") {alert("Total value can't be empty");return false;}if (qty <= 0) {alert("Please fill Qty");return false;}}// Rows typed by hand and rows filled from a bulk upload are checked alike: the screen is where the order is made.var overCap = [];$("#purchase-order-table > tbody > tr").each(function () {if ($(this).find('.qty').hasClass('mv-over')) {overCap.push($(this).find('.typeaheaditem').val() + ': ' + $(this).find('.mv-qty-warn').text());}});if (overCap.length > 0) {alert("Reduce these quantities before creating the PO:\n\n" + overCap.join("\n"));return false;}purchaseOrder['vendorId'] = vendorId;purchaseOrder['warehouseId'] = warehouseIdpurchaseOrder['sendPo'] = sendPopurchaseOrder['poDate'] = poDate;purchaseOrder['items'] = itemsconsole.log(purchaseOrder)// Modal 1 (pmscp) replaces the old native `confirm`. Populate the summary// tiles from what the user actually entered, then hand off to the button// handler wired in $(document).on('click', '#pmscpConfirmCreate', ...).openPmscpCreateConfirm(purchaseOrder);}// ─────────────────────────────────────────────────────────────────────────────// PO-create modal stack — three sequenced modals replace the old native// confirm/prompt/alert popups:// 1) #pmscpConfirmModal — "Are you sure you want to create this PO?"// 2) #pmscpReasonModal — reason textarea + live 50-char counter// (only when preflight says approval is needed)// 3) #pmscpApprovalModal — final "Sending to Niranjan Kala · L4" confirm// The pending purchaseOrder object rides on window.__pmscpPO between modals.// ─────────────────────────────────────────────────────────────────────────────var PMSCP_MIN_REASON = 50;// Minimal HTML escape for values injected into modal markup (item names from server).function escapePmscp(s) {if (s === null || s === undefined) return '';return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');}function openPmscpCreateConfirm(purchaseOrder) {window.__pmscpPO = purchaseOrder;// Populate summary tiles from the form + purchaseOrder payload.var vendorName = $('#vendorId option:selected').text() || purchaseOrder.vendorId;var warehouseName = $('#warehouseMap option:selected').text() || purchaseOrder.warehouseId;var totalQty = $('#totalQuantity').text() || (purchaseOrder.items || []).length;var grandTotal = $('#grandTotal').text() || '';$('#pmscpSumVendor').text(vendorName);$('#pmscpSumWarehouse').text(warehouseName);$('#pmscpSumItems').text((purchaseOrder.items || []).length + ' line item(s)');$('#pmscpSumQty').text(totalQty);$('#pmscpSumTotal').text(grandTotal ? '₹ ' + grandTotal : '—');$('#pmscpConfirmModal').modal('show');}// Handlers — bind once (safe with delegated `on`).$(document).on('click', '#pmscpConfirmCreate', function () {$('#pmscpConfirmModal').modal('hide');// Give Bootstrap a moment to finish the fade-out before firing the AJAX so// the loading overlay renders cleanly.setTimeout(function () {submitCreatePO(window.__pmscpPO);}, 200);});// Live counter on the reason textarea — gates the Continue button.$(document).on('input change', '#pmscpReasonText', function () {var len = $(this).val().trim().length;$('#pmscpReasonCount').text(len);var ok = len >= PMSCP_MIN_REASON;$('#pmscpReasonCounter').toggleClass('pmscp-ok', ok);$('#pmscpReasonContinue').prop('disabled', !ok);});// Continue from reason modal → open approval-target confirm modal.$(document).on('click', '#pmscpReasonContinue', function () {var reason = $('#pmscpReasonText').val().trim();if (reason.length < PMSCP_MIN_REASON) return; // defensivewindow.__pmscpPO.requesterReason = reason;$('#pmscpApprovalReasonPreview').text(reason);$('#pmscpReasonModal').modal('hide');setTimeout(function () {$('#pmscpApprovalModal').modal('show');}, 250);});// Back from approval modal → return to reason modal so user can tweak text.$(document).on('click', '#pmscpApprovalBack', function () {$('#pmscpApprovalModal').modal('hide');setTimeout(function () {$('#pmscpReasonModal').modal('show');}, 250);});// Final confirm → fire the real create request.$(document).on('click', '#pmscpApprovalConfirm', function () {$('#pmscpApprovalModal').modal('hide');setTimeout(function () {actuallyCreatePO(window.__pmscpPO);}, 200);});// Reset counter every time reason modal opens so a fresh session starts at 0.$(document).on('shown.bs.modal', '#pmscpReasonModal', function () {// Only reset text if we don't already have a saved reason (e.g. user// came back via the "Back" button — preserve what they typed).if (!window.__pmscpPO || !window.__pmscpPO.requesterReason) {$('#pmscpReasonText').val('');} else {$('#pmscpReasonText').val(window.__pmscpPO.requesterReason);}$('#pmscpReasonText').trigger('input').focus();});// Submits the PO create request. Runs a preflight stock-cover check first// (single always-200 call, no side effects): if the rule fires, opens the// reason modal → approval-target modal → hits /createPurchaseOrder once with// the reason included. This avoids the old "submit → 400 → retry" pattern// that triggered the global "Error occurred" popup on the first response.function submitCreatePO(purchaseOrder) {$.ajax({url: context + "/createPurchaseOrder/preflight-stock-check",type: "POST",contentType: "application/json",data: JSON.stringify(purchaseOrder),dataType: "json",global: false, // suppress global handlers just in case preflight failssuccess: function (preflight) {// Duplicate-guard: same vendor + item already in a pending approval.if (preflight && preflight.alreadyInReview) {var lines = [];for (var k in preflight.conflicts) {if (Object.prototype.hasOwnProperty.call(preflight.conflicts, k)) {var c = preflight.conflicts[k];// Server sends { itemId: { modelName, poNumber } } — support the// old shape { itemId: poNumber } as fallback in case of stale server.var poNumber = (c && typeof c === 'object') ? c.poNumber : c;var modelName = (c && typeof c === 'object' && c.modelName) ? c.modelName : "";var itemLabel = modelName ? (modelName + " #" + k) : ("Item #" + k);lines.push(" " + itemLabel + " → PO " + poNumber);}}alert("Cannot create this PO — one or more items are already under review for the same vendor:\n\n" +lines.join("\n") +"\n\nWait for those POs to be approved or rejected first, then try again.");return;}if (preflight && preflight.needsApproval && !purchaseOrder.requesterReason) {// Populate the "Items requiring approval" table from preflight.flaggedItems// BEFORE opening the modal so the requester sees exactly which SKUs and how// much aging vs 15d-sold when they compose their reason. Mixed POs (some// clean, some aging) show only the aging items here — clean items are not// held and not displayed.var flagged = (preflight && preflight.flaggedItems) || [];var $rows = $('#pmscpFlaggedRows').empty();if (flagged.length > 0) {for (var i = 0; i < flagged.length; i++) {var f = flagged[i];var name = f.modelName || ('#' + f.itemId);// If server left "#itemId" as the model name (item lookup failed),// skip the redundant "#itemId" span since the name already carries it.var idSpan = (name.indexOf('#') === 0) ? '' :' <span style="color:#7b8199;font-size:11px">#' + f.itemId + '</span>';// Excess turns green only when NO aging anywhere in the network.var hasAgingHere = (f.agingQty || 0) > 0;var hasAgingElsewhere = f.agingBreakdown && f.agingBreakdown.length > 0;var excessColor = (!hasAgingHere && !hasAgingElsewhere) ? '#177a3a' : '#c62828';$rows.append('<tr>' +'<td style="padding:6px 10px;border-bottom:1px solid #f0f2f7">' + escapePmscp(name) + idSpan + '</td>' +'<td style="padding:6px 10px;text-align:right;border-bottom:1px solid #f0f2f7">' + (f.agingQty || 0) + '</td>' +'<td style="padding:6px 10px;text-align:right;border-bottom:1px solid #f0f2f7">' + (f.sold15dQty || 0) + '</td>' +'<td style="padding:6px 10px;text-align:right;border-bottom:1px solid #f0f2f7">' + (f.poQty || 0) + '</td>' +'<td style="padding:6px 10px;text-align:right;border-bottom:1px solid #f0f2f7;color:' + excessColor + ';font-weight:600">+' + (f.excess || 0) + '</td>' +'</tr>');// Mirror the detail-modal pattern: when this vendor+warehouse has no// aging AND the item has aging elsewhere, render one amber sub-row per// (warehouse, vendor). Shortens "DL-NSSPL/Delhi/BSB Marketing/G" +// "BSB Marketing Private Limited" to "DL-NSSPL - (BSB)" for readability.if (!hasAgingHere && hasAgingElsewhere) {for (var j = 0; j < f.agingBreakdown.length; j++) {var b = f.agingBreakdown[j];var whFull = b.warehouseName || '';var vFull = b.vendorName || '';var whShort = whFull.indexOf('/') >= 0 ? whFull.substring(0, whFull.indexOf('/')) : whFull;var vShort = vFull.indexOf(' ') >= 0 ? vFull.substring(0, vFull.indexOf(' ')) : vFull;$rows.append('<tr style="background:#fff9e6;color:#8a6d3b">' +'<td style="padding:4px 10px 4px 26px;border-bottom:1px solid #f1e0b0;font-size:11px">' +'<span style="color:#c19c00">↳ aging elsewhere:</span> ' +escapePmscp(whShort) + ' - (' + escapePmscp(vShort) + ')' +'</td>' +'<td style="padding:4px 10px;text-align:right;border-bottom:1px solid #f1e0b0;font-size:11px">' + (b.qty || 0) + '</td>' +'<td colspan="3" style="border-bottom:1px solid #f1e0b0"></td>' +'</tr>');}}}$('#pmscpFlaggedCount').text(flagged.length);$('#pmscpFlaggedBox').show();} else {// Empty flaggedItems is unexpected when needsApproval=true, but hide// the table gracefully rather than showing an empty box.$('#pmscpFlaggedBox').hide();}$('#pmscpReasonModal').modal('show');return;}actuallyCreatePO(purchaseOrder);},error: function () {// Preflight failed for a reason unrelated to the rule — attempt// the create anyway; server will still enforce the rule if needed.actuallyCreatePO(purchaseOrder);}});}// The single, actual create request — fires once whether or not approval is needed.function actuallyCreatePO(purchaseOrder) {doPostAjaxRequestWithJsonHandler(context + "/createPurchaseOrder",JSON.stringify(purchaseOrder), function (response) {if (response == 'true') {alert("Successfully created");loadCreatePurchase("main-content");}});}function editPurchase(sendPo, poid) {var editpurchaseOrder = {}var items = []$("#purchase-edit-lineitem > tbody > tr").each(function () {var purchaseOrderJson = {};var itemId = $(this).find("td:eq(0)").text();var qty = $(this).find("td:eq(2)").text();var amendedQty = $(this).find(".editqty").val();var transferPrice = $.trim($(this).find(".transferPrice").html());purchaseOrderJson['itemId'] = itemId;purchaseOrderJson['amendedQty'] = parseInt(amendedQty)purchaseOrderJson['qty'] = parseInt(qty)purchaseOrderJson['transferPrice'] = transferPriceitems.push(purchaseOrderJson)});for (var i = 0; i < items.length; i++) {console.log(items[i])var amendedQty = items[i].amendedQtyvar qty = items[i].qtyif (amendedQty === "") {alert("please choose qty");return false;}if (amendedQty < qty) {alert("Quantity should be greater than existing qty");return false;}}editpurchaseOrder['purchaseOrderId'] = poid;editpurchaseOrder['sendPo'] = sendPoeditpurchaseOrder['items'] = itemsif (confirm("Are you sure you want to edit purchase order") == true) {doPostAjaxRequestWithJsonHandler(context+ "/editPurchaseOrder", JSON.stringify(editpurchaseOrder), function (response) {if (response == 'true') {alert("successfully created");$('#warehouseEditPurchaseContainer').modal('hide');$('.modal-backdrop').remove();loadOpenPurchase("main-content")}});}}function getInvoiceItemData(numItems, invoiceId, warehouseId, supplierId, validate) {var invoiceJson = {};console.log(numItems)var totalQty = 0;var invoiceItems = []$("#invoice-order-table > tbody > tr").each(function () {var invoiceItemJson = {};var itemId = $(this).find(".rate").data('itemid');var qty = $(this).find(".qty").val();if (qty != undefined) {totalQty += parseInt(qty)}var rate = $(this).find(".rate").val();invoiceItemJson['itemId'] = itemId;invoiceItemJson['qty'] = qtyinvoiceItemJson['rate'] = rateinvoiceItems.push(invoiceItemJson)});for (var i = 0; i < invoiceItems.length; i++) {var itemId = invoiceItems[i].itemIdvar qty = invoiceItems[i].qtyvar rate = invoiceItems[i].rateif (itemId === "" && qty === "" && rate === "") {alert("Field can't be empty");return false;}if (itemId === "") {alert("please select item");return false;}if (qty === "") {alert("please choose qty");return false;}if (rate === "") {alert("Rate can't be empty");return false;}}console.log(totalQty)if (totalQty > numItems) {alert("Qty Should not be more than Invoice items");return false;}if (validate) {if (totalQty != numItems) {alert("Qty Should not match with Invoice items");return false;}}invoiceJson['invoiceId'] = invoiceId;invoiceJson['warehouseId'] = warehouseId;invoiceJson['supplierId'] = supplierId;invoiceJson['invoiceItems'] = invoiceItemsif (validate) {validateInvoiceItems(invoiceJson);} else {saveInvoiceItems(invoiceJson)}}function saveInvoiceItems(invoiceJson) {if (confirm("Are you sure you want to save invoice item") == true) {doPostAjaxRequestWithJsonHandler(context+ "/invoiceItemDetail", JSON.stringify(invoiceJson), function (response) {if (response == 'true') {getInvoiceItems(invoiceJson.invoiceId);}});}}function validateInvoiceItems(invoiceJson) {if (confirm("Are you sure you want to validate invoice item") == true) {doPostAjaxRequestWithJsonHandler(context+ "/validateInvoiceItemDetail", JSON.stringify(invoiceJson), function (response) {$('.invoiceadditemcontainer').html(response);});}}function getOpenPOFromWarehouse() {var warehouseId = $('#warehouseMap').val();doGetAjaxRequestHandler(context + "/getOpenPurchaseOrderByWarehouseId?warehouseId=" + warehouseId, function (response) {$('#warehouseManagePuchaseContainer').html(response);});}function getPurchaseLineitem(poId) {doGetAjaxRequestHandler(context + "/getPurchaseOrderItemByPoId?poId=" + poId, function (response) {$('#warehouseManagePuchaseItemContainer').html(response);$('#warehousepurchaseitemgrn').hide()});}function getInvoiceItems(invoiceId) {doGetAjaxRequestHandler(context + "/getInvoiceItems?invoiceId=" + invoiceId, function (response) {$('.invoiceadditemcontainer').html(response);});}function getGrnRequestItems(requestId, invoiceId) {doGetAjaxRequestHandler(context + "/grnRequestItem?requestId=" + requestId + "&invoiceId=" + invoiceId, function (response) {$('.grnRequestItemContainer').html(response);});}function removeInvoice(invoiceId) {if (confirm(`Are your sure want to delete invoiceId ${invoiceId}`)) {doGetAjaxRequestHandler(`${context}/removeInvoice?invoiceId=${invoiceId}`, function (response) {$('#open-invoices').DataTable().row("#" + invoiceId).remove().draw();alert("Invoice Removed");});}}function removeGrnRequest(invoiceId) {if (confirm(`Are your sure want to remove grn request for invoiceId ${invoiceId}`)) {doGetAjaxRequestHandler(`${context}/removeGrnRequest?invoiceId=${invoiceId}`, function (response) {$('#' + domId).html(response);alert("Grn Request Removed");});}}// ============ GRN Correction ============$(document).on('click', '.warehouse-grn-correction', function () {loadGrnCorrection("main-content");});$(document).on('click', '.warehouse-grn-correction-approval', function () {loadGrnCorrectionApproval("main-content");});$(document).on('click', '.searchCorrectionInvoices', function () {var startDate = getDatesFromPicker('input[name="correction-duration"]').startDate;var endDate = getDatesFromPicker('input[name="correction-duration"]').endDate;doGetAjaxRequestHandler(context + "/grnCorrection?startDate=" + startDate + "&endDate=" + endDate, function (response) {$('#main-content').html(response);});});$(document).on('click', '.submitGrnCorrection', function () {var invoiceId = $(this).data('invoiceid');var remarks = $('#correctionRemarks').val();if (!remarks || remarks.trim() === '') {alert("Please enter remarks explaining the reason for correction");return false;}// Collect serial number corrections (old → new)var corrections = [];$('#serial-corrections-table > tbody > tr').each(function () {var oldSerial = $(this).find('.old-serial').val();var newSerial = $(this).find('.new-serial').val();if (oldSerial && newSerial && oldSerial.trim() !== '' && newSerial.trim() !== '') {corrections.push({oldSerialNumber: oldSerial.trim(),newSerialNumber: newSerial.trim()});}});// Collect item corrections (serialized or non-serialized)var qtyCorrections = [];var imeiCountError = false;$('#qty-corrections-table > tbody > tr').each(function () {var oldItemId = $(this).find('.qty-old-itemid').val();var newItemId = $(this).find('.qty-new-itemid').val();var serial = $(this).find('.qty-serial').val();var qty = parseInt($(this).find('.qty-correction-val').val());if (oldItemId && newItemId && serial && serial.trim() !== '') {// Split IMEIs by space, comma, or newlinevar imeis = serial.trim().split(/[\s,]+/).filter(function(s) { return s.length > 0; });var imeiCount = imeis.length;// Auto-set qty from IMEI count, validate if qty was manually enteredif (qty > 0 && qty !== imeiCount) {alert("Row error: Qty (" + qty + ") does not match IMEI count (" + imeiCount + ").\nIMEIs: " + imeis.join(", "));imeiCountError = true;return false;}// Create one entry per IMEI (each with qty=1)for (var i = 0; i < imeis.length; i++) {qtyCorrections.push({oldItemId: parseInt(oldItemId),newItemId: parseInt(newItemId),qty: 1,serialNumber: imeis[i].trim()});}} else if (oldItemId && newItemId && qty > 0) {qtyCorrections.push({oldItemId: parseInt(oldItemId),newItemId: parseInt(newItemId),qty: qty});}});if (imeiCountError) return false;if (corrections.length === 0 && qtyCorrections.length === 0) {alert("No corrections specified. Please enter serial number or quantity corrections.");return false;}var requestModel = {invoiceId: invoiceId,remarks: remarks,corrections: corrections,qtyCorrections: qtyCorrections};if (confirm("Are you sure you want to raise this GRN correction request?")) {doPostAjaxRequestWithJsonHandler(context + "/raiseGrnCorrection",JSON.stringify(requestModel), function (response) {var parsed;try { parsed = typeof response === 'string' ? JSON.parse(response) : response; }catch (e) { parsed = null; }if (parsed && parsed.status === true) {if (parsed.autoApproved) {alert("Correction applied successfully (auto-approved: no model/qty change)");} else {alert("Correction request raised for approval");}loadGrnCorrection("main-content");}});}});$(document).on('click', '.approveGrnCorrectionBtn', function () {var requestId = $(this).data('requestid');var remarks = $('#approvalRemarks').val();if (confirm("Are you sure you want to APPROVE this correction request? This will apply the IMEI/qty changes.")) {doPostAjaxRequestHandler(context + "/approveGrnCorrection?requestId=" + requestId + "&remarks=" + encodeURIComponent(remarks),function (response) {if (response == 'true') {alert("Correction approved and applied successfully");loadGrnCorrectionApproval("main-content");}});}});$(document).on('click', '.rejectGrnCorrectionBtn', function () {var requestId = $(this).data('requestid');var remarks = $('#approvalRemarks').val();if (!remarks || remarks.trim() === '') {alert("Please enter remarks explaining the reason for rejection");return false;}if (confirm("Are you sure you want to REJECT this correction request?")) {doPostAjaxRequestHandler(context + "/rejectGrnCorrection?requestId=" + requestId + "&remarks=" + encodeURIComponent(remarks),function (response) {if (response == 'true') {alert("Correction request rejected");loadGrnCorrectionApproval("main-content");}});}});function loadGrnCorrection(domId) {doGetAjaxRequestHandler(context + "/grnCorrection", function (response) {$('#' + domId).html(response);});}function loadGrnCorrectionApproval(domId) {doGetAjaxRequestHandler(context + "/grnCorrectionApproval", function (response) {$('#' + domId).html(response);});}function loadGrnCorrectionDetail(invoiceId) {doGetAjaxRequestHandler(context + "/grnCorrectionDetail?invoiceId=" + invoiceId, function (response) {$('.grnCorrectionDetailContainer').html(response);});}function loadGrnCorrectionRequestDetail(requestId) {doGetAjaxRequestHandler(context + "/grnCorrectionRequestDetail?requestId=" + requestId, function (response) {$('.grnCorrectionRequestDetailContainer').html(response);});}/*** Shows what the sending warehouse can actually release of an item, on the row where a quantity is being typed.** Only our own warehouses answer with this; an outside vendor's pricing response carries no availability, and its row* is left exactly as it was. Stock that is entirely promised is shown as zero with the orders holding it named, which* is what lets someone chase those orders instead of only seeing a smaller number.*/function renderMovementAvailability($row, pricing) {var $icon = $row.find('.mv-avail-icon');if (!pricing || typeof pricing.movableAltogether === 'undefined') {$icon.hide();$row.removeData('movableQty');return;}// Everything that can move: stock at more than one cost is raised as one PO per cost, automatically, on create.var cap = pricing.movableAltogether;$row.data('movableQty', cap);// Reads as a sum - in stock + arriving - promised - so the number that can move is never left unexplained.var rows = mvAvailLine('In stock', pricing.inStock, '');if (pricing.arrivingTotal > 0) {rows += mvAvailLine('Arriving (not yet received)', '+ ' + pricing.arrivingTotal, '');$.each(pricing.arriving || [], function (i, po) {rows += mvAvailLine(mvEscape(po.poNumber) + ' · ' + mvEscape(po.poDate)+ (po.destination ? ' ← ' + mvEscape(po.destination) : ''), po.qty, ' mv-po');});}if (pricing.promisedTotal > 0) {rows += mvAvailLine('Promised on open orders', '− ' + pricing.promisedTotal, '');$.each(pricing.promised || [], function (i, po) {rows += mvAvailLine(mvEscape(po.poNumber) + ' · ' + mvEscape(po.poDate)+ (po.destination ? ' → ' + mvEscape(po.destination) : ''), po.qty, ' mv-po');});}rows += mvAvailLine('Can move', cap, ' mv-total');var note = '';if (pricing.costSplit) {note += '<div class="mv-note">Stock at more than one cost — this will be raised as separate POs'+ ' automatically.</div>';}if (cap === 0) {note += '<div class="mv-note">' + (pricing.promisedTotal > 0? 'Nothing can move until one of the orders above is completed or closed.': 'Nothing can move from here yet.') + '</div>';}$icon.attr('data-mv-content', '<div class="mv-avail">' + rows + '</div>' + note);$icon.removeClass('mv-none mv-part');if (cap === 0) {$icon.addClass('mv-none');} else if (pricing.promisedTotal > 0) {$icon.addClass('mv-part');}$icon.show();$icon.popover('destroy');$icon.popover({html: true,container: 'body',placement: 'left',trigger: 'hover focus',title: 'Stock at ' + mvEscape(pricing.sourceName),content: function () {return $(this).attr('data-mv-content');}});checkMovableQty($row, parseInt($row.find('.qty').val()));}/** Flags a quantity that the order cannot take, while it is being typed rather than on submit. */function checkMovableQty($row, qty) {var cap = $row.data('movableQty');var $qty = $row.find('.qty');var $warn = $row.find('.mv-qty-warn');if (typeof cap === 'undefined' || isNaN(qty)) {$qty.removeClass('mv-over');$warn.hide().empty();return;}if (qty > cap) {$qty.addClass('mv-over');$warn.text(cap === 0 ? 'None can move right now' : 'Only ' + cap + ' can move').show();} else {$qty.removeClass('mv-over');$warn.hide().empty();}}/*** One line of the breakdown.** Built from divs and spans on purpose: Bootstrap's popover sanitizer whitelists neither tables nor their rows, and* strips a non-whitelisted element together with everything inside it - a table here renders as nothing at all.*/function mvAvailLine(label, value, extraClass) {return '<div class="mv-row' + extraClass + '"><span class="mv-label">' + label+ '</span><span class="mv-val">' + value + '</span></div>';}/** Values here are warehouse and order names out of the database, so they are escaped before going into the popover. */function mvEscape(value) {return $('<div/>').text(value === null || typeof value === 'undefined' ? '' : value).html();}