CLIENT-SCRIPT--
frappe.ui.form.on("Bill of Entry", {

    after_save: async function (frm) {

        // =====================================================
        // Execute only for saved BOE in Ready for Lot Number Staging
        // =====================================================

        if (frm.is_new()) return;

        if (frm.__lot_automation_running) return;

        if (frm.doc.workflow_state !== "Ready for Lot Number Staging") return;

        frm.__lot_automation_running = true;

        try {

            frappe.dom.freeze("Generating Lot Numbers. Please wait...");

            // =================================================
            // STEP 1
            // Create Lot Number Staging
            // =================================================

            await frappe.call({
                method: "canofy_ddit.canofy_ddit.doctype.lot_number_staging.lot_number_staging.create_lot_no_staging",
                args: {
                    doc_name: frm.doc.name
                }
            });

            // Wait for database commit

            await new Promise(resolve => setTimeout(resolve, 2000));

            // =================================================
            // STEP 2
            // Fetch Latest Lot Number Staging
            // =================================================

            const lns_list = await frappe.db.get_list(
                "Lot Number Staging",
                {
                    filters: {
                        linked_be_no: frm.doc.name
                    },
                    fields: ["name"],
                    order_by: "creation desc",
                    limit: 1
                }
            );

            if (!lns_list.length) {
                throw "Lot Number Staging document was not created.";
            }

            const lns_name = lns_list[0].name;

            // =================================================
            // STEP 3
            // Load Lot Number Staging
            // =================================================

            let lns = await frappe.db.get_doc(
                "Lot Number Staging",
                lns_name
            );

            // =================================================
            // STEP 4
            // Header & Item Validations
            // =================================================

            let validation_errors = [];

            // --------------------------------------------
            // Validation 1
            // BOE Number = LNS Transaction ID
            // --------------------------------------------

            if (
                String(frm.doc.p_0_beno || "").trim() !==
                String(lns.transaction_id || "").trim()
            ) {

                validation_errors.push(
                    "BE Number and Transaction ID do not match."
                );

            }

            // --------------------------------------------
            // Validation 2
            // BOE System Doc No = LNS Source EXIM Txn ID
            // --------------------------------------------

            if (
                String(frm.doc.name || "").trim() !==
                String(lns.linked_be_no || "").trim()
            ) {

                validation_errors.push(
                    "System Doc. No. and Source EXIM Txn ID do not match."
                );

            }

            // --------------------------------------------
            // Validation 3
            // Item Count
            // --------------------------------------------

            const boe_items = frm.doc.items || [];
            const lns_items = lns.item_details || [];

            if (!boe_items.length) {

                validation_errors.push(
                    "Bill of Entry contains no items."
                );

            }

            if (!lns_items.length) {

                validation_errors.push(
                    "Lot Number Staging contains no items."
                );

            }

            if (
                boe_items.length &&
                lns_items.length &&
                boe_items.length !== lns_items.length
            ) {

                validation_errors.push(
                    `Item count mismatch. BOE : ${boe_items.length}, LNS : ${lns_items.length}.`
                );

            }

            // =================================================
            // Stop if Validation Failed
            // =================================================

            if (validation_errors.length) {

                throw validation_errors.join("<br>");

            }

            frappe.show_alert({
                message: "Lot Number Staging validation completed successfully.",
                indicator: "green"
            });

            // =================================================
            // STEP 5
            // Submit Lot Number Staging
            // =================================================

            await frappe.xcall(
                "frappe.model.workflow.apply_workflow",
                {
                    doc: lns,
                    action: "Submit"
                }
            );

            // Reload latest document

            lns = await frappe.db.get_doc(
                "Lot Number Staging",
                lns_name
            );

            // =================================================
            // STEP 6
            // Create Lot Numbers
            // =================================================

            const response = await frappe.call({

                method: "run_doc_method",

                args: {

                    docs: lns,

                    method: "create_lot_number",

                    args: {}

                }

            });

            // =================================================
            // STEP 7
            // Verify API Execution
            // =================================================

            if (response.exc) {

                throw "Lot Number creation process failed.";

            }

            // Wait for server commit

            await new Promise(resolve => setTimeout(resolve, 2000));

            frappe.show_alert({
                message: "Lot Numbers created successfully.",
                indicator: "green"
            });

            // =================================================
            // SUCCESS
            // =================================================

            frappe.msgprint({

                title: "Automation Completed",

                indicator: "green",

                message:
                    "<b>Bill of Entry Automation Completed Successfully</b><br><br>" +
                    "✓ Bill of Entry validated.<br>" +
                    "✓ Workflow moved to Ready for Lot Number Staging.<br>" +
                    "✓ Lot Number Staging created.<br>" +
                    "✓ Header validations passed.<br>" +
                    "✓ Item validations passed.<br>" +
                    "✓ Lot Number Staging submitted.<br>" +
                    "✓ Lot Numbers created successfully."

            });

            frm.reload_doc();

        }

        catch (e) {

            console.error(e);

            frappe.msgprint({

                title: "Automation Failed",

                indicator: "red",

                message: e.message || e

            });

        }

        finally {

            frappe.dom.unfreeze();

            frm.__lot_automation_running = false;

        }

    }

});
##------------------------------------------------------------------
SERVER-CLIENT--
# ==========================================================
# Bill of Entry Automation
# Trigger : After Save
# Purpose :
#   1. Validate Bill of Entry
#   2. Move Workflow
#       Pending Review
#           ↓
#       Reviewed
#           ↓
#       Ready for Lot Number Staging
# ==========================================================

# Execute only for Draft Bill of Entry
if doc.docstatus == 0 and doc.workflow_state == "Pending Review":

    validation_errors = []

    # ======================================================
    # Header Validations
    # ======================================================

    # BE Number
    if not doc.p_0_beno:
        validation_errors.append("• Bill of Entry Number is mandatory.")

    # BE Type
    if not doc.p_0_betype:
        validation_errors.append("• Bill of Entry Type is mandatory.")

    # Inventory Workflow
    if doc.inventory_workflow_allowed != 1:
        validation_errors.append(
            "• Inventory Workflow Allowed must be enabled."
        )

    # ======================================================
    # Item Validations
    # ======================================================

    if not doc.items:
        validation_errors.append(
            "• At least one item must be available in the Item table."
        )

    # ======================================================
    # Stop Processing
    # ======================================================

    if validation_errors:

        frappe.throw(
            "<br><br><b>Bill of Entry Validation Failed</b><br><br>"
            + "<br>".join(validation_errors)
        )

    # ======================================================
    # Workflow Transition
    # ======================================================

    # Stage 1
    doc.workflow_state = "Reviewed"
    doc.save(ignore_permissions=True)

    # Reload latest values
    doc.reload()

    # Stage 2
    doc.workflow_state = "Ready for Lot Number Staging"
    doc.save(ignore_permissions=True)

    # ======================================================
    # Success Message
    # ======================================================

    frappe.msgprint(
        "<b>Bill of Entry Validation Successful</b><br><br>"
        "Workflow has been assigned to "
        "<b>Ready for Lot Number Staging</b>."
    )