100 lines
2.7 KiB
TypeScript
100 lines
2.7 KiB
TypeScript
|
|
import { serverAuth } from "./pb";
|
||
|
|
import type { RecordModel } from "pocketbase";
|
||
|
|
|
||
|
|
export type Asset = {
|
||
|
|
id: string;
|
||
|
|
code: string;
|
||
|
|
name: string;
|
||
|
|
category: string;
|
||
|
|
gl_account: string;
|
||
|
|
supplier: string;
|
||
|
|
monthly_excl: number;
|
||
|
|
monthly_incl: number;
|
||
|
|
purchase_value: number;
|
||
|
|
} & RecordModel;
|
||
|
|
|
||
|
|
export type MonthlyIncome = {
|
||
|
|
id: string;
|
||
|
|
asset: string;
|
||
|
|
year: number;
|
||
|
|
month: number;
|
||
|
|
income: number;
|
||
|
|
fixed_expenses: number;
|
||
|
|
running_cost: number;
|
||
|
|
} & RecordModel;
|
||
|
|
|
||
|
|
export type MonthlyRunningCost = {
|
||
|
|
id: string;
|
||
|
|
asset: string;
|
||
|
|
year: number;
|
||
|
|
month: number;
|
||
|
|
amount: number;
|
||
|
|
} & RecordModel;
|
||
|
|
|
||
|
|
export type Overhead = {
|
||
|
|
id: string;
|
||
|
|
section: string;
|
||
|
|
description: string;
|
||
|
|
gl_account: string;
|
||
|
|
supplier: string;
|
||
|
|
qty: number;
|
||
|
|
unit_price: number;
|
||
|
|
amount_excl: number;
|
||
|
|
vat: number;
|
||
|
|
amount_incl: number;
|
||
|
|
} & RecordModel;
|
||
|
|
|
||
|
|
export async function getAssets(): Promise<Asset[]> {
|
||
|
|
const pb = await serverAuth();
|
||
|
|
return pb.collection("assets").getFullList<Asset>({ sort: "category,name" });
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getAssetsByCategory(category: string): Promise<Asset[]> {
|
||
|
|
const all = await getAssets();
|
||
|
|
return all.filter((a) => a.category === category);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getMonthlyIncome(year = 2026): Promise<MonthlyIncome[]> {
|
||
|
|
const pb = await serverAuth();
|
||
|
|
return pb.collection("monthly_income").getFullList<MonthlyIncome>({
|
||
|
|
filter: `year = ${year}`,
|
||
|
|
sort: "month",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Part 1.2: YTD aggregations operate on actuals only by default.
|
||
|
|
// `is_forecast` is a boolean field on monthly_income; rows where it's true are excluded.
|
||
|
|
export async function getActualMonthlyIncome(year = 2026): Promise<MonthlyIncome[]> {
|
||
|
|
const all = await getMonthlyIncome(year);
|
||
|
|
return all.filter((r) => !(r as any).is_forecast);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns the last month that has any actual income — used to label forecast ranges.
|
||
|
|
export function lastActualMonth(rows: MonthlyIncome[]): number {
|
||
|
|
let m = 0;
|
||
|
|
for (const r of rows) if (r.income > 0 && r.month > m) m = r.month;
|
||
|
|
return m;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getRunningCosts(year = 2026): Promise<MonthlyRunningCost[]> {
|
||
|
|
const pb = await serverAuth();
|
||
|
|
return pb.collection("monthly_running_costs").getFullList<MonthlyRunningCost>({
|
||
|
|
filter: `year = ${year}`,
|
||
|
|
sort: "month",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getOverheads(): Promise<Overhead[]> {
|
||
|
|
const pb = await serverAuth();
|
||
|
|
return pb.collection("overheads").getFullList<Overhead>({ sort: "section,description" });
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getOverheadsBySection(section: string): Promise<Overhead[]> {
|
||
|
|
const all = await getOverheads();
|
||
|
|
return all.filter((o) => o.section === section);
|
||
|
|
}
|
||
|
|
|
||
|
|
export const MONTH_NAMES = [
|
||
|
|
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",
|
||
|
|
];
|