import Dexie, { type Table } from 'dexie' import type { Process, Activity, GatewayConfig, Resource, Simulation } from '@/domain/types' export class ProcessCostDb extends Dexie { processes!: Table activities!: Table gateways!: Table resources!: Table simulations!: Table constructor() { super('ProcessCostPlatform') this.version(1).stores({ processes: 'id, name, updatedAt', activities: 'id, processId, bpmnElementId', gateways: 'id, processId, bpmnElementId', resources: 'id, processId, name', simulations: 'id, processId, executedAt', }) // Sprint 1: agrega campos de automatización a Activity y volumetría a Process. // El upgrade popula datos existentes con defaults para garantizar invariante de tipo. this.version(2) .stores({ processes: 'id, name, updatedAt', activities: 'id, processId, bpmnElementId', gateways: 'id, processId, bpmnElementId', resources: 'id, processId, name', simulations: 'id, processId, executedAt', }) .upgrade(async (tx) => { await tx.table('activities').toCollection().modify((act) => { if (act.automatable === undefined) act.automatable = false if (act.automatedCostFixed === undefined) act.automatedCostFixed = 0 if (act.automatedTimeMinutes === undefined) act.automatedTimeMinutes = 0 }) await tx.table('processes').toCollection().modify((proc) => { if (proc.annualFrequency === undefined) proc.annualFrequency = 1000 if (proc.analysisHorizonYears === undefined) proc.analysisHorizonYears = 3 if (proc.automationInvestment === undefined) proc.automationInvestment = 0 }) }) // Sprint 2: migra ActivityResourceAssignment.utilizationPercent → utilizationMinutes + units. // Los stores no cambian — assignments están embebidos en Activity, no son tabla propia. this.version(3) .stores({ processes: 'id, name, updatedAt', activities: 'id, processId, bpmnElementId', gateways: 'id, processId, bpmnElementId', resources: 'id, processId, name', simulations: 'id, processId, executedAt', }) .upgrade(async (tx) => { await tx.table('activities').toCollection().modify((act) => { if (!Array.isArray(act.assignedResources)) { act.assignedResources = [] return } act.assignedResources = act.assignedResources.map((assignment: Record) => { if ('utilizationPercent' in assignment) { // Heurística: minutos = round(executionTimeMinutes × utilizationPercent) // Si executionTimeMinutes no está disponible o es 0, usar 0 (conservador) const execMinutes = typeof act.executionTimeMinutes === 'number' ? act.executionTimeMinutes : 0 const utilizationMinutes = Math.round(execMinutes * (assignment.utilizationPercent as number)) return { resourceId: assignment.resourceId, utilizationMinutes, units: 1 } } if ('utilizationMinutes' in assignment) { // Ya está en nuevo formato (idempotencia) return { resourceId: assignment.resourceId, utilizationMinutes: assignment.utilizationMinutes ?? 0, units: assignment.units ?? 1, } } // Fallback defensivo return { resourceId: assignment.resourceId, utilizationMinutes: 0, units: 1 } }) }) }) } } export const db = new ProcessCostDb()