Frappe Chart and Datatable not loading properly the first time on Edge

323 views Asked by At

I'm trying to create some charts and tables using Frappe Chart and Frappe Datatable included in the Frappe Framework. They work fine on Chrome and Safari, but for some reason it struggles to load on MS Edge the first time the page is opened, or after I clear all the cache. Some charts and tables are loaded, while others display nothing. Here is a screenshot: first-open

However, if I refresh the page, it displays everything just fine: other-open

Data in the tables are redacted, but they load correctly.

I thought it was a timing problem, but the problem still persists no mater what I tried. Here's the code that I used to create the chart:

frappe.pages['company-snapshot'].on_page_load = function(wrapper) {
    // new MyPage(wrapper);
    frappe.ui.make_app_page({
        parent: wrapper,
        title: 'Company Snapshot',
        single_column: true
    });

    wrapper.company_snapshot = new erpnext.CompanySnapshot(wrapper);

    frappe.breadcrumbs.add("Accounts");
}

erpnext.CompanySnapshot = class CompanySnapshot {
    constructor(wrapper) {
        var me = this;
        // 0 setTimeout hack - this gives time for canvas to get width and height
        setTimeout(function() {
            me.setup(wrapper);
        }, 0);
    }

    load_dom(wrapper) {
        return new Promise((resolve, reject) => {
            $(frappe.render_template("company_snapshot")).appendTo(wrapper.page.main);
            resolve(0);
        })
    }

    // Set up all default elements
    async setup(wrapper) {
        var me = this;

        // Push HTML DOM Elements to page
        console.log("Before")
        const result = await this.load_dom(wrapper);
        console.log("After:" + result);

        // Set up Period Selector
        this.elements = {
            layout: $(wrapper).find(".layout-main"),
            trend_period: $(wrapper).find("#ie-selector"),
            top_customer_period: $(wrapper).find("#top-customer-selector"),
            prev_year_income_period: $(wrapper).find("#prev-year-income-selector"),
            prev_year_expense_period: $(wrapper).find("#prev-year-expense-selector"),
            expense_breakdown_period: $(wrapper).find("#expense-breakdown-selector"),
            refresh_btn: wrapper.page.set_primary_action(__("Refresh"),
                function() { me.get_data(); }, "fa fa-refresh"),
        };

        // Set up default values
        this.options = {
            trend_period: 'this_year',
            top_customer_period: 'this_year',
            prev_year_income_period: 'yearly',
            prev_year_expense_period: 'yearly',
            expense_breakdown_period: 'this_year',
            current_year: new Date().getFullYear(),
            colors: ['green', "#ff1f1f", "orange"]
        };

        this.default_data = {
            labels: [],
            datasets: [
                {
                    name: "Total Sales",
                    chartType: 'bar',
                    values: []
                }
            ],
        };
        for (let i = this.options.current_year - 5; i < this.options.current_year + 1; i++) {
            this.default_data.labels.push(i);
            this.default_data.datasets[0].values.push(0);
        }

        this.default_invoice_table_data = {
            columns: [
                { name: 'Date', editable: false},
                { name: 'Num', width: 150, editable: false},
                { name: 'Amount', editable: false},
                { name: 'Open Balance(CAD)', editable: false}],
            data: [['-', '-', '-', '-']],
            layout: 'fluid'
        };
        
        // Set up formatter
        this.formatter = new Intl.NumberFormat('en-US', {
            style: 'currency',
            currency: 'USD',
        });

        // Set defaults and bind on change
        me.elements.trend_period.on("change", function() {
            me.options.trend_period = $(this).val();
            me.get_ie_trend(me);
        });

        me.elements.expense_breakdown_period.on("change", function() {
            me.options.expense_breakdown_period = $(this).val();
            console.log(me.options.expense_breakdown_period);
            me.get_expense_breakdown(me);
        });

        me.elements.top_customer_period.on("change", function() {
            me.options.top_customer_period = $(this).val();
            me.get_top_customer_by_sales(me);
        });

        me.elements.prev_year_income_period.on("change", function() {
            me.options.prev_year_income_period = $(this).val();
            me.get_prev_year_income_comparison(me);
        });

        me.elements.prev_year_expense_period.on("change", function() {
            me.options.prev_year_expense_period = $(this).val();
            me.get_prev_year_expense_comparison(me);
        });
        
        // Bind refresh button
        this.elements.refresh_btn.on("click", function() {
            me.get_data();
        });

        // Initialize Tables and Charts
        setTimeout(function () {me.get_data()}, 0);
    }

    get_data() {
        this.get_ie_trend(this);
        this.get_top_customer_by_sales(this);
        this.get_prev_year_income_comparison(this);
        this.get_prev_year_expense_comparison(this);
        this.get_owing_customers(this);
        this.get_expense_breakdown(this);
    }

    get_expense_breakdown(me) {
        let expense_breakdown_chart = this.render_loading("expense-breakdown-chart");
        let filters = this.build_expense_breakdown_filter(me);
        // Get the whole profit and loss statement report
        frappe.call({
            method: "frappe.desk.query_report.run",
            args: {
                filters: filters,
                report_name: "Profit and Loss Statement",
            },
            callback: function(r) {
                expense_breakdown_chart.removeAttribute("style");
                console.log(r.message);
                let total_expense = r.message.result.find(it => it.account == "Expenses - CIP");
                let cogs_expense = r.message.result.find(it => it.account == "Cost of Goods Sold - CIP");
                let operating_expense = r.message.result.find(it => it.account == "Operating Expenses - CIP");
                let other_expense = r.message.result.find(it => it.account == "Other Expenses - CIP");

                let expense_breakdown_data = {
                    labels: ["COGS", "Operating", "Other"],
                    datasets: [
                        {
                            name: "Expense",
                            chartType: "pie",
                            values: [cogs_expense.total, operating_expense.total, other_expense.total]
                        }
                    ],
                }
                me.render_chart(expense_breakdown_data, "pie", ["#ff1f1f", "#F8BE8A", "#F48928"], "#expense-breakdown-chart");
            }
        });
    }

    render_loading(element) {
        let AR_chart = document.getElementById(element)
        AR_chart.innerHTML = "Loading...";
        AR_chart.style.textAlign = "center";
        AR_chart.style.margin = "auto";

        return AR_chart;
    }

    build_expense_breakdown_filter(me) {
        let filters = {
            company:"XXXX",
            filter_based_on:"XXXX",
            period_start_date:"XXXX",
            period_end_date:"XXXX",
            from_fiscal_year:"XXXX",
            to_fiscal_year:"XXXX",
            periodicity:"XXXX",
            cost_center:[],
            project:[],
            include_default_book_entries:1
        };
        if (me.options.expense_breakdown_period == "this_year") {
            filters.from_fiscal_year = me.options.current_year;
            filters.to_fiscal_year = me.options.current_year;
            filters.period_start_date = String(me.options.current_year) + filters.period_start_date.slice(4);
            filters.period_end_date = String(me.options.current_year) + filters.period_end_date.slice(4);
        }
        else if (me.options.expense_breakdown_period == "last_year") {
            filters.from_fiscal_year = me.options.current_year - 1;
            filters.to_fiscal_year = me.options.current_year - 1;
            filters.period_start_date = String(me.options.current_year - 1) + filters.period_start_date.slice(4);
            filters.period_end_date = String(me.options.current_year - 1) + filters.period_end_date.slice(4);
        }

        return filters;
    }

    get_ie_trend(me) {
        frappe.call({
            method: "erpnext.accounts.page.company_snapshot.company_snapshot.get_income_expense_trend",
            args: {
                trend_period: this.options.trend_period
            },
            callback: function(r) {
                me.render_chart(r.message, "bar", me.options.colors, "#ie-trends-chart");
            }
        });
    }

    get_top_customer_by_sales(me) {
        frappe.call({
            method: "erpnext.accounts.page.company_snapshot.company_snapshot.get_top_customers_by_sales",
            args: {
                period: this.options.top_customer_period
            },
            callback: function(r) {
                // console.log(r.message);
                me.render_chart(r.message, "bar", me.options.colors, "#top-customers-chart");
            }
        });
    }

    get_prev_year_income_comparison(me) {
        frappe.call({
            method: "erpnext.accounts.page.company_snapshot.company_snapshot.get_prev_year_income_comparison",
            args: {
                period: this.options.prev_year_income_period
            },
            callback: function(r) {
                me.render_chart(r.message, "bar", me.options.colors, "#prev-year-income-chart");
            }
        });
    }

    get_prev_year_expense_comparison(me) {
        frappe.call({
            method: "erpnext.accounts.page.company_snapshot.company_snapshot.get_prev_year_expense_comparison",
            args: {
                period: this.options.prev_year_expense_period
            },
            callback: function(r) {
                me.render_chart(r.message, "bar", me.options.colors, "#prev-year-expense-chart");
            }
        });
    }

    get_owing_customers(me) {
        frappe.call({
            method: "erpnext.accounts.page.company_snapshot.company_snapshot.get_owing_customers",
            callback: function(r) {
                // console.log(r.message);
                let owing_customer_table_data = {
                    columns: [
                        { name: 'Customer', editable: false},
                        { name: 'Due Date', width: 140, editable: false},
                        { name: 'Amount Due (CAD)', editable: false}],
                    data: r.message,
                    layout: 'fluid'
                };

                for (let i = 0; i < owing_customer_table_data.data.length; i++) {
                    owing_customer_table_data.data[i][2] = me.formatter.format(owing_customer_table_data.data[i][2]);
                }
                
                me.render_table(owing_customer_table_data, "#customer-owing-table");
            }
        });
    }

    // Render table based on the data
    render_table(data, element) {
        let table = new frappe.DataTable(element, {
            columns: data.columns,
            data: data.data
        })
    }

    // Render chart based on the data
    render_chart(data, type, colors, element) {
        let chart = new frappe.Chart( element, { // or DOM element
            data: data,
            // title: title,
            type: type, // or 'bar', 'line', 'pie', 'percentage'
            height: 300,
            colors: colors,
            // ['green', "#ff1f1f", "orange"]
            tooltipOptions: {
                // formatTooltipX: d => (d + '').toUpperCase(),
                formatTooltipY: d => this.formatter.format(d),
            },
            barOptions: {
                spaceRatio: 0.7 // default: 1
            },
            axisOptions: {
                // xIsSeries: 1
                shortenYAxisNumbers: 1
            }
        });
        setTimeout(function () {chart.draw(!0)}, 1);
    }

    is_table_name(element, name) {
        while (element.parentElement) {
            element = element.parentElement;
            if (element.id == name) {
                return true;
            }
        }
        return false;
    }
};

I tried adding async await, setting different timeout in different spots based on a few suggestions online. Since I thought it was a timing issue, it thought it could be that the DOM is not loaded fully before I tried to create the charts and tables using the framework. But the problem still persists.

0

There are 0 answers