From 6d84520811ed563ad225d4948cb617b3ae3a595b Mon Sep 17 00:00:00 2001 From: Adrian Flores Cortes Date: Fri, 30 Jan 2026 17:06:05 -0600 Subject: [PATCH] feat(MAI-002): Add Torre, Nivel, Departamento entities for vertical structures - Add torre.entity.ts for construction.torres table - Add nivel.entity.ts for construction.niveles table - Add departamento.entity.ts for construction.departamentos table - Update etapa.entity.ts with OneToMany Torre relation - Update avance-obra.entity.ts with ManyToOne Lote/Departamento relations - Update entities index.ts with new exports Completes vertical structure entities matching DDL schema. Co-Authored-By: Claude Opus 4.5 --- .../entities/departamento.entity.ts | 94 +++++++++++++++++++ .../construction/entities/etapa.entity.ts | 4 + src/modules/construction/entities/index.ts | 3 + .../construction/entities/nivel.entity.ts | 72 ++++++++++++++ .../construction/entities/torre.entity.ts | 78 +++++++++++++++ .../progress/entities/avance-obra.entity.ts | 10 ++ 6 files changed, 261 insertions(+) create mode 100644 src/modules/construction/entities/departamento.entity.ts create mode 100644 src/modules/construction/entities/nivel.entity.ts create mode 100644 src/modules/construction/entities/torre.entity.ts diff --git a/src/modules/construction/entities/departamento.entity.ts b/src/modules/construction/entities/departamento.entity.ts new file mode 100644 index 0000000..bf413e4 --- /dev/null +++ b/src/modules/construction/entities/departamento.entity.ts @@ -0,0 +1,94 @@ +/** + * Departamento Entity + * Departamentos/Unidades en torre (vivienda vertical) + * + * @module Construction + * @table construction.departamentos + * @ddl schemas/01-construction-schema-ddl.sql + */ + +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToOne, + JoinColumn, + Index, +} from 'typeorm'; +import { Nivel } from './nivel.entity'; +import { Prototipo } from './prototipo.entity'; + +@Entity({ schema: 'construction', name: 'departamentos' }) +@Index(['nivelId', 'code'], { unique: true }) +@Index(['tenantId']) +@Index(['nivelId']) +@Index(['status']) +export class Departamento { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'tenant_id', type: 'uuid' }) + tenantId: string; + + @Column({ name: 'nivel_id', type: 'uuid' }) + nivelId: string; + + @Column({ name: 'prototipo_id', type: 'uuid', nullable: true }) + prototipoId: string; + + @Column({ type: 'varchar', length: 30 }) + code: string; + + @Column({ name: 'unit_number', type: 'varchar', length: 20 }) + unitNumber: string; + + @Column({ name: 'area_m2', type: 'decimal', precision: 10, scale: 2, nullable: true }) + areaM2: number; + + @Column({ type: 'varchar', length: 50, default: 'available' }) + status: string; + + @Column({ name: 'price_base', type: 'decimal', precision: 14, scale: 2, nullable: true }) + priceBase: number; + + @Column({ name: 'price_final', type: 'decimal', precision: 14, scale: 2, nullable: true }) + priceFinal: number; + + @Column({ name: 'buyer_id', type: 'uuid', nullable: true }) + buyerId: string; + + @Column({ name: 'sale_date', type: 'date', nullable: true }) + saleDate: Date; + + @Column({ name: 'delivery_date', type: 'date', nullable: true }) + deliveryDate: Date; + + @CreateDateColumn({ name: 'created_at', type: 'timestamptz' }) + createdAt: Date; + + @Column({ name: 'created_by', type: 'uuid', nullable: true }) + createdBy: string; + + @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz', nullable: true }) + updatedAt: Date; + + @Column({ name: 'updated_by', type: 'uuid', nullable: true }) + updatedBy: string; + + @Column({ name: 'deleted_at', type: 'timestamptz', nullable: true }) + deletedAt: Date; + + @Column({ name: 'deleted_by', type: 'uuid', nullable: true }) + deletedBy: string; + + // Relations + @ManyToOne(() => Nivel, (n) => n.departamentos, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'nivel_id' }) + nivel: Nivel; + + @ManyToOne(() => Prototipo) + @JoinColumn({ name: 'prototipo_id' }) + prototipo: Prototipo; +} diff --git a/src/modules/construction/entities/etapa.entity.ts b/src/modules/construction/entities/etapa.entity.ts index bc37c7a..f315521 100644 --- a/src/modules/construction/entities/etapa.entity.ts +++ b/src/modules/construction/entities/etapa.entity.ts @@ -18,6 +18,7 @@ import { } from 'typeorm'; import { Fraccionamiento } from './fraccionamiento.entity'; import { Manzana } from './manzana.entity'; +import { Torre } from './torre.entity'; @Entity({ schema: 'construction', name: 'etapas' }) @Index(['fraccionamientoId', 'code'], { unique: true }) @@ -80,4 +81,7 @@ export class Etapa { @OneToMany(() => Manzana, (m) => m.etapa) manzanas: Manzana[]; + + @OneToMany(() => Torre, (t) => t.etapa) + torres: Torre[]; } diff --git a/src/modules/construction/entities/index.ts b/src/modules/construction/entities/index.ts index 6bfb8b8..22a8a70 100644 --- a/src/modules/construction/entities/index.ts +++ b/src/modules/construction/entities/index.ts @@ -9,3 +9,6 @@ export { Etapa } from './etapa.entity'; export { Manzana } from './manzana.entity'; export { Lote } from './lote.entity'; export { Prototipo } from './prototipo.entity'; +export { Torre } from './torre.entity'; +export { Nivel } from './nivel.entity'; +export { Departamento } from './departamento.entity'; diff --git a/src/modules/construction/entities/nivel.entity.ts b/src/modules/construction/entities/nivel.entity.ts new file mode 100644 index 0000000..af5c210 --- /dev/null +++ b/src/modules/construction/entities/nivel.entity.ts @@ -0,0 +1,72 @@ +/** + * Nivel Entity + * Pisos/Niveles de una torre + * + * @module Construction + * @table construction.niveles + * @ddl schemas/01-construction-schema-ddl.sql + */ + +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToOne, + OneToMany, + JoinColumn, + Index, +} from 'typeorm'; +import { Torre } from './torre.entity'; +import { Departamento } from './departamento.entity'; + +@Entity({ schema: 'construction', name: 'niveles' }) +@Index(['torreId', 'floorNumber'], { unique: true }) +@Index(['tenantId']) +@Index(['torreId']) +export class Nivel { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'tenant_id', type: 'uuid' }) + tenantId: string; + + @Column({ name: 'torre_id', type: 'uuid' }) + torreId: string; + + @Column({ name: 'floor_number', type: 'integer' }) + floorNumber: number; + + @Column({ type: 'varchar', length: 50, nullable: true }) + name: string; + + @Column({ name: 'total_units', type: 'integer', default: 0 }) + totalUnits: number; + + @CreateDateColumn({ name: 'created_at', type: 'timestamptz' }) + createdAt: Date; + + @Column({ name: 'created_by', type: 'uuid', nullable: true }) + createdBy: string; + + @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz', nullable: true }) + updatedAt: Date; + + @Column({ name: 'updated_by', type: 'uuid', nullable: true }) + updatedBy: string; + + @Column({ name: 'deleted_at', type: 'timestamptz', nullable: true }) + deletedAt: Date; + + @Column({ name: 'deleted_by', type: 'uuid', nullable: true }) + deletedBy: string; + + // Relations + @ManyToOne(() => Torre, (t) => t.niveles, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'torre_id' }) + torre: Torre; + + @OneToMany(() => Departamento, (d) => d.nivel) + departamentos: Departamento[]; +} diff --git a/src/modules/construction/entities/torre.entity.ts b/src/modules/construction/entities/torre.entity.ts new file mode 100644 index 0000000..aad2b9b --- /dev/null +++ b/src/modules/construction/entities/torre.entity.ts @@ -0,0 +1,78 @@ +/** + * Torre Entity + * Torres/Edificios verticales dentro de una etapa + * + * @module Construction + * @table construction.torres + * @ddl schemas/01-construction-schema-ddl.sql + */ + +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToOne, + OneToMany, + JoinColumn, + Index, +} from 'typeorm'; +import { Etapa } from './etapa.entity'; +import { Nivel } from './nivel.entity'; + +@Entity({ schema: 'construction', name: 'torres' }) +@Index(['etapaId', 'code'], { unique: true }) +@Index(['tenantId']) +@Index(['etapaId']) +export class Torre { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'tenant_id', type: 'uuid' }) + tenantId: string; + + @Column({ name: 'etapa_id', type: 'uuid' }) + etapaId: string; + + @Column({ type: 'varchar', length: 20 }) + code: string; + + @Column({ type: 'varchar', length: 100 }) + name: string; + + @Column({ name: 'total_floors', type: 'integer', default: 1 }) + totalFloors: number; + + @Column({ name: 'total_units', type: 'integer', default: 0 }) + totalUnits: number; + + @Column({ type: 'varchar', length: 50, default: 'draft' }) + status: string; + + @CreateDateColumn({ name: 'created_at', type: 'timestamptz' }) + createdAt: Date; + + @Column({ name: 'created_by', type: 'uuid', nullable: true }) + createdBy: string; + + @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz', nullable: true }) + updatedAt: Date; + + @Column({ name: 'updated_by', type: 'uuid', nullable: true }) + updatedBy: string; + + @Column({ name: 'deleted_at', type: 'timestamptz', nullable: true }) + deletedAt: Date; + + @Column({ name: 'deleted_by', type: 'uuid', nullable: true }) + deletedBy: string; + + // Relations + @ManyToOne(() => Etapa, (e) => e.torres, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'etapa_id' }) + etapa: Etapa; + + @OneToMany(() => Nivel, (n) => n.torre) + niveles: Nivel[]; +} diff --git a/src/modules/progress/entities/avance-obra.entity.ts b/src/modules/progress/entities/avance-obra.entity.ts index 3e112a9..35a4c8c 100644 --- a/src/modules/progress/entities/avance-obra.entity.ts +++ b/src/modules/progress/entities/avance-obra.entity.ts @@ -22,6 +22,8 @@ import { import { Tenant } from '../../core/entities/tenant.entity'; import { User } from '../../core/entities/user.entity'; import { Concepto } from '../../budgets/entities/concepto.entity'; +import { Lote } from '../../construction/entities/lote.entity'; +import { Departamento } from '../../construction/entities/departamento.entity'; import { FotoAvance } from './foto-avance.entity'; export type AdvanceStatus = 'pending' | 'captured' | 'reviewed' | 'approved' | 'rejected'; @@ -122,6 +124,14 @@ export class AvanceObra { @JoinColumn({ name: 'approved_by' }) approvedBy: User | null; + @ManyToOne(() => Lote) + @JoinColumn({ name: 'lote_id' }) + lote: Lote | null; + + @ManyToOne(() => Departamento) + @JoinColumn({ name: 'departamento_id' }) + departamento: Departamento | null; + @OneToMany(() => FotoAvance, (f) => f.avance) fotos: FotoAvance[]; }