Utilities module: property browser (list/search/detail) + trust renewals
Plan step 5. Properties, services and trust accounts become a first-class
browser the way /polizas is for insurance.
API (apps/api/src/properties):
GET /properties search over address, customer, service account
number, meter, trust number and phones; filters for
service kind, municipality, trust bank, trust bucket
(with|without|active|expiring|expired|undated) and
hasServices; 5 sorts
GET /properties/stats properties/owners/services/trusts, renewal counts,
service mix per kind
GET /properties/facets kinds, municipalities, banks — all with counts
GET /properties/:id services, fideicomiso, linked policy, owner and
sibling properties, owner-level utility ledger
Web: /servicios (renewals-first browser, clickable stat cells and service-mix
strip) and /servicios/[id]. Property cards on /clientes/[id] and linked
properties on /polizas/[id] now navigate into it.
Data findings baked into the design:
- The trust deadline staff chase is trust_accounts.dueDate2 (DATMEX vence2),
one year after vence1 on 531 of 541 dated trusts: 18 due within 30 days,
119 already overdue. Every renewal bucket keys off dueDate2 alone.
- properties.zone is dead (1444 of 1519 null, the rest near-unique), so the
geographic filter is the municipality carried in the predial service's
notes (ROSARITO 566 / TIJUANA 221 / ENSENADA 152, 939/939 populated).
- PropertyService.notes means a different thing per kind (municipality, CFE
PAR/IMPAR cycle, gas supply type, cable provider) and is labelled as such.
- 240 of 1519 properties have no service rows at all — its own bucket.
Sorting by trust due date scopes to properties that have a trust, since MySQL
would otherwise float the ~966 trust-less NULLs above every real due date;
the sort label and the result meta both say so.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,13 @@ import type {
|
||||
PolicySort,
|
||||
PolicyStats,
|
||||
PolicyStatus,
|
||||
PropertyDetail,
|
||||
PropertyFacets,
|
||||
PropertyListResponse,
|
||||
PropertySort,
|
||||
PropertyStats,
|
||||
ServiceKind,
|
||||
TrustFilter,
|
||||
} from "./types";
|
||||
|
||||
export const API_ORIGIN =
|
||||
@@ -154,3 +161,54 @@ export function getPolicy(
|
||||
): Promise<PolicyDetail> {
|
||||
return apiFetch<PolicyDetail>(`/policies/${id}?days=${days}`);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------- Utilities module */
|
||||
|
||||
export interface PropertyQuery {
|
||||
query?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
serviceKind?: ServiceKind;
|
||||
municipality?: string;
|
||||
bank?: string;
|
||||
trust?: TrustFilter;
|
||||
hasServices?: boolean;
|
||||
customerId?: string;
|
||||
days?: number;
|
||||
sort?: PropertySort;
|
||||
}
|
||||
|
||||
export function listProperties(q: PropertyQuery): Promise<PropertyListResponse> {
|
||||
const params = new URLSearchParams();
|
||||
if (q.query) params.set("query", q.query);
|
||||
if (q.page) params.set("page", String(q.page));
|
||||
if (q.pageSize) params.set("pageSize", String(q.pageSize));
|
||||
if (q.serviceKind) params.set("serviceKind", q.serviceKind);
|
||||
if (q.municipality) params.set("municipality", q.municipality);
|
||||
if (q.bank) params.set("bank", q.bank);
|
||||
if (q.trust) params.set("trust", q.trust);
|
||||
if (q.hasServices !== undefined)
|
||||
params.set("hasServices", String(q.hasServices));
|
||||
if (q.customerId) params.set("customerId", q.customerId);
|
||||
if (q.days) params.set("days", String(q.days));
|
||||
if (q.sort) params.set("sort", q.sort);
|
||||
const qs = params.toString();
|
||||
return apiFetch<PropertyListResponse>(`/properties${qs ? `?${qs}` : ""}`);
|
||||
}
|
||||
|
||||
export function getPropertyStats(
|
||||
days: number = EXPIRY_WINDOW_DAYS,
|
||||
): Promise<PropertyStats> {
|
||||
return apiFetch<PropertyStats>(`/properties/stats?days=${days}`);
|
||||
}
|
||||
|
||||
export function getPropertyFacets(): Promise<PropertyFacets> {
|
||||
return apiFetch<PropertyFacets>("/properties/facets");
|
||||
}
|
||||
|
||||
export function getProperty(
|
||||
id: string,
|
||||
days: number = EXPIRY_WINDOW_DAYS,
|
||||
): Promise<PropertyDetail> {
|
||||
return apiFetch<PropertyDetail>(`/properties/${id}?days=${days}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user