This commit is contained in:
Marcelo Dares
2026-04-29 01:15:50 +02:00
parent 65aaf9275e
commit ea23136288
172 changed files with 30358 additions and 353 deletions

View File

@@ -60,6 +60,7 @@ enum LicitationSource {
MUNICIPAL_OPEN_PORTAL
PNT
MUNICIPAL_BACKUP
LICITAYA
}
enum LicitationProcedureType {
@@ -83,11 +84,151 @@ enum SyncRunStatus {
FAILED
}
enum ProposalStatus {
DRAFT
IN_PROGRESS
SUBMITTED
ARCHIVED
}
enum LicitationReviewStatus {
NEW
REVIEWED
INTERESTED
DISCARDED
}
enum NormativeDocumentType {
BASES_LICITACION
CONVOCATORIA
REGLAMENTO
LEY
OTRO
}
enum NormativeAnalysisMethod {
DIRECT
OCR
}
enum NormativeConfidence {
LOW
MEDIUM
HIGH
}
enum NormativeRiskLevel {
ALTO
MEDIO
BAJO
}
enum OfficialNormativeSourceType {
LEY
REGLAMENTO
LINEAMIENTO
PORTAL
}
enum NormativeVerificationStatus {
SUCCESS
WARNING
FAILED
}
enum MunicipalOpenPortalType {
GENERIC
SAN_PEDRO_ASPX
}
enum ContractStatus {
ACTIVE
COMPLETED
PAUSED
CANCELLED
}
enum ContractDeliverableStatus {
PENDING
DELIVERED
APPROVED
REJECTED
OVERDUE
}
enum ContractPaymentStatus {
REGISTERED
CONFIRMED
DISPUTED
}
enum ContractDocumentKind {
SIGNED_CONTRACT
ADDENDUM
DELIVERABLE_EVIDENCE
PAYMENT_EVIDENCE
OTHER
}
enum LegalCaseType {
CONTRACT_BREACH
PAYMENT_RETENTION
UNJUST_SANCTION
CONTRACT_DISPUTE
}
enum LegalCaseSeverity {
LOW
MEDIUM
HIGH
}
enum LegalCaseStatus {
OPEN
IN_PROGRESS
ESCALATED
RESOLVED
CLOSED
}
enum LegalJurisdictionLevel {
FEDERAL
STATE
MUNICIPAL
}
enum AuditSimulationStatus {
DRAFT
COMPLETED
}
enum AuditSimulationSectionStatus {
READY
WARNING
CRITICAL
}
enum AiSuggestionStatus {
GENERATED
ACCEPTED
DISMISSED
EXPIRED
}
enum ModulePlanKey {
PLAN_2_4
PLAN_5_7
PLAN_8_10
}
enum ModulePlanPurchaseStatus {
PENDING
APPROVED
REJECTED
CANCELLED
EXPIRED
}
model User {
id String @id @default(cuid())
email String @unique
@@ -103,9 +244,23 @@ model User {
strategicDiagnosticEvidenceDocs StrategicDiagnosticEvidenceDocument[]
workshopProgresses DevelopmentWorkshopProgress[]
workshopEvidenceDocs DevelopmentWorkshopEvidence[]
proposals Proposal[]
proposalDocuments ProposalDocument[]
licitationPreferences LicitationUserPreference[]
normativeAnalyses NormativeAnalysisHistory[]
contracts ContractRecord[]
contractExtractionHistories ContractExtractionHistory[]
legalCases LegalCase[]
legalDiagnoses LegalDiagnosis[]
legalDocuments LegalDocument[]
auditSimulations AuditSimulation[]
institutionalDossierSnapshots InstitutionalDossierSnapshot[]
verificationTokens EmailVerificationToken[]
responses Response[]
results AssessmentResult[]
aiSuggestions AiSuggestion[]
modulePlanPurchases ModulePlanPurchase[]
modulePlanSubscriptions ModulePlanSubscription[]
}
model Organization {
@@ -192,13 +347,13 @@ model StrategicDiagnosticEvidenceDocument {
}
model DiagnosticModule {
id String @id @default(cuid())
key String @unique
id String @id @default(cuid())
key String @unique
name String
description String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
questions Question[]
results AssessmentResult[]
recommendations Recommendation[]
@@ -206,39 +361,39 @@ model DiagnosticModule {
}
model DevelopmentWorkshop {
id String @id @default(cuid())
key String @unique
moduleId String
title String
summary String
videoUrl String
durationMinutes Int @default(0)
evidenceRequired String
id String @id @default(cuid())
key String @unique
moduleId String
title String
summary String
videoUrl String
durationMinutes Int @default(0)
evidenceRequired String
learningObjectives Json?
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
module DiagnosticModule @relation(fields: [moduleId], references: [id], onDelete: Cascade)
progresses DevelopmentWorkshopProgress[]
evidences DevelopmentWorkshopEvidence[]
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
module DiagnosticModule @relation(fields: [moduleId], references: [id], onDelete: Cascade)
progresses DevelopmentWorkshopProgress[]
evidences DevelopmentWorkshopEvidence[]
@@index([moduleId, sortOrder])
@@index([isActive, sortOrder])
}
model DevelopmentWorkshopProgress {
id String @id @default(cuid())
id String @id @default(cuid())
workshopId String
userId String
status WorkshopProgressStatus @default(NOT_STARTED)
watchedAt DateTime?
skippedAt DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
workshop DevelopmentWorkshop @relation(fields: [workshopId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
workshop DevelopmentWorkshop @relation(fields: [workshopId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([workshopId, userId])
@@index([userId, status])
@@ -384,6 +539,48 @@ model EmailVerificationToken {
@@index([expiresAt])
}
model ModulePlanPurchase {
id String @id @default(cuid())
userId String
planKey ModulePlanKey
status ModulePlanPurchaseStatus @default(PENDING)
externalReference String @unique
mercadoPreferenceId String?
mercadoPaymentId String?
mercadoOrderId String?
checkoutUrl String?
amount Decimal @db.Decimal(14, 2)
currency String @default("MXN")
requestJson Json?
responseJson Json?
approvedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
subscriptions ModulePlanSubscription[]
@@index([userId, planKey, createdAt])
@@index([status, updatedAt])
@@index([mercadoPaymentId])
}
model ModulePlanSubscription {
id String @id @default(cuid())
userId String
planKey ModulePlanKey
sourcePurchaseId String?
isActive Boolean @default(true)
startsAt DateTime @default(now())
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sourcePurchase ModulePlanPurchase? @relation(fields: [sourcePurchaseId], references: [id], onDelete: SetNull)
@@unique([userId, planKey])
@@index([userId, isActive, expiresAt])
}
model Municipality {
id String @id @default(cuid())
stateCode String
@@ -412,31 +609,33 @@ model Municipality {
}
model Licitation {
id String @id @default(cuid())
municipalityId String
source LicitationSource
sourceRecordId String
tenderCode String?
procedureType LicitationProcedureType @default(UNKNOWN)
title String
description String?
category LicitationCategory? @default(UNKNOWN)
isOpen Boolean @default(true)
openingDate DateTime?
closingDate DateTime?
publishDate DateTime?
eventDates Json?
amount Decimal? @db.Decimal(14, 2)
currency String?
status String?
supplierAwarded String?
documents Json?
rawSourceUrl String?
rawPayload Json
lastSeenAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
municipality Municipality @relation(fields: [municipalityId], references: [id], onDelete: Cascade)
id String @id @default(cuid())
municipalityId String
source LicitationSource
sourceRecordId String
tenderCode String?
procedureType LicitationProcedureType @default(UNKNOWN)
title String
description String?
category LicitationCategory? @default(UNKNOWN)
isOpen Boolean @default(true)
openingDate DateTime?
closingDate DateTime?
publishDate DateTime?
eventDates Json?
amount Decimal? @db.Decimal(14, 2)
currency String?
status String?
supplierAwarded String?
documents Json?
rawSourceUrl String?
rawPayload Json
lastSeenAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
municipality Municipality @relation(fields: [municipalityId], references: [id], onDelete: Cascade)
userPreferences LicitationUserPreference[]
normativeAnalyses NormativeAnalysisHistory[]
@@unique([municipalityId, source, sourceRecordId])
@@index([municipalityId, isOpen, closingDate])
@@ -475,3 +674,400 @@ model CompanyProfile {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: SetNull)
}
model LicitationUserPreference {
id String @id @default(cuid())
userId String
licitationId String
status LicitationReviewStatus @default(NEW)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
licitation Licitation @relation(fields: [licitationId], references: [id], onDelete: Cascade)
@@unique([userId, licitationId])
@@index([userId, status])
@@index([licitationId, status])
}
model NormativeAnalysisHistory {
id String @id @default(cuid())
userId String
sourceLicitationId String?
fileName String
documentType NormativeDocumentType
issuingEntity String?
methodUsed NormativeAnalysisMethod
numPages Int
warnings Json?
extractedChars Int
confidence NormativeConfidence
viabilityScore Int
riskLevel NormativeRiskLevel
executiveSummary String
result Json
analyzedAt DateTime @default(now())
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sourceLicitation Licitation? @relation(fields: [sourceLicitationId], references: [id], onDelete: SetNull)
@@index([userId, analyzedAt])
@@index([userId, deletedAt])
@@index([sourceLicitationId])
}
model Proposal {
id String @id @default(cuid())
userId String
sourceLicitationId String?
title String
issuingEntity String
summary String @default("")
workflowDraft Json?
currentStep Int @default(1)
completionPercent Int @default(0)
readyForSubmissionAt DateTime?
status ProposalStatus @default(DRAFT)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
documents ProposalDocument[]
contracts ContractRecord[]
@@index([userId, status])
@@index([updatedAt])
@@index([sourceLicitationId])
}
model ContractRecord {
id String @id @default(cuid())
userId String
sourceProposalId String?
title String
counterpartyEntity String
contractNumber String?
contractType String
startDate DateTime?
endDate DateTime?
totalAmount Decimal? @db.Decimal(14, 2)
currency String @default("MXN")
status ContractStatus @default(ACTIVE)
description String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sourceProposal Proposal? @relation(fields: [sourceProposalId], references: [id], onDelete: SetNull)
deliverables ContractDeliverable[]
payments ContractPayment[]
documents ContractDocument[]
extractions ContractExtractionHistory[]
legalCases LegalCase[]
@@index([userId, status, endDate])
@@index([sourceProposalId])
@@index([updatedAt])
}
model ContractDeliverable {
id String @id @default(cuid())
contractId String
title String
dueDate DateTime?
amountLinked Decimal? @db.Decimal(14, 2)
status ContractDeliverableStatus @default(PENDING)
deliveredAt DateTime?
approvedAt DateTime?
notes String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contract ContractRecord @relation(fields: [contractId], references: [id], onDelete: Cascade)
@@index([contractId, status, dueDate])
@@index([dueDate])
}
model ContractPayment {
id String @id @default(cuid())
contractId String
amount Decimal @db.Decimal(14, 2)
paymentDate DateTime
invoiceNumber String?
concept String @default("")
status ContractPaymentStatus @default(REGISTERED)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contract ContractRecord @relation(fields: [contractId], references: [id], onDelete: Cascade)
@@index([contractId, paymentDate])
@@index([status, paymentDate])
}
model ContractDocument {
id String @id @default(cuid())
contractId String
fileName String
filePath String
mimeType String
sizeBytes Int
checksumSha256 String?
kind ContractDocumentKind @default(OTHER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contract ContractRecord @relation(fields: [contractId], references: [id], onDelete: Cascade)
@@index([contractId, kind, createdAt])
}
model ContractExtractionHistory {
id String @id @default(cuid())
contractId String?
userId String
engine String
model String?
resultJson Json
warningsJson Json?
analyzedAt DateTime @default(now())
createdAt DateTime @default(now())
contract ContractRecord? @relation(fields: [contractId], references: [id], onDelete: SetNull)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, analyzedAt])
@@index([contractId, analyzedAt])
}
model LegalCase {
id String @id @default(cuid())
userId String
contractId String?
caseType LegalCaseType
severity LegalCaseSeverity
counterparty String
description String
amountAtRisk Decimal? @db.Decimal(14, 2)
status LegalCaseStatus @default(OPEN)
openedAt DateTime @default(now())
resolvedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
contract ContractRecord? @relation(fields: [contractId], references: [id], onDelete: SetNull)
diagnoses LegalDiagnosis[]
escalationLogs LegalEscalationStepLog[]
documents LegalDocument[]
@@index([userId, status, severity])
@@index([contractId])
}
model LegalDiagnosis {
id String @id @default(cuid())
userId String
legalCaseId String?
stepIndex Int @default(1)
totalSteps Int @default(4)
answersJson Json
recommendedRouteJson Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
legalCase LegalCase? @relation(fields: [legalCaseId], references: [id], onDelete: SetNull)
@@index([userId, updatedAt])
@@index([legalCaseId])
}
model LegalEscalationStepLog {
id String @id @default(cuid())
legalCaseId String
routeStepKey String
completedAt DateTime?
notes String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
legalCase LegalCase @relation(fields: [legalCaseId], references: [id], onDelete: Cascade)
@@unique([legalCaseId, routeStepKey])
@@index([legalCaseId, completedAt])
}
model LegalDocument {
id String @id @default(cuid())
legalCaseId String?
userId String
templateKey String?
aiGenerated Boolean @default(false)
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
legalCase LegalCase? @relation(fields: [legalCaseId], references: [id], onDelete: SetNull)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, createdAt])
@@index([legalCaseId, createdAt])
}
model LegalDirectoryEntity {
id String @id @default(cuid())
jurisdictionLevel LegalJurisdictionLevel
name String
scopeTagsJson Json
websiteUrl String?
phone String?
email String?
stateCode String?
municipalityCode String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([jurisdictionLevel, isActive])
@@index([stateCode, municipalityCode, isActive])
}
model AuditSimulation {
id String @id @default(cuid())
userId String
name String
auditType String
status AuditSimulationStatus @default(DRAFT)
overallScore Int?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sections AuditSimulationSection[]
responses AuditChecklistResponse[]
@@index([userId, status, updatedAt])
}
model AuditSimulationSection {
id String @id @default(cuid())
simulationId String
key String
score Int?
status AuditSimulationSectionStatus @default(READY)
findingsJson Json?
recommendationsJson Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
simulation AuditSimulation @relation(fields: [simulationId], references: [id], onDelete: Cascade)
@@unique([simulationId, key])
@@index([status])
}
model AuditChecklistResponse {
id String @id @default(cuid())
simulationId String
questionKey String
answer String
evidenceRefsJson Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
simulation AuditSimulation @relation(fields: [simulationId], references: [id], onDelete: Cascade)
@@index([simulationId, questionKey])
}
model InstitutionalDossierSnapshot {
id String @id @default(cuid())
userId String
generatedAt DateTime @default(now())
payloadJson Json
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, generatedAt])
}
model ProposalDocument {
id String @id @default(cuid())
proposalId String
userId String
fileName String
storedFileName String
filePath String
mimeType String
sizeBytes Int
checksumSha256 String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
proposal Proposal @relation(fields: [proposalId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([proposalId, createdAt])
@@index([userId, createdAt])
}
model OfficialNormativeSource {
id String @id
stateCode String
stateName String
municipalityCode String?
municipalityName String?
authorityName String
title String
officialUrl String
sourceType OfficialNormativeSourceType
versionLabel String?
isPilot Boolean @default(false)
lastKnownHash String?
lastVerifiedAt DateTime?
nextCheckAt DateTime?
lastStatus NormativeVerificationStatus?
lastMessage String?
lastChangedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([stateCode, municipalityCode, isPilot])
@@index([nextCheckAt])
@@index([lastStatus, lastVerifiedAt])
}
model OfficialNormativeSuggestion {
id String @id @default(cuid())
stateCode String
municipalityCode String?
authorityName String
title String
officialUrl String
sourceType OfficialNormativeSourceType
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([stateCode, municipalityCode, createdAt])
}
model AiSuggestion {
id String @id @default(cuid())
userId String
moduleKey String
featureKey String
subjectType String
subjectId String
inputHash String
requestJson Json
responseJson Json
confidence Float?
engine String
model String?
usageJson Json?
warningsJson Json?
promptVersion String
status AiSuggestionStatus @default(GENERATED)
actedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, moduleKey, featureKey, subjectType, subjectId, inputHash], map: "ai_suggestion_dedupe")
@@index([userId, moduleKey, featureKey, createdAt])
@@index([status, updatedAt])
@@index([inputHash])
}