diff --git a/example/dashboards.yaml b/example/dashboards.yaml index eb76646..ab29c2e 100644 --- a/example/dashboards.yaml +++ b/example/dashboards.yaml @@ -1068,23 +1068,29 @@ dashboards: amountsEx[`${row.month}/${row.year}`] = row.value[ledger.ccy]; } - const [year, month] = ledger.dateLast.split("-").map((x) => parseInt(x)); - const projectUntil = `${year + projectYears}-${month}-1`; - const months = helpers.iterateMonths(ledger.dateFirst, projectUntil).map((m) => `${m.month}/${m.year}`); - const lastMonthIdx = months.findIndex((m) => m === `${month}/${year}`); - const ledgerDays = (new Date(ledger.dateLast) - new Date(ledger.dateFirst)) / (1000 * 60 * 60 * 24) + 1; - const results = panel.queries[0].result; - const finalAmount = results[results.length - 1].value[ledger.ccy]; const resultsEx = panel.queries[1].result; - const totalDiffEx = resultsEx[resultsEx.length - 1].value[ledger.ccy] - resultsEx[0].value[ledger.ccy]; - const monthlyDiffEx = (totalDiffEx / ledgerDays) * (365 / 12); + const finalAmount = results[results.length - 1].value[ledger.ccy]; + const dateFirst = new Date(resultsEx[0].year, resultsEx[0].month - 1, 1); + const dateLast = new Date( + new Date(resultsEx[resultsEx.length - 1].year, resultsEx[resultsEx.length - 1].month, 1).getTime() - 1, + ); + const days = (dateLast - dateFirst) / (1000 * 60 * 60 * 24) + 1; + const totalDiff = resultsEx[resultsEx.length - 1].value[ledger.ccy] - resultsEx[0].value[ledger.ccy]; + const monthlyDiff = (totalDiff / days) * (365 / 12); + + const dateLastYear = dateLast.getFullYear(); + const dateLastMonth = dateLast.getMonth() + 1; + const dateFirstStr = `${dateFirst.getFullYear()}-${dateFirst.getMonth() + 1}-1`; + const dateProjectUntilStr = `${dateLastYear + projectYears}-${dateLastMonth}-1`; + const months = helpers.iterateMonths(dateFirstStr, dateProjectUntilStr).map((m) => `${m.month}/${m.year}`); + const lastMonthIdx = months.findIndex((m) => m === `${dateLastMonth}/${dateLastYear}`); const projection = []; let sum = finalAmount; for (let i = lastMonthIdx; i < months.length; i++) { projection[months[i]] = sum; - sum += monthlyDiffEx; + sum += monthlyDiff; } return { diff --git a/frontend/tests/e2e/__image_snapshots__/dashboard_projection.png b/frontend/tests/e2e/__image_snapshots__/dashboard_projection.png index d56c140..a75ca63 100644 Binary files a/frontend/tests/e2e/__image_snapshots__/dashboard_projection.png and b/frontend/tests/e2e/__image_snapshots__/dashboard_projection.png differ diff --git a/frontend/tests/e2e/__snapshots__/dashboards.test.js.snap b/frontend/tests/e2e/__snapshots__/dashboards.test.js.snap index 6fdfe3f..c117eff 100644 --- a/frontend/tests/e2e/__snapshots__/dashboards.test.js.snap +++ b/frontend/tests/e2e/__snapshots__/dashboards.test.js.snap @@ -9025,7 +9025,7 @@ exports[`Dashboard: HTML Snapshot Tests Projection 1`] = ` ] } ], - "script": "const currencyFormat = new Intl.NumberFormat(undefined, {\\n style: \\"currency\\",\\n currency: ledger.ccy,\\n maximumFractionDigits: 0,\\n});\\nconst projectYears = 2; // number of years to project\\n\\n// the beancount query only returns months where there was at least one matching transaction, therefore we group by month\\nconst amounts = {};\\nconst amountsEx = {};\\nfor (let row of panel.queries[0].result) {\\n amounts[\`\${row.month}/\${row.year}\`] = row.value[ledger.ccy];\\n}\\nfor (let row of panel.queries[1].result) {\\n amountsEx[\`\${row.month}/\${row.year}\`] = row.value[ledger.ccy];\\n}\\n\\nconst [year, month] = ledger.dateLast.split(\\"-\\").map((x) =\\u003e parseInt(x));\\nconst projectUntil = \`\${year + projectYears}-\${month}-1\`;\\nconst months = helpers.iterateMonths(ledger.dateFirst, projectUntil).map((m) =\\u003e \`\${m.month}/\${m.year}\`);\\nconst lastMonthIdx = months.findIndex((m) =\\u003e m === \`\${month}/\${year}\`);\\nconst ledgerDays = (new Date(ledger.dateLast) - new Date(ledger.dateFirst)) / (1000 * 60 * 60 * 24) + 1;\\n\\nconst results = panel.queries[0].result;\\nconst finalAmount = results[results.length - 1].value[ledger.ccy];\\nconst resultsEx = panel.queries[1].result;\\nconst totalDiffEx = resultsEx[resultsEx.length - 1].value[ledger.ccy] - resultsEx[0].value[ledger.ccy];\\nconst monthlyDiffEx = (totalDiffEx / ledgerDays) * (365 / 12);\\n\\nconst projection = [];\\nlet sum = finalAmount;\\nfor (let i = lastMonthIdx; i \\u003c months.length; i++) {\\n projection[months[i]] = sum;\\n sum += monthlyDiffEx;\\n}\\n\\nreturn {\\n tooltip: {\\n trigger: \\"axis\\",\\n valueFormatter: (val) =\\u003e (val ? currencyFormat.format(val) : \\"\\"),\\n },\\n legend: {\\n top: \\"bottom\\",\\n },\\n xAxis: {\\n data: months,\\n },\\n yAxis: {\\n axisLabel: {\\n formatter: currencyFormat.format,\\n },\\n },\\n series: [\\n {\\n type: \\"line\\",\\n name: \\"Net Worth\\",\\n smooth: true,\\n connectNulls: true,\\n showSymbol: false,\\n data: months.map((month) =\\u003e amounts[month]),\\n },\\n {\\n type: \\"line\\",\\n name: \\"Excluding onetime txns\\",\\n smooth: true,\\n connectNulls: true,\\n showSymbol: false,\\n data: months.map((month) =\\u003e amountsEx[month]),\\n },\\n {\\n type: \\"line\\",\\n name: \\"Projection\\",\\n lineStyle: {\\n type: \\"dashed\\",\\n },\\n showSymbol: false,\\n data: months.map((month) =\\u003e projection[month]),\\n },\\n ],\\n onClick: (event) =\\u003e {\\n if (!panel.queries[0].link) return;\\n const [month, year] = event.name.split(\\"/\\");\\n const link = panel.queries[0].link.replaceAll(\\"#\\", \\"%23\\").replace(\\"{time}\\", \`\${year}-\${month.padStart(2, \\"0\\")}\`);\\n window.open(link);\\n },\\n};\\n", + "script": "const currencyFormat = new Intl.NumberFormat(undefined, {\\n style: \\"currency\\",\\n currency: ledger.ccy,\\n maximumFractionDigits: 0,\\n});\\nconst projectYears = 2; // number of years to project\\n\\n// the beancount query only returns months where there was at least one matching transaction, therefore we group by month\\nconst amounts = {};\\nconst amountsEx = {};\\nfor (let row of panel.queries[0].result) {\\n amounts[\`\${row.month}/\${row.year}\`] = row.value[ledger.ccy];\\n}\\nfor (let row of panel.queries[1].result) {\\n amountsEx[\`\${row.month}/\${row.year}\`] = row.value[ledger.ccy];\\n}\\n\\nconst results = panel.queries[0].result;\\nconst resultsEx = panel.queries[1].result;\\nconst finalAmount = results[results.length - 1].value[ledger.ccy];\\nconst dateFirst = new Date(resultsEx[0].year, resultsEx[0].month - 1, 1);\\nconst dateLast = new Date(\\n new Date(resultsEx[resultsEx.length - 1].year, resultsEx[resultsEx.length - 1].month, 1).getTime() - 1,\\n);\\nconst days = (dateLast - dateFirst) / (1000 * 60 * 60 * 24) + 1;\\nconst totalDiff = resultsEx[resultsEx.length - 1].value[ledger.ccy] - resultsEx[0].value[ledger.ccy];\\nconst monthlyDiff = (totalDiff / days) * (365 / 12);\\n\\nconst dateLastYear = dateLast.getFullYear();\\nconst dateLastMonth = dateLast.getMonth() + 1;\\nconst dateFirstStr = \`\${dateFirst.getFullYear()}-\${dateFirst.getMonth() + 1}-1\`;\\nconst dateProjectUntilStr = \`\${dateLastYear + projectYears}-\${dateLastMonth}-1\`;\\nconst months = helpers.iterateMonths(dateFirstStr, dateProjectUntilStr).map((m) =\\u003e \`\${m.month}/\${m.year}\`);\\nconst lastMonthIdx = months.findIndex((m) =\\u003e m === \`\${dateLastMonth}/\${dateLastYear}\`);\\n\\nconst projection = [];\\nlet sum = finalAmount;\\nfor (let i = lastMonthIdx; i \\u003c months.length; i++) {\\n projection[months[i]] = sum;\\n sum += monthlyDiff;\\n}\\n\\nreturn {\\n tooltip: {\\n trigger: \\"axis\\",\\n valueFormatter: (val) =\\u003e (val ? currencyFormat.format(val) : \\"\\"),\\n },\\n legend: {\\n top: \\"bottom\\",\\n },\\n xAxis: {\\n data: months,\\n },\\n yAxis: {\\n axisLabel: {\\n formatter: currencyFormat.format,\\n },\\n },\\n series: [\\n {\\n type: \\"line\\",\\n name: \\"Net Worth\\",\\n smooth: true,\\n connectNulls: true,\\n showSymbol: false,\\n data: months.map((month) =\\u003e amounts[month]),\\n },\\n {\\n type: \\"line\\",\\n name: \\"Excluding onetime txns\\",\\n smooth: true,\\n connectNulls: true,\\n showSymbol: false,\\n data: months.map((month) =\\u003e amountsEx[month]),\\n },\\n {\\n type: \\"line\\",\\n name: \\"Projection\\",\\n lineStyle: {\\n type: \\"dashed\\",\\n },\\n showSymbol: false,\\n data: months.map((month) =\\u003e projection[month]),\\n },\\n ],\\n onClick: (event) =\\u003e {\\n if (!panel.queries[0].link) return;\\n const [month, year] = event.name.split(\\"/\\");\\n const link = panel.queries[0].link.replaceAll(\\"#\\", \\"%23\\").replace(\\"{time}\\", \`\${year}-\${month.padStart(2, \\"0\\")}\`);\\n window.open(link);\\n },\\n};\\n", "title": "Net Worth \\ud83d\\udcb0", "type": "echarts" }