/** * BidBudget Entity - Presupuesto de Licitación * * Desglose del presupuesto para la propuesta económica. * * @module Bidding */ import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, Index, } from 'typeorm'; import { Bid } from './bid.entity'; export type BudgetItemType = | 'direct_cost' | 'indirect_cost' | 'labor' | 'materials' | 'equipment' | 'subcontract' | 'overhead' | 'profit' | 'contingency' | 'financing' | 'taxes' | 'bonds' | 'other'; export type BudgetStatus = 'draft' | 'calculated' | 'reviewed' | 'approved' | 'locked'; @Entity('bid_budget', { schema: 'bidding' }) @Index(['tenantId', 'bidId']) @Index(['tenantId', 'itemType']) export class BidBudget { @PrimaryGeneratedColumn('uuid') id!: string; @Column({ name: 'tenant_id', type: 'uuid' }) @Index() tenantId!: string; // Referencia a licitación @Column({ name: 'bid_id', type: 'uuid' }) bidId!: string; @ManyToOne(() => Bid, (bid) => bid.budgetItems) @JoinColumn({ name: 'bid_id' }) bid?: Bid; // Jerarquía @Column({ name: 'parent_id', type: 'uuid', nullable: true }) parentId?: string; @Column({ name: 'sort_order', type: 'int', default: 0 }) sortOrder!: number; @Column({ type: 'int', default: 0 }) level!: number; @Column({ length: 50 }) code!: string; // Información del item @Column({ length: 255 }) name!: string; @Column({ type: 'text', nullable: true }) description?: string; @Column({ name: 'item_type', type: 'enum', enum: ['direct_cost', 'indirect_cost', 'labor', 'materials', 'equipment', 'subcontract', 'overhead', 'profit', 'contingency', 'financing', 'taxes', 'bonds', 'other'], enumName: 'bid_budget_item_type', }) itemType!: BudgetItemType; @Column({ type: 'enum', enum: ['draft', 'calculated', 'reviewed', 'approved', 'locked'], enumName: 'bid_budget_status', default: 'draft', }) status!: BudgetStatus; // Unidad y cantidad @Column({ length: 20, nullable: true }) unit?: string; @Column({ type: 'decimal', precision: 18, scale: 4, default: 0, }) quantity!: number; // Precios @Column({ name: 'unit_price', type: 'decimal', precision: 18, scale: 4, default: 0, }) unitPrice!: number; @Column({ name: 'total_amount', type: 'decimal', precision: 18, scale: 2, default: 0, }) totalAmount!: number; // Desglose de costos directos @Column({ name: 'materials_cost', type: 'decimal', precision: 18, scale: 2, nullable: true, }) materialsCost?: number; @Column({ name: 'labor_cost', type: 'decimal', precision: 18, scale: 2, nullable: true, }) laborCost?: number; @Column({ name: 'equipment_cost', type: 'decimal', precision: 18, scale: 2, nullable: true, }) equipmentCost?: number; @Column({ name: 'subcontract_cost', type: 'decimal', precision: 18, scale: 2, nullable: true, }) subcontractCost?: number; // Porcentajes @Column({ name: 'indirect_percentage', type: 'decimal', precision: 5, scale: 2, nullable: true, }) indirectPercentage?: number; @Column({ name: 'profit_percentage', type: 'decimal', precision: 5, scale: 2, nullable: true, }) profitPercentage?: number; @Column({ name: 'financing_percentage', type: 'decimal', precision: 5, scale: 2, nullable: true, }) financingPercentage?: number; // Comparación con base de licitación @Column({ name: 'base_amount', type: 'decimal', precision: 18, scale: 2, nullable: true, }) baseAmount?: number; @Column({ name: 'variance_amount', type: 'decimal', precision: 18, scale: 2, nullable: true, }) varianceAmount?: number; @Column({ name: 'variance_percentage', type: 'decimal', precision: 8, scale: 2, nullable: true, }) variancePercentage?: number; // Flags @Column({ name: 'is_summary', type: 'boolean', default: false }) isSummary!: boolean; @Column({ name: 'is_calculated', type: 'boolean', default: false }) isCalculated!: boolean; @Column({ name: 'is_adjusted', type: 'boolean', default: false }) isAdjusted!: boolean; @Column({ name: 'adjustment_reason', type: 'text', nullable: true }) adjustmentReason?: string; // Referencia a concepto de catálogo @Column({ name: 'catalog_concept_id', type: 'uuid', nullable: true }) catalogConceptId?: string; // Notas y metadatos @Column({ type: 'text', nullable: true }) notes?: string; @Column({ type: 'jsonb', nullable: true }) metadata?: Record; // Auditoría @Column({ name: 'created_by', type: 'uuid', nullable: true }) createdBy?: string; @Column({ name: 'updated_by', type: 'uuid', nullable: true }) updatedBy?: string; @CreateDateColumn({ name: 'created_at', type: 'timestamptz' }) createdAt!: Date; @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' }) updatedAt!: Date; @Column({ name: 'deleted_at', type: 'timestamptz', nullable: true }) deletedAt?: Date; }