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.