integratedhub/services/rentChangeService.ts

185 lines
4.9 KiB
TypeScript

// 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<RentChange[]> {
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<number | null> {
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<RentChange | null> {
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<RentChange[]> {
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<Array<{
effective_date: number;
monthly_rent: number;
previous_rent: number | null;
reason: string | null;
is_current: boolean;
}>> {
// 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();
}