91 lines
3.9 KiB
MySQL
91 lines
3.9 KiB
MySQL
|
|
-- Backs crates/invoice (InvoiceService, BillingProfileRepo) — GST-compliant
|
||
|
|
-- invoice generation for Tracecoin/package purchases. Never created by any
|
||
|
|
-- active migration (same root cause as everything else fixed this session):
|
||
|
|
-- init-db.sql has a much simpler, older `invoices` shape that doesn't match
|
||
|
|
-- what InvoiceService actually reads/writes (no GST breakdown, seller/
|
||
|
|
-- customer snapshot, or line items), so it wasn't used as the reference here
|
||
|
|
-- — the schema below matches crates/invoice/src/lib.rs's Invoice/LineItem
|
||
|
|
-- structs and crates/invoice/src/service.rs's queries exactly.
|
||
|
|
CREATE TABLE IF NOT EXISTS invoices (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
invoice_number VARCHAR(50) NOT NULL UNIQUE,
|
||
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
||
|
|
user_id UUID NOT NULL REFERENCES users(id),
|
||
|
|
status VARCHAR(20) NOT NULL DEFAULT 'ISSUED',
|
||
|
|
currency VARCHAR(10) NOT NULL DEFAULT 'INR',
|
||
|
|
invoice_type VARCHAR(50) NOT NULL DEFAULT 'TRACECOIN_PURCHASE',
|
||
|
|
subtotal INTEGER NOT NULL,
|
||
|
|
discount_amount INTEGER NOT NULL DEFAULT 0,
|
||
|
|
cgst_rate DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||
|
|
cgst_amount INTEGER NOT NULL DEFAULT 0,
|
||
|
|
sgst_rate DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||
|
|
sgst_amount INTEGER NOT NULL DEFAULT 0,
|
||
|
|
igst_rate DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||
|
|
igst_amount INTEGER NOT NULL DEFAULT 0,
|
||
|
|
total INTEGER NOT NULL,
|
||
|
|
reverse_charge BOOLEAN NOT NULL DEFAULT false,
|
||
|
|
seller_name VARCHAR(255) NOT NULL,
|
||
|
|
seller_address TEXT NOT NULL,
|
||
|
|
seller_gstin VARCHAR(20),
|
||
|
|
seller_pan VARCHAR(20),
|
||
|
|
seller_state_code VARCHAR(10),
|
||
|
|
place_of_supply_state VARCHAR(10),
|
||
|
|
customer_name VARCHAR(255),
|
||
|
|
customer_email VARCHAR(255),
|
||
|
|
customer_phone VARCHAR(20),
|
||
|
|
customer_billing_address TEXT,
|
||
|
|
customer_gstin VARCHAR(20),
|
||
|
|
customer_state_code VARCHAR(10),
|
||
|
|
discount_label VARCHAR(255),
|
||
|
|
notes TEXT,
|
||
|
|
pdf_object_key VARCHAR(500),
|
||
|
|
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
paid_at TIMESTAMPTZ,
|
||
|
|
voided_at TIMESTAMPTZ,
|
||
|
|
voided_by_user_id UUID REFERENCES users(id),
|
||
|
|
void_reason TEXT,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_invoices_user_id ON invoices(user_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_invoices_payment_id ON invoices(payment_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_invoices_status ON invoices(status);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS invoice_line_items (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
invoice_id UUID NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
|
||
|
|
line_number INTEGER NOT NULL,
|
||
|
|
description VARCHAR(500) NOT NULL,
|
||
|
|
hsn_sac_code VARCHAR(20),
|
||
|
|
quantity DOUBLE PRECISION NOT NULL DEFAULT 1,
|
||
|
|
unit_price_inr INTEGER NOT NULL,
|
||
|
|
line_subtotal_inr INTEGER NOT NULL,
|
||
|
|
tax_rate DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||
|
|
line_tax_inr INTEGER NOT NULL DEFAULT 0,
|
||
|
|
line_total_inr INTEGER NOT NULL,
|
||
|
|
metadata JSONB
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_invoice_line_items_invoice_id ON invoice_line_items(invoice_id);
|
||
|
|
|
||
|
|
-- Default billing details a user has on file, used to prefill invoices
|
||
|
|
-- without asking again on every purchase (BillingProfileRepo).
|
||
|
|
CREATE TABLE IF NOT EXISTS billing_profiles (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
|
|
legal_name VARCHAR(255) NOT NULL,
|
||
|
|
email VARCHAR(255),
|
||
|
|
phone VARCHAR(20),
|
||
|
|
gstin VARCHAR(20),
|
||
|
|
pan VARCHAR(20),
|
||
|
|
billing_address TEXT NOT NULL,
|
||
|
|
state_code VARCHAR(10),
|
||
|
|
is_default BOOLEAN NOT NULL DEFAULT false,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_billing_profiles_user_id ON billing_profiles(user_id);
|
||
|
|
|
||
|
|
-- InvoiceService::create allocates invoice numbers from this sequence
|
||
|
|
-- (crates/invoice/src/service.rs: `SELECT nextval('invoice_number_seq')`).
|
||
|
|
CREATE SEQUENCE IF NOT EXISTS invoice_number_seq;
|