← back to AbramsOS

db/migrations/0007_bills_reorders.sql

57 lines

-- 0007_bills_reorders.sql
-- Adds two household-admin owner tables that also feed the calendar_reminder engine:
--   bill         — recurring/one-off money owed (utilities, rent, insurance, subscriptions,
--                  loans, cards, AND tax/government payments such as CRA — category='tax'|'government')
--   reorder_item — things bought repeatedly ("what we order a lot") + best-price tracking ("save money")
-- Idempotent. Safe to re-run.

BEGIN;

CREATE TABLE IF NOT EXISTS bill (
  id             text PRIMARY KEY,
  user_id        text NOT NULL REFERENCES user_account(id) ON DELETE CASCADE,
  name           text NOT NULL,
  payee          text,
  category       text NOT NULL DEFAULT 'other',    -- utility|rent|mortgage|insurance|subscription|loan|card|phone|internet|tax|government|other
  amount         numeric(12,2),
  currency       text NOT NULL DEFAULT 'USD',
  cadence        text NOT NULL DEFAULT 'monthly',   -- once|weekly|biweekly|monthly|quarterly|semiannual|annual
  due_date       date,                              -- NEXT date this bill is due
  autopay        boolean NOT NULL DEFAULT false,
  account_hint   text,                              -- redacted only, e.g. "Visa ••1234" — never a full PAN/account number
  url            text,                              -- pay-online / account link
  status         text NOT NULL DEFAULT 'active',    -- active|paused|closed
  last_paid_at   date,
  notes          text,
  metadata_jsonb jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at     timestamptz NOT NULL DEFAULT now(),
  updated_at     timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS bill_user_due_idx  ON bill (user_id, status, due_date);
CREATE INDEX IF NOT EXISTS bill_category_idx  ON bill (user_id, category);

CREATE TABLE IF NOT EXISTS reorder_item (
  id                   text PRIMARY KEY,
  user_id              text NOT NULL REFERENCES user_account(id) ON DELETE CASCADE,
  name                 text NOT NULL,
  merchant             text,                         -- where we usually buy it
  category             text,                         -- grocery|household|pet|office|health|other
  unit                 text,                         -- e.g. "12-pack", "1 gal", "each"
  typical_price        numeric(12,2),                -- what we usually pay
  best_price           numeric(12,2),                -- cheapest seen (drives "save money")
  best_price_source    text,                         -- where the cheapest price was
  currency             text NOT NULL DEFAULT 'USD',
  reorder_cadence_days integer,                      -- how often we repurchase
  last_ordered_at      date,
  next_due_date        date,                         -- computed: last_ordered_at + cadence
  url                  text,
  status               text NOT NULL DEFAULT 'active', -- active|paused|archived
  notes                text,
  metadata_jsonb       jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at           timestamptz NOT NULL DEFAULT now(),
  updated_at           timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS reorder_user_due_idx ON reorder_item (user_id, status, next_due_date);

COMMIT;