// services/rentChangeService.ts // Handle rent change history tracking for properties import { eq, desc } from "drizzle-orm"; import { db } from "../db/db.ts"; import { rent_changes, properties } from "../db/schema.ts"; // Use the type from the schema export type RentChange = typeof rent_changes.$inferSelect; /** * Create a new rent change record * Also updates the property's monthly_rent to the new value */ export async function createRentChange( property_id: string, effective_date: number, monthly_rent: number, options: { reason?: string; created_by?: string; } = {}, ) { // First, get the current rent from the property const propertyResult = await db.select({ monthly_rent: properties.monthly_rent, }) .from(properties) .where(eq(properties.id, property_id)) .limit(1); const previous_rent = propertyResult[0]?.monthly_rent || null; const now = Date.now(); // Create the rent change record const result = await db.insert(rent_changes) .values({ id: crypto.randomUUID(), property_id, effective_date, monthly_rent, previous_rent, reason: options.reason || null, created_by: options.created_by || null, created_on: now, }) .returning(); // Update the property with the new rent await db.update(properties) .set({ monthly_rent, updated_on: now }) .where(eq(properties.id, property_id)); return result; } /** * Get all rent changes for a property, ordered by effective date (newest first) */ export async function getRentChangesByProperty(property_id: string): Promise { return await db.select() .from(rent_changes) .where(eq(rent_changes.property_id, property_id)) .orderBy(desc(rent_changes.effective_date)); } /** * Get the current rent for a property (most recent effective date that has passed) */ export async function getCurrentRentForProperty(property_id: string): Promise { const now = Date.now(); const result = await db.select() .from(rent_changes) .where(eq(rent_changes.property_id, property_id)) .orderBy(desc(rent_changes.effective_date)) .limit(1); // If there are rent changes, return the most recent one if (result.length > 0) { return result[0].monthly_rent; } // Otherwise, fall back to the property's base monthly_rent const propertyResult = await db.select({ monthly_rent: properties.monthly_rent, }) .from(properties) .where(eq(properties.id, property_id)) .limit(1); return propertyResult[0]?.monthly_rent || null; } /** * Get rent change by ID */ export async function getRentChangeById(changeId: string): Promise { const result = await db.select() .from(rent_changes) .where(eq(rent_changes.id, changeId)); return result[0] || null; } /** * Get upcoming rent changes for a property (effective_date is in the future) */ export async function getUpcomingRentChanges(property_id: string): Promise { const now = Date.now(); return await db.select() .from(rent_changes) .where(eq(rent_changes.property_id, property_id)) .orderBy(desc(rent_changes.effective_date)); } /** * Get rent history for a property including current rent from the property record */ export async function getRentHistory(property_id: string): Promise> { // Get the property's current rent const propertyResult = await db.select({ monthly_rent: properties.monthly_rent, updated_on: properties.updated_on, }) .from(properties) .where(eq(properties.id, property_id)) .limit(1); const property = propertyResult[0]; // Get all rent changes const changes = await getRentChangesByProperty(property_id); // Build history including current property rent as the baseline const history: Array<{ effective_date: number; monthly_rent: number; previous_rent: number | null; reason: string | null; is_current: boolean; }> = []; // Add current property rent as the starting point if (property && property.monthly_rent) { history.push({ effective_date: property.updated_on || Date.now(), monthly_rent: property.monthly_rent, previous_rent: null, reason: "Initial rent", is_current: true, }); } // Add all historical changes for (const change of changes) { history.push({ effective_date: change.effective_date || 0, monthly_rent: change.monthly_rent || 0, previous_rent: change.previous_rent, reason: change.reason, is_current: false, }); } // Sort by effective date descending (newest first) history.sort((a, b) => b.effective_date - a.effective_date); return history; } /** * Delete a rent change record */ export async function deleteRentChange(changeId: string) { return await db.delete(rent_changes) .where(eq(rent_changes.id, changeId)) .returning(); }