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 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-01-30 17:06:05 -06:00
parent 598c3215e1
commit 6d84520811
6 changed files with 261 additions and 0 deletions

View File

@ -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;
}

View File

@ -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[];
}

View File

@ -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';

View File

@ -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[];
}

View File

@ -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[];
}

View File

@ -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[];
}