Utilities section becomes create/edit/archive-able, with its child data. API: - Property gains archivedAt (soft-delete); list/browser default to archivedAt=null with ?includeArchived opt-in. - PropertiesService: header create/update/archive/restore (customer FK validated); PropertyService add/update/remove scoped to the property; TrustAccount upsert (1:1) + remove; ServiceDocument pointer delete. - Controller write routes: create needs STAFF+ (property:create), archive MANAGER+ (property:delete), every service/trust/document route property:update. Mutations audited. DTOs added. - Document *upload* deliberately deferred: it needs the object-storage client wired into the API (today only the migration writes to MinIO); removing an existing pointer row is supported and the UI says so. Web: - PropertyForm (header) with CustomerPicker; /servicios/nuevo (accepts ?customerId prefill) and /servicios/[id]/editar. - Property detail: gated action bar (Editar/Archivar) + "Administrar propiedad" — services via the shared ChildCollection editor, an inline 1:1 TrustEditor (create/update/clear), and document-row delete. - "Nueva propiedad" buttons on the list and customer detail (prefilled). api.ts + types for all of it. Verified against dev: property create (archivedAt null), service add/update, VIEWER service-add 403, trust upsert (create then update the same row), trust/service remove, cross-property child guard 404, archive drops from the default list and includeArchived surfaces it. Both apps compile clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import {
|
|
IsBoolean,
|
|
IsEnum,
|
|
IsNumber,
|
|
IsOptional,
|
|
IsString,
|
|
MinLength,
|
|
} from "class-validator";
|
|
import { ServiceKind } from "@jorgecuadros/database";
|
|
|
|
export class CreatePropertyDto {
|
|
@IsString() @MinLength(1) customerId!: string;
|
|
@IsOptional() @IsString() policyId?: string;
|
|
@IsOptional() @IsString() addressLine1?: string;
|
|
@IsOptional() @IsString() addressLine2?: string;
|
|
@IsOptional() @IsString() phone1?: string;
|
|
@IsOptional() @IsString() phone2?: string;
|
|
@IsOptional() @IsString() phone3?: string;
|
|
@IsOptional() @IsString() zone?: string;
|
|
}
|
|
|
|
export class UpdatePropertyDto {
|
|
@IsOptional() @IsString() policyId?: string;
|
|
@IsOptional() @IsString() addressLine1?: string;
|
|
@IsOptional() @IsString() addressLine2?: string;
|
|
@IsOptional() @IsString() phone1?: string;
|
|
@IsOptional() @IsString() phone2?: string;
|
|
@IsOptional() @IsString() phone3?: string;
|
|
@IsOptional() @IsString() zone?: string;
|
|
}
|
|
|
|
export class ServiceDto {
|
|
@IsEnum(ServiceKind) kind!: ServiceKind;
|
|
@IsOptional() @IsString() accountNumber?: string;
|
|
@IsOptional() @IsString() meterNumber?: string;
|
|
@IsOptional() @IsString() route?: string;
|
|
@IsOptional() @IsString() dueDay?: string;
|
|
@IsOptional() @IsBoolean() active?: boolean;
|
|
@IsOptional() @IsString() notes?: string;
|
|
}
|
|
export class UpdateServiceDto {
|
|
@IsOptional() @IsEnum(ServiceKind) kind?: ServiceKind;
|
|
@IsOptional() @IsString() accountNumber?: string;
|
|
@IsOptional() @IsString() meterNumber?: string;
|
|
@IsOptional() @IsString() route?: string;
|
|
@IsOptional() @IsString() dueDay?: string;
|
|
@IsOptional() @IsBoolean() active?: boolean;
|
|
@IsOptional() @IsString() notes?: string;
|
|
}
|
|
|
|
/** Trust is 1:1 with a property — this both creates and updates it (upsert). */
|
|
export class TrustDto {
|
|
@IsOptional() @IsString() bankName?: string;
|
|
@IsOptional() @IsString() trustNumber?: string;
|
|
@IsOptional() @IsNumber() bankFee?: number;
|
|
@IsOptional() @IsString() dueDate1?: string;
|
|
@IsOptional() @IsString() dueDate2?: string;
|
|
}
|