Implement Phase 2 features: roles, assessment groups, budget import, Kanban
- Add hierarchical roles: SuperUser Admin (is_superadmin flag), Tenant Admin, Tenant User with separate /admin route and admin panel - Add Assessment Groups module for property type-based assessment rates (SFHs, Condos, Estate Lots with different regular/special rates) - Enhance Chart of Accounts: initial balance on create (with journal entry), archive/restore accounts, edit all fields including account number & fund type - Add Budget CSV import with downloadable template and account mapping - Add Capital Projects Kanban board with drag-and-drop between year columns, table/kanban view toggle, and PDF export via browser print - Update seed data with assessment groups, second test user, superadmin flag - Create repeatable reseed.sh script for clean database population - Fix AgingReportPage Mantine v7 Table prop compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { TenantService } from '../../database/tenant.service';
|
||||
|
||||
@Injectable()
|
||||
export class AssessmentGroupsService {
|
||||
constructor(private tenant: TenantService) {}
|
||||
|
||||
async findAll() {
|
||||
return this.tenant.query(`
|
||||
SELECT ag.*,
|
||||
(SELECT COUNT(*) FROM units u WHERE u.assessment_group_id = ag.id) as actual_unit_count,
|
||||
ag.regular_assessment * ag.unit_count as monthly_operating_income,
|
||||
ag.special_assessment * ag.unit_count as monthly_reserve_income,
|
||||
(ag.regular_assessment + ag.special_assessment) * ag.unit_count as total_monthly_income
|
||||
FROM assessment_groups ag
|
||||
ORDER BY ag.name
|
||||
`);
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const rows = await this.tenant.query('SELECT * FROM assessment_groups WHERE id = $1', [id]);
|
||||
if (!rows.length) throw new NotFoundException('Assessment group not found');
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
async create(dto: any) {
|
||||
const rows = await this.tenant.query(
|
||||
`INSERT INTO assessment_groups (name, description, regular_assessment, special_assessment, unit_count)
|
||||
VALUES ($1, $2, $3, $4, $5) RETURNING *`,
|
||||
[dto.name, dto.description || null, dto.regularAssessment || 0, dto.specialAssessment || 0, dto.unitCount || 0],
|
||||
);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
async update(id: string, dto: any) {
|
||||
await this.findOne(id);
|
||||
const sets: string[] = [];
|
||||
const params: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (dto.name !== undefined) { sets.push(`name = $${idx++}`); params.push(dto.name); }
|
||||
if (dto.description !== undefined) { sets.push(`description = $${idx++}`); params.push(dto.description); }
|
||||
if (dto.regularAssessment !== undefined) { sets.push(`regular_assessment = $${idx++}`); params.push(dto.regularAssessment); }
|
||||
if (dto.specialAssessment !== undefined) { sets.push(`special_assessment = $${idx++}`); params.push(dto.specialAssessment); }
|
||||
if (dto.unitCount !== undefined) { sets.push(`unit_count = $${idx++}`); params.push(dto.unitCount); }
|
||||
if (dto.isActive !== undefined) { sets.push(`is_active = $${idx++}`); params.push(dto.isActive); }
|
||||
|
||||
if (!sets.length) return this.findOne(id);
|
||||
|
||||
sets.push('updated_at = NOW()');
|
||||
params.push(id);
|
||||
|
||||
const rows = await this.tenant.query(
|
||||
`UPDATE assessment_groups SET ${sets.join(', ')} WHERE id = $${idx} RETURNING *`,
|
||||
params,
|
||||
);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
async getSummary() {
|
||||
const rows = await this.tenant.query(`
|
||||
SELECT
|
||||
COUNT(*) as group_count,
|
||||
COALESCE(SUM(regular_assessment * unit_count), 0) as total_monthly_operating,
|
||||
COALESCE(SUM(special_assessment * unit_count), 0) as total_monthly_reserve,
|
||||
COALESCE(SUM((regular_assessment + special_assessment) * unit_count), 0) as total_monthly_income,
|
||||
COALESCE(SUM(unit_count), 0) as total_units
|
||||
FROM assessment_groups WHERE is_active = true
|
||||
`);
|
||||
return rows[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user