Frappe CRM Kanban Board Customization

I Hi I created a customization to CRM. I add Item Product List based on the item inside erpnext, everything has work for now. The problem is, i created this script to calculate deal expected value based on the probability field. the probability i set to automaticly update based on the status. the script worked.

but how to apply this script to Kanban board view? changing status through kanban board break the script, not keeping all of the script working.

Here is my working script for CRM Form Script:

// =========================
// CRM DEAL (PARENT)
// =========================

class CRMDeal {

    async after_load() {
        // Kanban-safe probability update
        let status = this.doc.status;

        if (status) {
            let result = await call("frappe.client.get_value", {
                doctype: "CRM Deal Status",
                filters: { deal_status: status },
                fieldname: ["probability"]
            });

            if (result && result.probability != null) {
                this.doc.probability = result.probability;
            }
        }

        // Recalculate expected deal value
        let deal_value = this.doc.deal_value  0;
        let probability = this.doc.probability  0;

        this.doc.expected_deal_value = (probability / 100) * deal_value;
    }

    async refresh() {
    // Fetch probability based on status (Kanban-safe)
    let status = this.doc.status;

    if (status) {
        let result = await call("frappe.client.get_value", {
            doctype: "CRM Deal Status",
            filters: { deal_status: status },
            fieldname: ["probability"]
        });

        if (result && result.probability != null) {
            this.doc.probability = result.probability;
        }
    }

    // Recalculate expected deal value
    let deal_value = this.doc.deal_value  0;
    let probability = this.doc.probability  0;

    this.doc.expected_deal_value = (probability / 100) * deal_value;
    }

    update_total() {
        let total = 0;

        (this.doc.custom_crm_item  []).forEach((d) => {
            total += d.amount  0;
        });

        // Update total fields
        this.doc.custom_crm_total = total;
        this.doc.deal_value = total;
// Recalculate expected deal value
        let probability = this.doc.probability  0;
        this.doc.expected_deal_value = (probability / 100) * total;
    }

    probability() {
        let deal_value = this.doc.deal_value  0;
        let probability = this.doc.probability  0;

        this.doc.expected_deal_value = (probability / 100) * deal_value;
    }

    async status() {
        let status = this.doc.status;
        if (!status) return;

        // Fetch probability from CRM Deal Status table
        let result = await call("frappe.client.get_value", {
            doctype: "CRM Deal Status",
            filters: { deal_status: status },
            fieldname: ["probability"]
        });

        if (result && result.probability != null) {
            this.doc.probability = result.probability;
        }

        // Recalculate expected deal value
        let deal_value = this.doc.deal_value  0;
        let probability = this.doc.probability  0;

        this.doc.expected_deal_value = (probability / 100) * deal_value;
    }
}



// =========================
// CRM ITEM (CHILD TABLE)
// =========================

class CRMItem {

    custom_crm_item_add() {
        let row = this.doc.getRow("custom_crm_item");

        if (!row.qty) {
            row.qty = 1;
        }

        if (row.price_list_rate && !row.rate) {
            row.rate = row.price_list_rate;
        }

        row.amount = (row.qty  0) * (row.rate  0);

        this.doc.trigger("update_total");
    }

    custom_crm_item_remove() {
        this.doc.trigger("update_total");
    }

    async item_code(idx) {
        let row = this.doc.getRow("custom_crm_item", idx);
        if (!row.item_code) return;

        // Fetch item details
        let item = await call("frappe.client.get_value", {
            doctype: "Item",
            filters: { name: row.item_code },
            fieldname: ["item_name", "item_group", "stock_uom", "brand"]
        });

        if (item) {
            row.item_name = item.item_name;
            row.item_group = item.item_group;
            row.uom = item.stock_uom;
            row.brand = item.brand;
        }

        // Always use default selling price list
        let settings = await call("frappe.client.get_value", {
            doctype: "Selling Settings",
            fieldname: ["selling_price_list"]
        });

        let price_list = settings?.selling_price_list;

        if (!price_list) {
            frappe.msgprint("No default Selling Price List found in Selling Settings.");
            return;
        }

        // Fetch Item Price
        let price = await call("frappe.client.get_value", {
            doctype: "Item Price",
            filters: {
                item_code: row.item_code,
                price_list: price_list
            },
            fieldname: ["price_list_rate"]
        });

        if (price) {
            row.price_list_rate = price.price_list_rate  0;

            if (!row.rate) {
                row.rate = row.price_list_rate;
            }
        }

        row.amount = (row.qty  0) * (row.rate  0);

        this.doc.trigger("update_total");
    }

    qty(idx) {
        let row = this.doc.getRow("custom_crm_item", idx);
        row.amount = (row.qty  0) * (row.rate  0);
        this.doc.trigger("update_total");
    }

    rate(idx) {
        let row = this.doc.getRow("custom_crm_item", idx);
        row.amount = (row.qty  0) * (row.rate  0);
        this.doc.trigger("update_total");
    }
}

I learned that kanban board directly change the status to the database, so I need to tweak the main kanban code inside Frappe CRM, is there any way I do this using API, so I dont touch the main CRM code?

I have tried to implement server script and custom app, but it seems that the Frappe CRM does not allow any hook to the kanban board.