From 850c0d7a6a05d1a234c9112591c74ed6311586d2 Mon Sep 17 00:00:00 2001 From: Russ Kollmansberger Date: Sun, 24 May 2026 09:12:52 -0500 Subject: [PATCH] Add budget.js --- budget.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 budget.js diff --git a/budget.js b/budget.js new file mode 100644 index 0000000..5245607 --- /dev/null +++ b/budget.js @@ -0,0 +1,74 @@ +const api = require('@actual-app/api'); +const { closeBudget, ensurePayee, getAccountBalance, getAccountNote, getTagValue, openBudget, showPercent, sleep, + getBudgetMonth, getCategoryGroups, getCategories } = require('./utils'); +require("dotenv").config(); + +(async function () { + await openBudget(); + + var today = new Date(); + var yyyy = today.getFullYear(); + var mm = (today.getMonth() + 1).toString().padStart(2, '0'); + var budgetMonth = yyyy + '-' + mm; + const budget = await api.getBudgetMonth(budgetMonth); + + console.log(''); + console.log('~~!!'); // Make it easy to parse for e-mail + + console.log('Budget for Month: ' + budgetMonth); + console.log('Total Budget: $' + (budget.totalBudgeted * .01).toFixed(2).padStart(8, ' ')); + console.log('Income: $' + (budget.totalIncome * .01).toFixed(2).padStart(8, ' ')); + console.log('Spent: $' + (budget.totalSpent * .01).toFixed(2).padStart(8, ' ')); + console.log('Balance: $' + (budget.totalBalance * .01).toFixed(2).padStart(8, ' ')); + console.log('----------------------------------------------------'); + + const categoryGroups = await api.getCategoryGroups(); + for (const categoryGroup of budget.categoryGroups) { + if (categoryGroup.hidden || categoryGroup.budgeted == 0 || categoryGroup.is_income) { + continue; + } + + const categorySpentPercent = ((categoryGroup.spent * .01) / (categoryGroup.budgeted * .01) * -100); + const categoryBalancePercent = ((categoryGroup.balance * .01) / (categoryGroup.budgeted * .01) * 10); + + console.log(categoryGroup.name.padEnd(21, ' ') + ' ' + categorySpentPercent.toFixed(1).trim().padStart(7, ' ') + '% spent of $' + (categoryGroup.budgeted * .01).toFixed(2).trim().padStart(8, ' ')); + } + + console.log('!!~~'); // Make it easy to parse for e-mail + console.log(''); + + var transactionDate = new Date(); + transactionDate.setDate(transactionDate.getDate() - 1); + let txDate = transactionDate.toISOString().substring(0, 10); + console.log('Transactions on ' + txDate); + console.log('DATE ACCOUNT PAYEE NOTES / MEMO AMOUNT'); + console.log('-------------------------------------------------------------------------------------------------------------------------'); + const payees = await api.getPayees(); + const accounts = await api.getAccounts(); + ( await api.runQuery( + api.q('transactions') + .filter({ + date: { $gte: txDate }, + }) + .select('*') + ) + ).data.map(row => { + let payeeName = payees.find(p => p.id === row.payee)?.name; + let accountName = accounts.find(a => a.id === row.account)?.name; + console.log(row.date + ' ' + + fixedStringLength(accountName, 20) + ' ' + + fixedStringLength(payeeName, 25) + ' ' + + fixedStringLength(row.notes, 45) + ' ' + + ('$' + (row.amount / 100).toFixed(2).trim()).padStart(11, ' ')); + }); + + + await closeBudget(); +})(); + +function fixedStringLength(str, length) { + if (str === null) { + str = ''; + } + return str.padEnd(length).substring(0, length); +} \ No newline at end of file