272 lines
9.4 KiB
TypeScript
272 lines
9.4 KiB
TypeScript
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<string, Account>;
|
|
CurrentAccountID: string | null;
|
|
Categories: Map<string, Category>;
|
|
Months: Map<number, Map<number, Map<string, Category>>>;
|
|
Available: Map<number, Map<number, number>>;
|
|
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<string, Account>(),
|
|
CurrentAccountID: null,
|
|
Months: new Map<number, Map<number, Map<string, Category>>>(),
|
|
Available: new Map<number, Map<number, number>>(),
|
|
Categories: new Map<string, Category>(),
|
|
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<number, Map<string, Category>>();
|
|
const monthMap =
|
|
yearMap.get(month) || new Map<string, Category>();
|
|
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<number, number>();
|
|
yearMapAv.set(month, available);
|
|
state.Available.set(year, yearMapAv);
|
|
});
|
|
},
|
|
logout() {
|
|
this.$reset();
|
|
},
|
|
},
|
|
});
|