feat(extraordinary-expenses): Add module for extraordinary expenses

Introduces a new module to manage and track extraordinary expenses within condominiums. This includes defining expense items, sharing arrangements, and attaching relevant documents.

The module adds new types for `ExpenseItem`, `ExpenseShare`, and `ExtraordinaryExpense`. Mock database functions are updated to support fetching, creating, and managing these expenses. UI components in `Layout.tsx` and `Settings.tsx` are modified to include navigation and feature toggling for extraordinary expenses. Additionally, new routes are added in `App.tsx` for both administrative and user-facing views of these expenses.
This commit is contained in:
2025-12-09 23:00:05 +01:00
parent 048180db75
commit fa12a8de85
13 changed files with 918 additions and 77 deletions

View File

@@ -46,7 +46,12 @@ const dbInterface = {
if (DB_CLIENT === 'postgres') {
return {
query: executeQuery,
release: () => {}
release: () => {},
// Mock transaction methods for Postgres simple wrapper
// In a real prod app, you would get a specific client from the pool here
beginTransaction: async () => {},
commit: async () => {},
rollback: async () => {}
};
} else {
return await mysqlPool.getConnection();
@@ -347,6 +352,58 @@ const initDb = async () => {
)
`);
// 11. Extraordinary Expenses Tables
await connection.query(`
CREATE TABLE IF NOT EXISTS extraordinary_expenses (
id VARCHAR(36) PRIMARY KEY,
condo_id VARCHAR(36) NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
start_date ${TIMESTAMP_TYPE},
end_date ${TIMESTAMP_TYPE},
contractor_name VARCHAR(255),
total_amount DECIMAL(15, 2) DEFAULT 0,
created_at ${TIMESTAMP_TYPE} DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (condo_id) REFERENCES condos(id) ON DELETE CASCADE
)
`);
await connection.query(`
CREATE TABLE IF NOT EXISTS expense_items (
id VARCHAR(36) PRIMARY KEY,
expense_id VARCHAR(36) NOT NULL,
description VARCHAR(255),
amount DECIMAL(15, 2) NOT NULL,
FOREIGN KEY (expense_id) REFERENCES extraordinary_expenses(id) ON DELETE CASCADE
)
`);
await connection.query(`
CREATE TABLE IF NOT EXISTS expense_shares (
id VARCHAR(36) PRIMARY KEY,
expense_id VARCHAR(36) NOT NULL,
family_id VARCHAR(36) NOT NULL,
percentage DECIMAL(5, 2) NOT NULL,
amount_due DECIMAL(15, 2) NOT NULL,
amount_paid DECIMAL(15, 2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'UNPAID',
FOREIGN KEY (expense_id) REFERENCES extraordinary_expenses(id) ON DELETE CASCADE,
FOREIGN KEY (family_id) REFERENCES families(id) ON DELETE CASCADE
)
`);
await connection.query(`
CREATE TABLE IF NOT EXISTS expense_attachments (
id VARCHAR(36) PRIMARY KEY,
expense_id VARCHAR(36) NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_type VARCHAR(100),
data ${LONG_TEXT_TYPE},
created_at ${TIMESTAMP_TYPE} DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (expense_id) REFERENCES extraordinary_expenses(id) ON DELETE CASCADE
)
`);
// --- SEEDING ---
const [rows] = await connection.query('SELECT * FROM settings WHERE id = 1');
const defaultFeatures = {
@@ -354,7 +411,8 @@ const initDb = async () => {
tickets: true,
payPal: true,
notices: true,
reports: true
reports: true,
extraordinaryExpenses: true
};
if (rows.length === 0) {