/* * accounts.js * Copyright (c) 2023 james@firefly-iii.org * * This file is part of Firefly III (https://github.com/firefly-iii). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import {getVariable} from "../../store/get-variable.js"; import {setVariable} from "../../store/set-variable.js"; import Dashboard from "../../api/v2/chart/account/dashboard.js"; import formatMoney from "../../util/format-money.js"; import Get from "../../api/v2/model/account/get.js"; import {Chart} from 'chart.js'; import {getDefaultChartSettings} from "../../support/default-chart-settings.js"; // this is very ugly, but I have no better ideas at the moment to save the currency info // for each series. let currencies = []; let chart = null; let chartData = null; let afterPromises = false; const CHART_CACHE_KEY = 'dashboard-accounts-chart'; const ACCOUNTS_CACHE_KEY = 'dashboard-accounts-data'; export default () => ({ loading: false, loadingAccounts: false, accountList: [], autoConversion: false, chartOptions: null, switchAutoConversion() { this.autoConversion = !this.autoConversion; setVariable('autoConversion', this.autoConversion); }, getFreshData() { const cacheValid = window.store.get('cacheValid'); let cachedData = window.store.get(CHART_CACHE_KEY); if (cacheValid && typeof cachedData !== 'undefined') { let options = window.store.get(CHART_CACHE_KEY); this.drawChart(options); this.loading = false; console.log('Chart from cache'); } if (!cacheValid || typeof cachedData === 'undefined') { const dashboard = new Dashboard(); dashboard.dashboard(new Date(window.store.get('start')), new Date(window.store.get('end')), null).then((response) => { this.chartData = response.data; // cache generated options: let options = this.generateOptions(this.chartData); window.store.set(CHART_CACHE_KEY, options); this.drawChart(options); this.loading = false; console.log('Chart FRESH'); }); } }, generateOptions(data) { currencies = []; let options = getDefaultChartSettings('line'); for (let i = 0; i < data.length; i++) { if (data.hasOwnProperty(i)) { let yAxis = 'y'; let current = data[i]; let dataset = {}; let collection = []; // if index = 0, push all keys as labels: if (0 === i) { options.data.labels = Object.keys(current.entries); } dataset.label = current.label; // use the "native" currency code and use the "native_entries" as array if (this.autoConversion) { currencies.push(current.native_code); dataset.currency_code = current.native_code; collection = Object.values(current.native_entries); yAxis = 'y' + current.native_code; } if (!this.autoConversion) { yAxis = 'y' + current.currency_code; dataset.currency_code = current.currency_code; currencies.push(current.currency_code); collection = Object.values(current.entries); } dataset.yAxisID = yAxis; dataset.data = collection; // add data set to the correct Y Axis: options.data.datasets.push(dataset); } } // for each entry in currencies, add a new y-axis: for (let currency in currencies) { if (currencies.hasOwnProperty(currency)) { let code = 'y' + currencies[currency]; if (!options.options.scales.hasOwnProperty(code)) { options.options.scales[code] = { id: currency, type: 'linear', position: 1 === parseInt(currency) ? 'right' : 'left', ticks: { callback: function (value, index, values) { return formatMoney(value, currencies[currency]); } } }; } } } return options; }, loadChart() { if (true === this.loading) { return; } this.loading = true; if (null === chartData) { this.getFreshData(); return; } this.drawChart(this.generateOptions(chartData)); this.loading = false; }, drawChart(options) { if (null !== chart) { // chart already in place, refresh: chart.options = options.options; chart.data = options.data; chart.update(); return; } chart = new Chart(document.querySelector("#account-chart"), options); }, loadAccounts() { // console.log('loadAccounts'); if (true === this.loadingAccounts) { // console.log('loadAccounts CANCELLED'); return; } this.loadingAccounts = true; if (this.accountList.length > 0) { // console.log('NO need to load account data'); this.loadingAccounts = false; return; } const cacheValid = window.store.get('cacheValid'); let cachedData = window.store.get(ACCOUNTS_CACHE_KEY); if (cacheValid && typeof cachedData !== 'undefined') { this.accountList = cachedData; this.loadingAccounts = false; return; } // console.log('loadAccounts continue!'); const max = 10; let totalAccounts = 0; let count = 0; let accounts = []; Promise.all([getVariable('frontpageAccounts'),]).then((values) => { totalAccounts = values[0].length; //console.log(values[0]); for (let i in values[0]) { let account = values[0]; if (account.hasOwnProperty(i)) { let accountId = account[i]; // grab account info for box: (new Get).get(accountId, new Date(window.store.get('end'))).then((response) => { let parent = response.data.data; // get groups for account: (new Get).transactions(parent.id, 1).then((response) => { let groups = []; for (let ii = 0; ii < response.data.data.length; ii++) { if (ii >= max) { break; } let current = response.data.data[ii]; let group = { title: null === current.attributes.group_title ? '' : current.attributes.group_title, id: current.id, transactions: [], }; for (let iii = 0; iii < current.attributes.transactions.length; iii++) { let currentTransaction = current.attributes.transactions[iii]; //console.log(currentTransaction); group.transactions.push({ description: currentTransaction.description, id: current.id, type: currentTransaction.type, amount_raw: parseFloat(currentTransaction.amount), amount: formatMoney(currentTransaction.amount, currentTransaction.currency_code), native_amount_raw: parseFloat(currentTransaction.native_amount), native_amount: formatMoney(currentTransaction.native_amount, currentTransaction.native_code), }); } groups.push(group); } // console.log(parent); accounts.push({ name: parent.attributes.name, id: parent.id, balance_raw: parseFloat(parent.attributes.current_balance), balance: formatMoney(parent.attributes.current_balance, parent.attributes.currency_code), native_balance_raw: parseFloat(parent.attributes.native_current_balance), native_balance: formatMoney(parent.attributes.native_current_balance, parent.attributes.native_code), groups: groups, }); count++; if (count === totalAccounts) { this.accountList = accounts; this.loadingAccounts = false; window.store.set(ACCOUNTS_CACHE_KEY, accounts); } }); }); } } //this.loadingAccounts = false; }); }, init() { // console.log('accounts init'); Promise.all([getVariable('viewRange', '1M'), getVariable('autoConversion', false), getVariable('language', 'en-US')]).then((values) => { //console.log('accounts after promises'); this.autoConversion = values[1]; afterPromises = true; // main dashboard chart: this.loadChart(); this.loadAccounts(); }); window.store.observe('end', () => { if (!afterPromises) { return; } // console.log('accounts observe end'); chartData = null; this.accountList = []; // main dashboard chart: this.loadChart(); this.loadAccounts(); }); window.store.observe('autoConversion', () => { if (!afterPromises) { return; } // console.log('accounts observe autoconversion'); this.loadChart(); this.loadAccounts(); }); }, });