castle/migrations.py
2025-10-22 12:33:45 +02:00

117 lines
3.7 KiB
Python

async def m001_initial(db):
"""
Initial migration for Castle accounting extension.
Creates tables for double-entry bookkeeping system.
"""
await db.execute(
f"""
CREATE TABLE castle.accounts (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
account_type TEXT NOT NULL,
description TEXT,
user_id TEXT,
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
);
"""
)
await db.execute(
"""
CREATE INDEX idx_accounts_user_id ON castle.accounts (user_id);
"""
)
await db.execute(
"""
CREATE INDEX idx_accounts_type ON castle.accounts (account_type);
"""
)
await db.execute(
f"""
CREATE TABLE castle.journal_entries (
id TEXT PRIMARY KEY,
description TEXT NOT NULL,
entry_date TIMESTAMP NOT NULL,
created_by TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
reference TEXT
);
"""
)
await db.execute(
"""
CREATE INDEX idx_journal_entries_created_by ON castle.journal_entries (created_by);
"""
)
await db.execute(
"""
CREATE INDEX idx_journal_entries_date ON castle.journal_entries (entry_date);
"""
)
await db.execute(
f"""
CREATE TABLE castle.entry_lines (
id TEXT PRIMARY KEY,
journal_entry_id TEXT NOT NULL,
account_id TEXT NOT NULL,
debit INTEGER NOT NULL DEFAULT 0,
credit INTEGER NOT NULL DEFAULT 0,
description TEXT,
FOREIGN KEY (journal_entry_id) REFERENCES castle.journal_entries (id),
FOREIGN KEY (account_id) REFERENCES castle.accounts (id)
);
"""
)
await db.execute(
"""
CREATE INDEX idx_entry_lines_journal_entry ON castle.entry_lines (journal_entry_id);
"""
)
await db.execute(
"""
CREATE INDEX idx_entry_lines_account ON castle.entry_lines (account_id);
"""
)
# Insert default chart of accounts
default_accounts = [
# Assets
("cash", "Cash", "asset", "Cash on hand"),
("bank", "Bank Account", "asset", "Bank account"),
("lightning", "Lightning Balance", "asset", "Lightning Network balance"),
("accounts_receivable", "Accounts Receivable", "asset", "Money owed to the Castle"),
# Liabilities
("accounts_payable", "Accounts Payable", "liability", "Money owed by the Castle"),
# Equity
("member_equity", "Member Equity", "equity", "Member contributions"),
("retained_earnings", "Retained Earnings", "equity", "Accumulated profits"),
# Revenue
("accommodation_revenue", "Accommodation Revenue", "revenue", "Revenue from stays"),
("service_revenue", "Service Revenue", "revenue", "Revenue from services"),
("other_revenue", "Other Revenue", "revenue", "Other revenue"),
# Expenses
("utilities", "Utilities", "expense", "Electricity, water, internet"),
("food", "Food & Supplies", "expense", "Food and supplies"),
("maintenance", "Maintenance", "expense", "Repairs and maintenance"),
("other_expense", "Other Expenses", "expense", "Miscellaneous expenses"),
]
for acc_id, name, acc_type, desc in default_accounts:
await db.execute(
"""
INSERT INTO castle.accounts (id, name, account_type, description)
VALUES (:id, :name, :type, :description)
""",
{"id": acc_id, "name": name, "type": acc_type, "description": desc}
)