add money tracker
All checks were successful
Test and deploy staging / staging (push) Successful in 1m46s
All checks were successful
Test and deploy staging / staging (push) Successful in 1m46s
This commit is contained in:
@@ -30,8 +30,15 @@ try {
|
||||
to_regclass('public.list_item_completions') is not null as list_completions,
|
||||
(select count(*) from information_schema.columns where table_name = 'tasks_to_topics' and column_name = 'id') as topic_link_id,
|
||||
(select count(*) from information_schema.columns
|
||||
where table_name in ('animals', 'sightings', 'photos')
|
||||
and column_name = 'id' and data_type = 'integer') as animal_serial_ids
|
||||
where table_name in ('animals', 'sightings', 'photos')
|
||||
and column_name = 'id' and data_type = 'integer') as animal_serial_ids,
|
||||
(select count(*) from information_schema.tables
|
||||
where table_schema = 'public' and table_name in (
|
||||
'money_accounts', 'money_transactions', 'money_recurring', 'money_forecasts',
|
||||
'money_utility_settings', 'money_utility_readings', 'money_wishlist', 'money_insurances'
|
||||
)) as money_tables,
|
||||
(select count(*) from information_schema.columns
|
||||
where table_name = 'money_utility_settings' and column_name = 'billing_start') as money_billing_start
|
||||
`;
|
||||
if (
|
||||
!Number(schema.user_role) ||
|
||||
@@ -41,7 +48,9 @@ try {
|
||||
!Number(schema.list_slug) ||
|
||||
!schema.list_completions ||
|
||||
!Number(schema.topic_link_id) ||
|
||||
Number(schema.animal_serial_ids) !== 3
|
||||
Number(schema.animal_serial_ids) !== 3 ||
|
||||
Number(schema.money_tables) !== 8 ||
|
||||
!Number(schema.money_billing_start)
|
||||
) {
|
||||
throw new Error(
|
||||
'Required migrations or Animaldex ID conversion have not been applied to this database'
|
||||
|
||||
25
scripts/legacy-schema-migration.sql
Normal file
25
scripts/legacy-schema-migration.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- One-off bridge from the pre-foundation site schema to the current table names.
|
||||
-- Run after the Animaldex UUID conversion, if needed, and before the auth,
|
||||
-- foundation, and catalog migrations. Review and back up each database first.
|
||||
-- Renaming keeps existing rows and foreign keys; lists did not exist in this schema.
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE public.game RENAME TO games;
|
||||
ALTER TABLE public.pokemon_game_entry RENAME TO pokemon_game_entries;
|
||||
ALTER TABLE public.user_entry RENAME TO user_entries;
|
||||
ALTER TABLE public.user_game RENAME TO user_games;
|
||||
|
||||
CREATE TABLE public.lists (
|
||||
id serial PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.list_items (
|
||||
id serial PRIMARY KEY,
|
||||
list_id integer NOT NULL REFERENCES public.lists(id) ON DELETE CASCADE,
|
||||
name text NOT NULL
|
||||
);
|
||||
CREATE INDEX list_items_list_id_idx ON public.list_items(list_id);
|
||||
|
||||
COMMIT;
|
||||
15
scripts/migrate-money-tracker.ts
Normal file
15
scripts/migrate-money-tracker.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import postgres from 'postgres';
|
||||
|
||||
if (!process.env.DB_URL) throw new Error('DB_URL is required');
|
||||
const client = postgres(process.env.DB_URL, { max: 1 });
|
||||
try {
|
||||
await client.begin(async (tx) => {
|
||||
await tx.unsafe(
|
||||
await readFile(new URL('./money-tracker-migration.sql', import.meta.url), 'utf8')
|
||||
);
|
||||
});
|
||||
console.log('Money tracker migration applied');
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
95
scripts/money-tracker-migration.sql
Normal file
95
scripts/money-tracker-migration.sql
Normal file
@@ -0,0 +1,95 @@
|
||||
CREATE TABLE IF NOT EXISTS money_accounts (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
opening_balance_cents integer NOT NULL DEFAULT 0,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (user_id, name)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_accounts_user_id_idx ON money_accounts (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_transactions (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
account_id integer NOT NULL REFERENCES money_accounts(id),
|
||||
date date NOT NULL,
|
||||
description text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_transactions_user_date_idx ON money_transactions (user_id, date, id);
|
||||
CREATE INDEX IF NOT EXISTS money_transactions_account_id_idx ON money_transactions (account_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_recurring (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
cadence text NOT NULL,
|
||||
account_id integer REFERENCES money_accounts(id) ON DELETE SET NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_recurring_user_id_idx ON money_recurring (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_forecasts (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
date date NOT NULL,
|
||||
description text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
account_id integer REFERENCES money_accounts(id) ON DELETE SET NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_forecasts_user_date_idx ON money_forecasts (user_id, date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_utility_settings (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
kind text NOT NULL,
|
||||
rate_per_unit numeric(12, 4) NOT NULL DEFAULT 0,
|
||||
monthly_advance_cents integer NOT NULL DEFAULT 0,
|
||||
billing_start date,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (user_id, kind)
|
||||
);
|
||||
ALTER TABLE money_utility_settings ADD COLUMN IF NOT EXISTS billing_start date;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_utility_readings (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
kind text NOT NULL,
|
||||
date date NOT NULL,
|
||||
reading numeric(12, 3) NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_utility_readings_user_kind_date_idx ON money_utility_readings (user_id, kind, date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_wishlist (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
estimated_cents integer,
|
||||
notes text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_wishlist_user_id_idx ON money_wishlist (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS money_insurances (
|
||||
id serial PRIMARY KEY,
|
||||
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
provider text,
|
||||
coverage text,
|
||||
premium_cents integer,
|
||||
cadence text NOT NULL DEFAULT 'monthly',
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS money_insurances_user_id_idx ON money_insurances (user_id);
|
||||
Reference in New Issue
Block a user