Subversion Repositories SmartDukaan

Rev

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"]').endDate

        var 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 row
                            let 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 found
            purchaseItemIds.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'] = serialNumbers
            grnItemJson['qty'] = qty
            grnItemJson['poId'] = poId
            grnItemJson['invoiceId'] = invoiceId
            grnItemJson['itemType'] = itemType
            grnItemJson['excessQty'] = excessQty

            grnItemJson['returnSerialNumbers'] = returnSerialNumbers

            grnItemArray.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].excessQty


                var serialNumbers = grnItemArray[i].serialNumbers.length
                var qty = grnItemArray[i].qty

                if (serialNumbers != qty) {
                    alert("serial number is not matched with qty")
                    return false;
                }

                if (excessQty > 0) {

                    var returnSerialNumbers = grnItemArray[i].returnSerialNumbers.length

                    if (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'] = excessQty
                if (priceMismatch === '') {
                    grnItemJson['mismatch'] = 'QtyMismatch';
                } else {
                    grnItemJson['mismatch'] = priceMismatch
                }
                grnItemJson['poId'] = poId

                grnRequestItemArray.push(grnItemJson)
            }
        });

        console.log(grnRequestItemArray)


        grnRequestJson['invoiceId'] = invoiceId;
        grnRequestJson['warehousePurchaseModel'] = grnRequestItemArray

        if (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'] = qty
        purchaseOrderJson['totalValue'] = totalValue
        purchaseOrderJson['transferPrice'] = transferPrice

        items.push(purchaseOrderJson)
    });


    for (var i = 0; i < items.length; i++) {
        console.log(items[i])

        var itemId = items[i].itemId
        var qty = items[i].amendedQty
        var transferPrice = items[i].transferPrice
        var totalValue = items[i].totalValue

        if (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'] = warehouseId
    purchaseOrder['sendPo'] = sendPo
    purchaseOrder['poDate'] = poDate;
    purchaseOrder['items'] = items

    console.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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}

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;   // defensive
    window.__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 fails
        success: 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">&#8627; 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'] = transferPrice

        items.push(purchaseOrderJson)
    });


    for (var i = 0; i < items.length; i++) {
        console.log(items[i])

        var amendedQty = items[i].amendedQty
        var qty = items[i].qty


        if (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'] = sendPo
    editpurchaseOrder['items'] = items


    if (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'] = qty
        invoiceItemJson['rate'] = rate
        invoiceItems.push(invoiceItemJson)
    });


    for (var i = 0; i < invoiceItems.length; i++) {

        var itemId = invoiceItems[i].itemId
        var qty = invoiceItems[i].qty
        var rate = invoiceItems[i].rate
        if (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'] = invoiceItems
    if (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 newline
            var 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 entered
            if (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) + ' &middot; ' + mvEscape(po.poDate)
                + (po.destination ? ' &larr; ' + mvEscape(po.destination) : ''), po.qty, ' mv-po');
        });
    }
    if (pricing.promisedTotal > 0) {
        rows += mvAvailLine('Promised on open orders', '&minus; ' + pricing.promisedTotal, '');
        $.each(pricing.promised || [], function (i, po) {
            rows += mvAvailLine(mvEscape(po.poNumber) + ' &middot; ' + mvEscape(po.poDate)
                + (po.destination ? ' &rarr; ' + 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 &mdash; 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();
}