add money tracker
All checks were successful
Test and deploy staging / staging (push) Successful in 1m46s

This commit is contained in:
2026-09-23 17:02:32 +00:00
parent 753b2e5d6a
commit 0638ead87f
15 changed files with 2532 additions and 3 deletions

View File

@@ -0,0 +1,130 @@
import { date, index, integer, numeric, pgTable, text, unique } from 'drizzle-orm/pg-core';
import { id, timestamps } from './helpers';
import { user } from './auth';
export const moneyAccounts = pgTable(
'money_accounts',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
openingBalanceCents: integer('opening_balance_cents').notNull().default(0),
...timestamps
},
(table) => [unique().on(table.userId, table.name), index().on(table.userId)]
);
export const moneyTransactions = pgTable(
'money_transactions',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
accountId: integer('account_id')
.notNull()
.references(() => moneyAccounts.id),
date: date('date', { mode: 'string' }).notNull(),
description: text('description').notNull(),
amountCents: integer('amount_cents').notNull(),
...timestamps
},
(table) => [index().on(table.userId, table.date, table.id), index().on(table.accountId)]
);
export const moneyRecurring = pgTable(
'money_recurring',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
amountCents: integer('amount_cents').notNull(),
cadence: text('cadence').notNull(),
accountId: integer('account_id').references(() => moneyAccounts.id, { onDelete: 'set null' }),
...timestamps
},
(table) => [index().on(table.userId)]
);
export const moneyForecasts = pgTable(
'money_forecasts',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
date: date('date', { mode: 'string' }).notNull(),
description: text('description').notNull(),
amountCents: integer('amount_cents').notNull(),
accountId: integer('account_id').references(() => moneyAccounts.id, { onDelete: 'set null' }),
...timestamps
},
(table) => [index().on(table.userId, table.date)]
);
export const moneyUtilitySettings = pgTable(
'money_utility_settings',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
kind: text('kind').notNull(),
ratePerUnit: numeric('rate_per_unit', { precision: 12, scale: 4 }).notNull().default('0'),
monthlyAdvanceCents: integer('monthly_advance_cents').notNull().default(0),
billingStart: date('billing_start', { mode: 'string' }),
...timestamps
},
(table) => [unique().on(table.userId, table.kind)]
);
export const moneyUtilityReadings = pgTable(
'money_utility_readings',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
kind: text('kind').notNull(),
date: date('date', { mode: 'string' }).notNull(),
reading: numeric('reading', { precision: 12, scale: 3 }).notNull(),
...timestamps
},
(table) => [index().on(table.userId, table.kind, table.date)]
);
export const moneyWishlist = pgTable(
'money_wishlist',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
estimatedCents: integer('estimated_cents'),
notes: text('notes'),
...timestamps
},
(table) => [index().on(table.userId)]
);
export const moneyInsurances = pgTable(
'money_insurances',
{
id: id(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
provider: text('provider'),
coverage: text('coverage'),
premiumCents: integer('premium_cents'),
cadence: text('cadence').notNull().default('monthly'),
...timestamps
},
(table) => [index().on(table.userId)]
);