Add daily AI health score calculation (0-100) for both operating and reserve funds. Scores include trajectory tracking, factor analysis, recommendations, and data readiness checks. Dashboard displays graphical RingProgress gauges with color-coded scores, trend indicators, and expandable detail popovers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
468 lines
20 KiB
TypeScript
468 lines
20 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { DataSource } from 'typeorm';
|
|
|
|
@Injectable()
|
|
export class TenantSchemaService {
|
|
constructor(private dataSource: DataSource) {}
|
|
|
|
async createTenantSchema(schemaName: string): Promise<void> {
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.connect();
|
|
|
|
try {
|
|
await queryRunner.startTransaction();
|
|
|
|
await queryRunner.query(`CREATE SCHEMA IF NOT EXISTS "${schemaName}"`);
|
|
|
|
const sql = this.getTenantSchemaDDL(schemaName);
|
|
for (const statement of sql) {
|
|
await queryRunner.query(statement);
|
|
}
|
|
|
|
await this.seedDefaultFiscalPeriods(queryRunner, schemaName);
|
|
|
|
await queryRunner.commitTransaction();
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
throw error;
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
private getTenantSchemaDDL(s: string): string[] {
|
|
return [
|
|
// Accounts (Chart of Accounts)
|
|
`CREATE TABLE "${s}".accounts (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
account_number VARCHAR(50) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
account_type VARCHAR(50) NOT NULL CHECK (account_type IN ('asset', 'liability', 'equity', 'income', 'expense')),
|
|
fund_type VARCHAR(20) NOT NULL CHECK (fund_type IN ('operating', 'reserve')),
|
|
parent_account_id UUID REFERENCES "${s}".accounts(id),
|
|
is_1099_reportable BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
is_system BOOLEAN DEFAULT FALSE,
|
|
is_primary BOOLEAN DEFAULT FALSE,
|
|
interest_rate DECIMAL(6,4),
|
|
balance DECIMAL(15,2) DEFAULT 0.00,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(account_number)
|
|
)`,
|
|
|
|
// Fiscal Periods
|
|
`CREATE TABLE "${s}".fiscal_periods (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
year INTEGER NOT NULL,
|
|
month INTEGER NOT NULL CHECK (month BETWEEN 1 AND 12),
|
|
status VARCHAR(20) DEFAULT 'open' CHECK (status IN ('open', 'closed', 'locked')),
|
|
closed_by UUID,
|
|
closed_at TIMESTAMPTZ,
|
|
locked_by UUID,
|
|
locked_at TIMESTAMPTZ,
|
|
UNIQUE(year, month)
|
|
)`,
|
|
|
|
// Journal Entries
|
|
`CREATE TABLE "${s}".journal_entries (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
entry_date DATE NOT NULL,
|
|
description TEXT NOT NULL,
|
|
reference_number VARCHAR(100),
|
|
entry_type VARCHAR(50) NOT NULL CHECK (entry_type IN (
|
|
'manual', 'assessment', 'payment', 'late_fee', 'transfer',
|
|
'adjustment', 'closing', 'opening_balance', 'monthly_actual'
|
|
)),
|
|
fiscal_period_id UUID NOT NULL REFERENCES "${s}".fiscal_periods(id),
|
|
source_type VARCHAR(50),
|
|
source_id UUID,
|
|
is_posted BOOLEAN DEFAULT FALSE,
|
|
posted_by UUID,
|
|
posted_at TIMESTAMPTZ,
|
|
is_void BOOLEAN DEFAULT FALSE,
|
|
is_reconciled BOOLEAN DEFAULT FALSE,
|
|
voided_by UUID,
|
|
voided_at TIMESTAMPTZ,
|
|
void_reason TEXT,
|
|
created_by UUID NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Journal Entry Lines
|
|
`CREATE TABLE "${s}".journal_entry_lines (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
journal_entry_id UUID NOT NULL REFERENCES "${s}".journal_entries(id) ON DELETE CASCADE,
|
|
account_id UUID NOT NULL REFERENCES "${s}".accounts(id),
|
|
debit DECIMAL(15,2) DEFAULT 0.00,
|
|
credit DECIMAL(15,2) DEFAULT 0.00,
|
|
memo TEXT,
|
|
CHECK (debit >= 0 AND credit >= 0),
|
|
CHECK (NOT (debit > 0 AND credit > 0))
|
|
)`,
|
|
|
|
// Assessment Groups
|
|
`CREATE TABLE "${s}".assessment_groups (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
regular_assessment DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
|
special_assessment DECIMAL(10,2) DEFAULT 0.00,
|
|
unit_count INTEGER DEFAULT 0,
|
|
frequency VARCHAR(20) DEFAULT 'monthly' CHECK (frequency IN ('monthly', 'quarterly', 'annual')),
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Units (homeowners/lots)
|
|
`CREATE TABLE "${s}".units (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
unit_number VARCHAR(50) NOT NULL UNIQUE,
|
|
address_line1 VARCHAR(255),
|
|
address_line2 VARCHAR(255),
|
|
city VARCHAR(100),
|
|
state VARCHAR(2),
|
|
zip_code VARCHAR(10),
|
|
square_footage INTEGER,
|
|
lot_size DECIMAL(10,2),
|
|
owner_user_id UUID,
|
|
owner_name VARCHAR(255),
|
|
owner_email VARCHAR(255),
|
|
owner_phone VARCHAR(20),
|
|
is_rented BOOLEAN DEFAULT FALSE,
|
|
monthly_assessment DECIMAL(10,2),
|
|
assessment_group_id UUID REFERENCES "${s}".assessment_groups(id),
|
|
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'sold')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Invoices
|
|
`CREATE TABLE "${s}".invoices (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
invoice_number VARCHAR(50) NOT NULL UNIQUE,
|
|
unit_id UUID NOT NULL REFERENCES "${s}".units(id),
|
|
invoice_date DATE NOT NULL,
|
|
due_date DATE NOT NULL,
|
|
invoice_type VARCHAR(50) NOT NULL CHECK (invoice_type IN (
|
|
'regular_assessment', 'special_assessment', 'late_fee', 'other'
|
|
)),
|
|
description TEXT,
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
amount_paid DECIMAL(10,2) DEFAULT 0.00,
|
|
status VARCHAR(20) DEFAULT 'draft' CHECK (status IN (
|
|
'draft', 'sent', 'paid', 'partial', 'overdue', 'void', 'written_off'
|
|
)),
|
|
journal_entry_id UUID REFERENCES "${s}".journal_entries(id),
|
|
sent_at TIMESTAMPTZ,
|
|
paid_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Payments
|
|
`CREATE TABLE "${s}".payments (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
unit_id UUID NOT NULL REFERENCES "${s}".units(id),
|
|
invoice_id UUID REFERENCES "${s}".invoices(id),
|
|
payment_date DATE NOT NULL,
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
payment_method VARCHAR(50) CHECK (payment_method IN (
|
|
'check', 'ach', 'credit_card', 'cash', 'wire', 'other'
|
|
)),
|
|
reference_number VARCHAR(100),
|
|
stripe_payment_id VARCHAR(255),
|
|
status VARCHAR(20) DEFAULT 'completed' CHECK (status IN (
|
|
'pending', 'completed', 'failed', 'refunded'
|
|
)),
|
|
journal_entry_id UUID REFERENCES "${s}".journal_entries(id),
|
|
received_by UUID,
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Vendors
|
|
`CREATE TABLE "${s}".vendors (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
contact_name VARCHAR(255),
|
|
email VARCHAR(255),
|
|
phone VARCHAR(20),
|
|
address_line1 VARCHAR(255),
|
|
address_line2 VARCHAR(255),
|
|
city VARCHAR(100),
|
|
state VARCHAR(2),
|
|
zip_code VARCHAR(10),
|
|
tax_id VARCHAR(20),
|
|
is_1099_eligible BOOLEAN DEFAULT FALSE,
|
|
default_account_id UUID REFERENCES "${s}".accounts(id),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
ytd_payments DECIMAL(15,2) DEFAULT 0.00,
|
|
last_negotiated DATE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Budgets
|
|
`CREATE TABLE "${s}".budgets (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
fiscal_year INTEGER NOT NULL,
|
|
account_id UUID NOT NULL REFERENCES "${s}".accounts(id),
|
|
fund_type VARCHAR(20) NOT NULL CHECK (fund_type IN ('operating', 'reserve')),
|
|
jan DECIMAL(12,2) DEFAULT 0, feb DECIMAL(12,2) DEFAULT 0,
|
|
mar DECIMAL(12,2) DEFAULT 0, apr DECIMAL(12,2) DEFAULT 0,
|
|
may DECIMAL(12,2) DEFAULT 0, jun DECIMAL(12,2) DEFAULT 0,
|
|
jul DECIMAL(12,2) DEFAULT 0, aug DECIMAL(12,2) DEFAULT 0,
|
|
sep DECIMAL(12,2) DEFAULT 0, oct DECIMAL(12,2) DEFAULT 0,
|
|
nov DECIMAL(12,2) DEFAULT 0, dec_amt DECIMAL(12,2) DEFAULT 0,
|
|
notes TEXT,
|
|
UNIQUE(fiscal_year, account_id, fund_type)
|
|
)`,
|
|
|
|
// Reserve Components
|
|
`CREATE TABLE "${s}".reserve_components (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
category VARCHAR(100),
|
|
description TEXT,
|
|
useful_life_years INTEGER NOT NULL,
|
|
remaining_life_years DECIMAL(5,1),
|
|
replacement_cost DECIMAL(15,2) NOT NULL,
|
|
current_fund_balance DECIMAL(15,2) DEFAULT 0.00,
|
|
annual_contribution DECIMAL(12,2) DEFAULT 0.00,
|
|
last_replacement_date DATE,
|
|
next_replacement_date DATE,
|
|
condition_rating INTEGER CHECK (condition_rating BETWEEN 1 AND 10),
|
|
account_id UUID REFERENCES "${s}".accounts(id),
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Investment Accounts
|
|
`CREATE TABLE "${s}".investment_accounts (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
institution VARCHAR(255),
|
|
account_number_last4 VARCHAR(4),
|
|
investment_type VARCHAR(50) CHECK (investment_type IN (
|
|
'cd', 'money_market', 'treasury', 'savings', 'other'
|
|
)),
|
|
fund_type VARCHAR(20) NOT NULL CHECK (fund_type IN ('operating', 'reserve')),
|
|
principal DECIMAL(15,2) NOT NULL,
|
|
interest_rate DECIMAL(6,4),
|
|
maturity_date DATE,
|
|
purchase_date DATE,
|
|
current_value DECIMAL(15,2),
|
|
account_id UUID REFERENCES "${s}".accounts(id),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Capital Projects
|
|
`CREATE TABLE "${s}".capital_projects (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
estimated_cost DECIMAL(15,2) NOT NULL,
|
|
actual_cost DECIMAL(15,2),
|
|
target_year INTEGER NOT NULL,
|
|
target_month INTEGER CHECK (target_month BETWEEN 1 AND 12),
|
|
status VARCHAR(20) DEFAULT 'planned' CHECK (status IN (
|
|
'planned', 'approved', 'in_progress', 'completed', 'deferred', 'cancelled'
|
|
)),
|
|
reserve_component_id UUID REFERENCES "${s}".reserve_components(id),
|
|
fund_source VARCHAR(20) CHECK (fund_source IN ('operating', 'reserve', 'special_assessment')),
|
|
priority INTEGER DEFAULT 3 CHECK (priority BETWEEN 1 AND 5),
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Unified Projects (replaces reserve_components + capital_projects for new features)
|
|
`CREATE TABLE "${s}".projects (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
category VARCHAR(100),
|
|
estimated_cost DECIMAL(15,2) NOT NULL DEFAULT 0,
|
|
actual_cost DECIMAL(15,2),
|
|
current_fund_balance DECIMAL(15,2) DEFAULT 0.00,
|
|
annual_contribution DECIMAL(12,2) DEFAULT 0.00,
|
|
fund_source VARCHAR(20) CHECK (fund_source IN ('operating', 'reserve', 'special_assessment')),
|
|
funded_percentage DECIMAL(5,2) DEFAULT 0,
|
|
useful_life_years INTEGER,
|
|
remaining_life_years DECIMAL(5,1),
|
|
condition_rating INTEGER CHECK (condition_rating BETWEEN 1 AND 10),
|
|
last_replacement_date DATE,
|
|
next_replacement_date DATE,
|
|
planned_date DATE,
|
|
target_year INTEGER,
|
|
target_month INTEGER CHECK (target_month BETWEEN 1 AND 12),
|
|
status VARCHAR(20) DEFAULT 'planned' CHECK (status IN (
|
|
'planned', 'approved', 'in_progress', 'completed', 'deferred', 'cancelled'
|
|
)),
|
|
priority INTEGER DEFAULT 3 CHECK (priority BETWEEN 1 AND 5),
|
|
account_id UUID REFERENCES "${s}".accounts(id),
|
|
notes TEXT,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
is_funding_locked BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// AI Investment Recommendations (saved per tenant)
|
|
`CREATE TABLE "${s}".ai_recommendations (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
recommendations_json JSONB NOT NULL,
|
|
overall_assessment TEXT,
|
|
risk_notes JSONB,
|
|
requested_by UUID,
|
|
response_time_ms INTEGER,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Health Scores (AI-derived operating / reserve fund health)
|
|
`CREATE TABLE "${s}".health_scores (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
score_type VARCHAR(20) NOT NULL CHECK (score_type IN ('operating', 'reserve')),
|
|
score INTEGER NOT NULL CHECK (score >= 0 AND score <= 100),
|
|
previous_score INTEGER,
|
|
trajectory VARCHAR(20) CHECK (trajectory IN ('improving', 'stable', 'declining')),
|
|
label VARCHAR(30),
|
|
summary TEXT,
|
|
factors JSONB,
|
|
recommendations JSONB,
|
|
missing_data JSONB,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'complete' CHECK (status IN ('complete', 'pending', 'error')),
|
|
response_time_ms INTEGER,
|
|
calculated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
`CREATE INDEX "idx_${s}_hs_type_calc" ON "${s}".health_scores(score_type, calculated_at DESC)`,
|
|
|
|
// Attachments (file storage for receipts/invoices)
|
|
`CREATE TABLE "${s}".attachments (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
journal_entry_id UUID NOT NULL REFERENCES "${s}".journal_entries(id) ON DELETE CASCADE,
|
|
filename VARCHAR(255) NOT NULL,
|
|
mime_type VARCHAR(100) NOT NULL,
|
|
file_size INTEGER NOT NULL,
|
|
file_data BYTEA NOT NULL,
|
|
uploaded_by UUID NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)`,
|
|
|
|
// Indexes
|
|
`CREATE INDEX "idx_${s}_att_je" ON "${s}".attachments(journal_entry_id)`,
|
|
`CREATE INDEX "idx_${s}_je_date" ON "${s}".journal_entries(entry_date)`,
|
|
`CREATE INDEX "idx_${s}_je_fiscal" ON "${s}".journal_entries(fiscal_period_id)`,
|
|
`CREATE INDEX "idx_${s}_jel_entry" ON "${s}".journal_entry_lines(journal_entry_id)`,
|
|
`CREATE INDEX "idx_${s}_jel_account" ON "${s}".journal_entry_lines(account_id)`,
|
|
`CREATE INDEX "idx_${s}_inv_unit" ON "${s}".invoices(unit_id)`,
|
|
`CREATE INDEX "idx_${s}_inv_status" ON "${s}".invoices(status)`,
|
|
`CREATE INDEX "idx_${s}_inv_due" ON "${s}".invoices(due_date)`,
|
|
`CREATE INDEX "idx_${s}_pay_unit" ON "${s}".payments(unit_id)`,
|
|
`CREATE INDEX "idx_${s}_pay_inv" ON "${s}".payments(invoice_id)`,
|
|
`CREATE INDEX "idx_${s}_bud_year" ON "${s}".budgets(fiscal_year)`,
|
|
];
|
|
}
|
|
|
|
private async seedDefaultChartOfAccounts(queryRunner: any, s: string): Promise<void> {
|
|
const accounts = [
|
|
// Assets
|
|
[1000, 'Operating Cash - Checking', 'asset', 'operating', false, true],
|
|
[1010, 'Operating Cash - Savings', 'asset', 'operating', false, true],
|
|
[1020, 'Operating Cash - Money Market', 'asset', 'operating', false, true],
|
|
[1100, 'Reserve Cash - Checking', 'asset', 'reserve', false, true],
|
|
[1110, 'Reserve Cash - Savings', 'asset', 'reserve', false, true],
|
|
[1120, 'Reserve Cash - Money Market', 'asset', 'reserve', false, true],
|
|
[1130, 'Reserve Cash - CDs', 'asset', 'reserve', false, true],
|
|
[1140, 'Reserve Cash - Treasuries', 'asset', 'reserve', false, true],
|
|
[1200, 'Accounts Receivable - Assessments', 'asset', 'operating', false, true],
|
|
[1210, 'Accounts Receivable - Late Fees', 'asset', 'operating', false, true],
|
|
[1300, 'Prepaid Insurance', 'asset', 'operating', false, true],
|
|
[1400, 'Other Current Assets', 'asset', 'operating', false, false],
|
|
// Liabilities
|
|
[2000, 'Accounts Payable', 'liability', 'operating', false, true],
|
|
[2100, 'Accrued Expenses', 'liability', 'operating', false, true],
|
|
[2200, 'Prepaid Assessments', 'liability', 'operating', false, true],
|
|
[2300, 'Security Deposits Held', 'liability', 'operating', false, false],
|
|
[2400, 'Loan Payable', 'liability', 'operating', false, false],
|
|
// Equity
|
|
[3000, 'Operating Fund Balance', 'equity', 'operating', false, true],
|
|
[3100, 'Reserve Fund Balance', 'equity', 'reserve', false, true],
|
|
[3200, 'Retained Earnings', 'equity', 'operating', false, true],
|
|
// Income
|
|
[4000, 'Regular Assessments', 'income', 'operating', false, true],
|
|
[4010, 'Special Assessments', 'income', 'operating', false, true],
|
|
[4100, 'Late Fees', 'income', 'operating', false, true],
|
|
[4200, 'Interest Income - Operating', 'income', 'operating', false, true],
|
|
[4210, 'Interest Income - Reserve', 'income', 'reserve', false, true],
|
|
[4300, 'Transfer Fees', 'income', 'operating', false, false],
|
|
[4400, 'Clubhouse Rental Income', 'income', 'operating', false, false],
|
|
[4500, 'Other Income', 'income', 'operating', false, false],
|
|
[4600, 'Reserve Contributions', 'income', 'reserve', false, true],
|
|
// Expenses
|
|
[5000, 'Management Fees', 'expense', 'operating', true, true],
|
|
[5100, 'Insurance - Property', 'expense', 'operating', false, true],
|
|
[5110, 'Insurance - D&O', 'expense', 'operating', false, true],
|
|
[5120, 'Insurance - Liability', 'expense', 'operating', false, true],
|
|
[5200, 'Utilities - Water/Sewer', 'expense', 'operating', false, true],
|
|
[5210, 'Utilities - Electric (Common)', 'expense', 'operating', false, true],
|
|
[5220, 'Utilities - Gas', 'expense', 'operating', false, true],
|
|
[5230, 'Utilities - Trash/Recycling', 'expense', 'operating', false, true],
|
|
[5300, 'Landscape Maintenance', 'expense', 'operating', true, true],
|
|
[5310, 'Landscape - Irrigation', 'expense', 'operating', false, true],
|
|
[5400, 'Pool Maintenance', 'expense', 'operating', true, true],
|
|
[5500, 'Building Maintenance', 'expense', 'operating', false, true],
|
|
[5510, 'Janitorial', 'expense', 'operating', true, true],
|
|
[5600, 'Pest Control', 'expense', 'operating', true, true],
|
|
[5700, 'Legal Fees', 'expense', 'operating', true, true],
|
|
[5800, 'Accounting/Audit Fees', 'expense', 'operating', true, true],
|
|
[5900, 'Office & Admin Expenses', 'expense', 'operating', false, true],
|
|
[5910, 'Postage & Mailing', 'expense', 'operating', false, true],
|
|
[5920, 'Bank Fees', 'expense', 'operating', false, true],
|
|
[6000, 'Repairs & Maintenance - General', 'expense', 'operating', false, true],
|
|
[6100, 'Snow Removal', 'expense', 'operating', true, true],
|
|
[6200, 'Security/Gate', 'expense', 'operating', false, true],
|
|
[6300, 'Cable/Internet (Common)', 'expense', 'operating', false, false],
|
|
[6400, 'Social Events/Activities', 'expense', 'operating', false, false],
|
|
[6500, 'Contingency/Miscellaneous', 'expense', 'operating', false, true],
|
|
// Reserve Expenses
|
|
[7000, 'Reserve - Roof Replacement', 'expense', 'reserve', false, true],
|
|
[7100, 'Reserve - Paving/Asphalt', 'expense', 'reserve', false, true],
|
|
[7200, 'Reserve - Pool Renovation', 'expense', 'reserve', false, true],
|
|
[7300, 'Reserve - HVAC Replacement', 'expense', 'reserve', false, true],
|
|
[7400, 'Reserve - Painting/Exterior', 'expense', 'reserve', false, true],
|
|
[7500, 'Reserve - Fencing', 'expense', 'reserve', false, true],
|
|
[7600, 'Reserve - Elevator', 'expense', 'reserve', false, false],
|
|
[7700, 'Reserve - Other', 'expense', 'reserve', false, true],
|
|
];
|
|
|
|
for (const [num, name, type, fund, is1099, isSys] of accounts) {
|
|
await queryRunner.query(
|
|
`INSERT INTO "${s}".accounts (account_number, name, account_type, fund_type, is_1099_reportable, is_system)
|
|
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
[num, name, type, fund, is1099, isSys],
|
|
);
|
|
}
|
|
}
|
|
|
|
private async seedDefaultFiscalPeriods(queryRunner: any, s: string): Promise<void> {
|
|
const currentYear = new Date().getFullYear();
|
|
for (let month = 1; month <= 12; month++) {
|
|
await queryRunner.query(
|
|
`INSERT INTO "${s}".fiscal_periods (year, month, status) VALUES ($1, $2, 'open')`,
|
|
[currentYear, month],
|
|
);
|
|
}
|
|
}
|
|
}
|