import { defineStore } from "pinia"; import { GET, POST } from "../api"; import { useBudgetsStore } from "./budget"; import { useSessionStore } from "./session"; import { useTransactionsStore } from "./transactions"; interface State { Accounts: Map; CurrentAccountID: string | null; Categories: Map; Months: Map>>; Available: Map>; Assignments: []; } export interface Account { ID: string; Name: string; OnBudget: boolean; IsOpen: boolean; ClearedBalance: number; WorkingBalance: number; ReconciledBalance: number; Transactions: string[]; LastReconciled: NullDate; } interface NullDate { Valid: boolean; Time: Date; } export interface Category { ID: string; Group: string; Name: string; AvailableLastMonth: number; Assigned: number; Activity: number; } interface BudgetedAmounts { Assigned: number, Deassigned: number, Spent: number, Income: number, } export const useAccountStore = defineStore("budget/account", { state: (): State => ({ Accounts: new Map(), CurrentAccountID: null, Months: new Map>>(), Available: new Map>(), Categories: new Map(), Assignments: [], }), getters: { AccountsList(state) { return [...state.Accounts.values()]; }, AllCategoriesForMonth: (state) => (year: number, month: number) => { const yearMap = state.Months.get(year); const monthMap = yearMap?.get(month); return [...(monthMap?.values() || [])]; }, GetCategoryAvailable(state) { return (category: Category): number => { return ( category.AvailableLastMonth + category.Assigned + category.Activity ); }; }, GetIncomeCategoryID(state) { const budget = useBudgetsStore(); return budget.CurrentBudget?.IncomeCategoryID; }, GetBudgeted(state) { return (year: number, month: number) : BudgetedAmounts => { const IncomeCategoryID = this.GetIncomeCategoryID; if (IncomeCategoryID == null) return {Spent: 0, Income: 0, Assigned: 0, Deassigned: 0}; const categories = this.AllCategoriesForMonth(year, month); let assigned = 0, deassigned = 0; let spent = 0, income = 0; for (const category of categories) { if (category.ID == IncomeCategoryID) continue; if(category.Activity > 0) income += category.Activity; else spent += category.Activity; if(category.Assigned > 0) assigned += category.Assigned; else deassigned += category.Assigned; } return { Assigned: assigned, Deassigned: deassigned, Spent: spent, Income: income }; }; }, GetIncomeAvailable(state) { return (year: number, month: number) => { const yearMapAv = this.Available.get(year); return yearMapAv?.get(month); }; }, CategoryGroupsForMonth(state) { return (year: number, month: number) => { const categories = this.AllCategoriesForMonth(year, month); const categoryGroups = []; let prev = undefined; for (const category of categories) { //if (category.ID == this.GetIncomeCategoryID) continue; if (prev == undefined || category.Group != prev.Name) { prev = { Name: category.Group, Available: this.GetCategoryAvailable(category), AvailableLastMonth: category.AvailableLastMonth, Activity: category.Activity, Assigned: category.Assigned, }; categoryGroups.push({ ...prev, Expand: prev.Name != "Hidden Categories", }); } else { categoryGroups[categoryGroups.length - 1].Available += this.GetCategoryAvailable(category); categoryGroups[ categoryGroups.length - 1 ].AvailableLastMonth += category.AvailableLastMonth; categoryGroups[categoryGroups.length - 1].Activity += category.Activity; categoryGroups[categoryGroups.length - 1].Assigned += category.Assigned; continue; } } return categoryGroups; }; }, CategoriesForMonthAndGroup(state) { return (year: number, month: number, group: string) => { const categories = this.AllCategoriesForMonth(year, month); return categories.filter((x) => x.Group == group); }; }, GetAccount(state) { return (accountid: string) => { return this.Accounts.get(accountid); }; }, CurrentAccount(state): Account | undefined { if (state.CurrentAccountID == null) return undefined; return this.GetAccount(state.CurrentAccountID); }, OnBudgetAccounts(state) { return [...state.Accounts.values()].filter((x) => x.OnBudget); }, OnBudgetAccountsBalance(state): number { return this.OnBudgetAccounts.reduce( (prev, curr) => prev + Number(curr.ClearedBalance), 0 ); }, OffBudgetAccounts(state) { return [...state.Accounts.values()].filter((x) => !x.OnBudget); }, OffBudgetAccountsBalance(state): number { return this.OffBudgetAccounts.reduce( (prev, curr) => prev + Number(curr.ClearedBalance), 0 ); }, }, actions: { async SetCurrentAccount(budgetid: string, accountid: string) { if (budgetid == null) return; this.CurrentAccountID = accountid; if (accountid == null) return; const account = this.GetAccount(accountid)!; useSessionStore().setTitle(account.Name); await this.FetchAccount(account); }, async FetchAccount(account: Account) { const result = await GET( "/account/" + account.ID + "/transactions" ); const response = await result.json(); const transactionsStore = useTransactionsStore(); transactionsStore.AddTransactions( response.Transactions ); }, async FetchMonthBudget(budgetid: string, year: number, month: number) { const result = await GET( "/budget/" + budgetid + "/" + year + "/" + (month + 1) ); const response = await result.json(); if ( response.Categories == undefined || response.Categories.length <= 0 ) return; this.addCategoriesForMonth(year, month, response.Categories, response.AvailableBalance); }, async EditAccount( accountid: string, name: string, onBudget: boolean, isOpen: boolean ) { const result = await POST( "/account/" + accountid, JSON.stringify({ name: name, onBudget: onBudget, isOpen: isOpen, }) ); const response = await result.json(); useBudgetsStore().MergeBudgetingData(response); if (!isOpen) { this.Accounts.delete(accountid); } }, addCategoriesForMonth( year: number, month: number, categories: Category[], available: number ): void { this.$patch((state) => { const yearMap = state.Months.get(year) || new Map>(); const monthMap = yearMap.get(month) || new Map(); for (const category of categories) { monthMap.set(category.ID, category); } yearMap.set(month, monthMap); state.Months.set(year, yearMap); const yearMapAv = state.Available.get(year) || new Map(); yearMapAv.set(month, available); state.Available.set(year, yearMapAv); }); }, logout() { this.$reset(); }, }, });