dodil/erp-brick-payments
DODIL ERP brick: payments
src/brick_payments/schema.sql
-- payments (pay_): receipts, allocations, refunds, collections, statements, credit, configuration and the Odoo migration.
-- Every table prefixed `pay_`, a natural primary key each (never SERIAL), money NUMERIC(18,2), rates NUMERIC(19,4),
-- creation columns last so a later column is an append. Additive only. Kept equal to infrastructure/tables.py by tests/test_schema.py.
CREATE TABLE IF NOT EXISTS pay_allocation (
source_kind VARCHAR NOT NULL,
source_id VARCHAR NOT NULL,
invoice_id VARCHAR NOT NULL,
seq INTEGER NOT NULL,
amount NUMERIC(18,2),
currency VARCHAR,
allocated_on DATE,
allocated_by VARCHAR,
reason VARCHAR,
reverses_seq INTEGER,
suggestion_id VARCHAR,
decided_how VARCHAR,
origin VARCHAR,
bp_id BIGINT,
source_amount NUMERIC(18,2),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (source_kind, source_id, invoice_id, seq)
);
CREATE TABLE IF NOT EXISTS pay_bank_account (
bank_account_id VARCHAR NOT NULL,
name VARCHAR,
currency VARCHAR,
iban_or_sort_code_masked VARCHAR,
gl_account_id VARCHAR,
origin VARCHAR,
house_bank_id VARCHAR,
company_code VARCHAR,
purposes VARCHAR,
account_name VARCHAR,
status VARCHAR,
closed_from DATE,
account_ref VARCHAR,
iban VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bank_account_id)
);
CREATE TABLE IF NOT EXISTS pay_collection (
bp_id BIGINT NOT NULL,
kind VARCHAR NOT NULL,
level INTEGER NOT NULL,
ref_id VARCHAR NOT NULL,
invoice_id VARCHAR,
occurred_on DATE,
promise_date DATE,
reason VARCHAR,
outcome VARCHAR,
note VARCHAR,
draft_text VARCHAR,
status VARCHAR,
origin VARCHAR,
recorded_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bp_id, kind, level, ref_id)
);
CREATE TABLE IF NOT EXISTS pay_credit (
bp_id BIGINT NOT NULL,
valid_from DATE NOT NULL,
credit_limit_amount NUMERIC(18,2),
currency VARCHAR,
reason VARCHAR,
set_by VARCHAR,
set_at TIMESTAMP,
origin VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bp_id, valid_from)
);
CREATE TABLE IF NOT EXISTS pay_credit_override (
bp_id BIGINT NOT NULL,
kind VARCHAR NOT NULL,
starts_at TIMESTAMP NOT NULL,
ends_on DATE,
reason VARCHAR,
set_by VARCHAR,
origin VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bp_id, kind, starts_at)
);
CREATE TABLE IF NOT EXISTS pay_match_suggestion (
suggestion_id VARCHAR NOT NULL,
payment_id VARCHAR,
proposed_lines VARCHAR,
evidence VARCHAR,
confidence NUMERIC(5,2),
model VARCHAR,
exact INTEGER,
status VARCHAR,
decided_by VARCHAR,
decided_at TIMESTAMP,
decided_lines VARCHAR,
reviewed_by VARCHAR,
reviewed_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (suggestion_id)
);
CREATE TABLE IF NOT EXISTS pay_migration_run (
run_id VARCHAR NOT NULL,
params VARCHAR,
odoo_figures VARCHAR,
counts VARCHAR,
ran_by VARCHAR,
ran_at TIMESTAMP,
signed_by VARCHAR,
signed_at TIMESTAMP,
signed_figures VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (run_id)
);
CREATE TABLE IF NOT EXISTS pay_odoo_map (
odoo_ref VARCHAR NOT NULL,
target_table VARCHAR,
target_key VARCHAR,
migrated_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (odoo_ref)
);
CREATE TABLE IF NOT EXISTS pay_receipt (
payment_id VARCHAR NOT NULL,
bp_id BIGINT,
currency VARCHAR,
amount NUMERIC(18,2),
received_on DATE,
method VARCHAR,
bank_account_id VARCHAR,
bank_reference VARCHAR,
remittance_text VARCHAR,
status VARCHAR,
batch_id VARCHAR,
fx_rate NUMERIC(19,4),
returned_on DATE,
bank_return_ref VARCHAR,
reversed_on DATE,
origin VARCHAR,
recorded_by VARCHAR,
recorded_at TIMESTAMP,
cleared_by VARCHAR,
recorded_on DATE,
return_reason_code VARCHAR,
statement_line_id VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (payment_id)
);
CREATE TABLE IF NOT EXISTS pay_receipt_ref (
bank_account_id VARCHAR NOT NULL,
bank_reference VARCHAR NOT NULL,
payment_id VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bank_account_id, bank_reference)
);
CREATE TABLE IF NOT EXISTS pay_refund (
refund_id VARCHAR NOT NULL,
bp_id BIGINT,
currency VARCHAR,
amount NUMERIC(18,2),
status VARCHAR,
requested_by VARCHAR,
requested_at TIMESTAMP,
approved_by VARCHAR,
approved_at TIMESTAMP,
approved_on DATE,
reason VARCHAR,
bank_ref VARCHAR,
paid_on DATE,
origin VARCHAR,
requested_sub VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (refund_id)
);
CREATE TABLE IF NOT EXISTS pay_reminder_level (
policy_id VARCHAR NOT NULL,
level INTEGER NOT NULL,
days_after_due INTEGER,
channel VARCHAR,
template VARCHAR,
mode VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (policy_id, level)
);
CREATE TABLE IF NOT EXISTS pay_settings (
setting_key VARCHAR NOT NULL,
value VARCHAR,
changes VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (setting_key)
);
CREATE TABLE IF NOT EXISTS pay_settings_log (
setting_key VARCHAR NOT NULL,
seq INTEGER NOT NULL,
old_value VARCHAR,
new_value VARCHAR,
changed_by VARCHAR,
changed_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (setting_key, seq)
);
CREATE TABLE IF NOT EXISTS pay_statement (
bp_id BIGINT NOT NULL,
as_at DATE NOT NULL,
currency VARCHAR NOT NULL,
closing_balance NUMERIC(18,2),
sent_at TIMESTAMP,
sent_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bp_id, as_at, currency)
);
-- audit 1 (00002_audit1): the claim rows that make two writers of the same money collide, and the dated time zone.
CREATE TABLE IF NOT EXISTS pay_invoice_claim (
invoice_id VARCHAR NOT NULL,
n INTEGER NOT NULL,
claimed_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (invoice_id, n)
);
CREATE TABLE IF NOT EXISTS pay_source_claim (
source_kind VARCHAR NOT NULL,
source_id VARCHAR NOT NULL,
n INTEGER NOT NULL,
claimed_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (source_kind, source_id, n)
);
CREATE TABLE IF NOT EXISTS pay_timezone (
valid_from DATE NOT NULL,
zone VARCHAR,
set_by VARCHAR,
set_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (valid_from)
);
-- v4 slice A (00003_statements_dd): statements and their lines, rules, remittances, direct debits, claims and objects.
CREATE TABLE IF NOT EXISTS pay_claim (
claim_scope VARCHAR NOT NULL,
claim_key VARCHAR NOT NULL,
n INTEGER NOT NULL,
ref VARCHAR,
claimed_by VARCHAR,
claimed_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (claim_scope, claim_key, n)
);
CREATE TABLE IF NOT EXISTS pay_object (
object_key VARCHAR NOT NULL,
content_type VARCHAR,
sha256 VARCHAR,
body VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (object_key)
);
CREATE TABLE IF NOT EXISTS pay_house_bank (
house_bank_id VARCHAR NOT NULL,
name VARCHAR,
bic VARCHAR,
sort_code VARCHAR,
address VARCHAR,
sun VARCHAR,
creditor_id VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (house_bank_id)
);
CREATE TABLE IF NOT EXISTS pay_bank_statement (
statement_id VARCHAR NOT NULL,
bank_account_id VARCHAR,
from_date DATE,
to_date DATE,
opening_balance NUMERIC(18,2),
closing_balance NUMERIC(18,2),
format VARCHAR,
file_key VARCHAR,
lifecycle VARCHAR,
reconciled_by VARCHAR,
reconciled_at TIMESTAMP,
imported_by VARCHAR,
imported_on DATE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (statement_id)
);
CREATE TABLE IF NOT EXISTS pay_statement_line (
line_id VARCHAR NOT NULL,
statement_id VARCHAR,
bank_account_id VARCHAR,
booked_on DATE,
value_on DATE,
amount NUMERIC(18,2),
currency VARCHAR,
counterparty_name VARCHAR,
counterparty_account_masked VARCHAR,
text VARCHAR,
bank_tx_code VARCHAR,
lifecycle VARCHAR,
matched_kind VARCHAR,
matched_ref VARCHAR,
account_id VARCHAR,
tax_code VARCHAR,
cost_centre VARCHAR,
journal_id VARCHAR,
decided_how VARCHAR,
decided_by VARCHAR,
decided_on DATE,
receipt_image VARCHAR,
vat_amount NUMERIC(18,2),
vat_reclaimed VARCHAR,
reversal_reason VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (line_id)
);
CREATE TABLE IF NOT EXISTS pay_line_suggestion (
suggestion_id VARCHAR NOT NULL,
line_id VARCHAR,
account_id VARCHAR,
tax_code VARCHAR,
cost_centre VARCHAR,
receipt_needed VARCHAR,
evidence VARCHAR,
confidence VARCHAR,
model VARCHAR,
status VARCHAR,
decided_by VARCHAR,
decided_on DATE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (suggestion_id)
);
CREATE TABLE IF NOT EXISTS pay_reconciliation_rule (
rule_id VARCHAR NOT NULL,
valid_from DATE NOT NULL,
bank_account_id VARCHAR,
direction VARCHAR,
text_contains VARCHAR,
counterparty_contains VARCHAR,
amount_min NUMERIC(18,2),
amount_max NUMERIC(18,2),
account_id VARCHAR,
tax_code VARCHAR,
cost_centre VARCHAR,
mode VARCHAR,
set_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (rule_id, valid_from)
);
CREATE TABLE IF NOT EXISTS pay_remittance (
remittance_id VARCHAR NOT NULL,
line_no INTEGER NOT NULL,
file_ref VARCHAR,
format VARCHAR,
payer VARCHAR,
payment_id VARCHAR,
bp_id BIGINT,
invoice_ref VARCHAR,
invoice_id VARCHAR,
amount NUMERIC(18,2),
currency VARCHAR,
deduction_reason VARCHAR,
status VARCHAR,
suggestion_id VARCHAR,
imported_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (remittance_id, line_no)
);
CREATE TABLE IF NOT EXISTS pay_dd_mandate (
sun VARCHAR NOT NULL,
mandate_ref VARCHAR NOT NULL,
bp_id BIGINT,
bank_account_id VARCHAR,
payer_sort_code VARCHAR,
payer_account_masked VARCHAR,
payer_account_name VARCHAR,
scheme VARCHAR,
lifecycle VARCHAR,
lodged_on DATE,
auddis_result VARCHAR,
cancelled_reason VARCHAR,
cancel_lodged_on DATE,
last_collected_on DATE,
recorded_on DATE,
recorded_by VARCHAR,
notice_days INTEGER,
ends_on DATE,
disputed_on DATE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (sun, mandate_ref)
);
CREATE TABLE IF NOT EXISTS pay_dd_mandate_details (
sun VARCHAR NOT NULL,
mandate_ref VARCHAR NOT NULL,
valid_from DATE NOT NULL,
sort_code VARCHAR,
account_number VARCHAR,
source VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (sun, mandate_ref, valid_from)
);
CREATE TABLE IF NOT EXISTS pay_dd_run (
run_id VARCHAR NOT NULL,
bank_account_id VARCHAR,
collection_date DATE,
input_day DATE,
lifecycle VARCHAR,
prepared_by VARCHAR,
prepared_by_sub VARCHAR,
approved_by VARCHAR,
approved_by_sub VARCHAR,
file_key VARCHAR,
submission_ref VARCHAR,
total NUMERIC(18,2),
count INTEGER,
currency VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (run_id)
);
CREATE TABLE IF NOT EXISTS pay_dd_collection (
run_id VARCHAR NOT NULL,
mandate_ref VARCHAR NOT NULL,
bp_id BIGINT,
sun VARCHAR,
invoice_ids VARCHAR,
amount NUMERIC(18,2),
transaction_code VARCHAR,
notice_sent_on DATE,
status VARCHAR,
payment_id VARCHAR,
return_reason_code VARCHAR,
returned_on DATE,
represented VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (run_id, mandate_ref)
);
-- v4 slice B (PAY-24..PAY-29): default receiving accounts, the unmasked-read audit, cash, deposit batches, forecast snapshots
CREATE TABLE IF NOT EXISTS pay_default_bank_account (
company_code VARCHAR NOT NULL,
currency VARCHAR NOT NULL,
valid_from DATE NOT NULL,
bank_account_id VARCHAR,
set_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (company_code, currency, valid_from)
);
CREATE TABLE IF NOT EXISTS pay_bank_account_read (
read_id VARCHAR NOT NULL,
bank_account_id VARCHAR,
caller VARCHAR,
requested_at TIMESTAMP,
purpose VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (read_id)
);
CREATE TABLE IF NOT EXISTS pay_cash_journal (
cash_journal_id VARCHAR NOT NULL,
company_code VARCHAR,
currency VARCHAR,
gl_account_id VARCHAR,
name VARCHAR,
status VARCHAR,
opening_balance NUMERIC(18,2),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (cash_journal_id)
);
CREATE TABLE IF NOT EXISTS pay_cash_day (
cash_journal_id VARCHAR NOT NULL,
day DATE NOT NULL,
lifecycle VARCHAR,
opening_balance NUMERIC(18,2),
expected_balance NUMERIC(18,2),
counted_amount NUMERIC(18,2),
difference NUMERIC(18,2),
counted_by VARCHAR,
approved_by VARCHAR,
approval_reason VARCHAR,
closed_by VARCHAR,
closed_at TIMESTAMP,
difference_journal_id VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (cash_journal_id, day)
);
CREATE TABLE IF NOT EXISTS pay_cash_entry (
cash_journal_id VARCHAR NOT NULL,
day DATE NOT NULL,
seq INTEGER NOT NULL,
kind VARCHAR,
amount NUMERIC(18,2),
currency VARCHAR,
bp_id BIGINT,
payment_id VARCHAR,
account_id VARCHAR,
tax_code VARCHAR,
description VARCHAR,
receipt_image VARCHAR,
vat_amount NUMERIC(18,2),
vat_reclaimed VARCHAR,
journal_id VARCHAR,
statement_line_id VARCHAR,
recorded_by VARCHAR,
recorded_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (cash_journal_id, day, seq)
);
CREATE TABLE IF NOT EXISTS pay_cash_count (
cash_journal_id VARCHAR NOT NULL,
day DATE NOT NULL,
seq INTEGER NOT NULL,
counted_amount NUMERIC(18,2),
denominations VARCHAR,
counted_by VARCHAR,
counted_by_ids VARCHAR,
counted_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (cash_journal_id, day, seq)
);
CREATE TABLE IF NOT EXISTS pay_deposit_batch (
batch_id VARCHAR NOT NULL,
bank_account_id VARCHAR,
currency VARCHAR,
slip_no VARCHAR,
lifecycle VARCHAR,
total NUMERIC(18,2),
cheque_count INTEGER,
deposited_on DATE,
statement_line_id VARCHAR,
final_batch_id VARCHAR,
opened_by VARCHAR,
kind VARCHAR,
batch_ref VARCHAR,
source_module VARCHAR,
clearing_purpose VARCHAR,
expected_on DATE,
company_code VARCHAR,
difference NUMERIC(18,2),
accepted_by VARCHAR,
accepted_sub VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (batch_id)
);
CREATE TABLE IF NOT EXISTS pay_card_settlement (
settlement_ref VARCHAR NOT NULL,
provider VARCHAR,
company_code VARCHAR,
currency VARCHAR,
gross NUMERIC(18,2),
business_date DATE,
transaction_count INTEGER,
source_module VARCHAR,
lifecycle VARCHAR,
bank_account_id VARCHAR,
statement_line_id VARCHAR,
fees NUMERIC(18,2),
fee_pct NUMERIC(19,4),
matched_by VARCHAR,
matched_sub VARCHAR,
accepted_by VARCHAR,
accepted_sub VARCHAR,
accept_reason VARCHAR,
claim VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (settlement_ref)
);
CREATE TABLE IF NOT EXISTS pay_payout (
payout_ref VARCHAR NOT NULL,
payout_id VARCHAR,
payee_bp_id BIGINT,
person_id VARCHAR,
company_code VARCHAR,
currency VARCHAR,
amount NUMERIC(18,2),
due_on DATE,
reference VARCHAR,
source_module VARCHAR,
source_document VARCHAR,
clearing_purpose VARCHAR,
lifecycle VARCHAR,
bank_account_id VARCHAR,
requested_by VARCHAR,
approved_by VARCHAR,
approved_sub VARCHAR,
approved_on DATE,
exported_by VARCHAR,
exported_sub VARCHAR,
exported_on DATE,
export_key VARCHAR,
paid_on DATE,
bank_ref VARCHAR,
statement_line_id VARCHAR,
journal_id VARCHAR,
returned_on DATE,
cancel_reason VARCHAR,
cancelled_by VARCHAR,
told_source_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (payout_ref)
);
CREATE TABLE IF NOT EXISTS pay_forecast_snapshot (
snapshot_id VARCHAR NOT NULL,
saved_on DATE,
saved_by VARCHAR,
body VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (snapshot_id)
);
-- v4 slice C (PAY-33, PAY-35): the FX difference posted per allocation, open-banking feed consents
CREATE TABLE IF NOT EXISTS pay_fx_posting (
payment_id VARCHAR NOT NULL,
invoice_id VARCHAR NOT NULL,
seq INTEGER NOT NULL,
event VARCHAR,
direction VARCHAR,
source_document VARCHAR,
journal_id VARCHAR,
amount NUMERIC(18,2),
currency VARCHAR,
receipt_rate NUMERIC(19,4),
invoice_rate NUMERIC(19,4),
posted_on DATE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (payment_id, invoice_id, seq)
);
CREATE TABLE IF NOT EXISTS pay_feed_consent (
bank_account_id VARCHAR NOT NULL,
granted_on DATE NOT NULL,
provider VARCHAR,
expires_on DATE,
recorded_by VARCHAR,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (bank_account_id, granted_on)
);