Files
Kontia/prisma/schema.prisma
Marcelo Dares ea23136288 changes
2026-04-29 01:15:50 +02:00

1074 lines
33 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum PriorityLevel {
LOW
MEDIUM
HIGH
}
enum UserRole {
USER
ADMIN
}
enum ContentPageType {
FAQ
MANUAL
}
enum OverallScoreMethod {
EQUAL_ALL_MODULES
EQUAL_ANSWERED_MODULES
WEIGHTED_ANSWERED_MODULES
}
enum OrganizationDocumentType {
ACTA_CONSTITUTIVA
}
enum StrategicDiagnosticEvidenceSection {
TECHNICAL
EXPERIENCE
ORGANIZATION
PUBLIC_PROCUREMENT
}
enum WorkshopProgressStatus {
NOT_STARTED
WATCHED
EVIDENCE_SUBMITTED
APPROVED
REJECTED
SKIPPED
}
enum WorkshopEvidenceValidationStatus {
PENDING
APPROVED
REJECTED
ERROR
}
enum LicitationSource {
MUNICIPAL_OPEN_PORTAL
PNT
MUNICIPAL_BACKUP
LICITAYA
}
enum LicitationProcedureType {
LICITACION_PUBLICA
INVITACION_RESTRINGIDA
ADJUDICACION_DIRECTA
UNKNOWN
}
enum LicitationCategory {
GOODS
SERVICES
WORKS
MIXED
UNKNOWN
}
enum SyncRunStatus {
SUCCESS
PARTIAL
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
passwordHash String
name String?
role UserRole @default(USER)
emailVerifiedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization?
companyProfile CompanyProfile?
organizationDocs OrganizationDocument[]
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 {
id String @id @default(cuid())
userId String @unique
name String
tradeName String?
rfc String?
legalRepresentative String?
incorporationDate String?
deedNumber String?
notaryName String?
stateOfIncorporation String?
companyType String?
fiscalAddress String?
businessPurpose String?
industry String?
operatingState String?
municipality String?
companySize String?
yearsOfOperation String?
annualRevenueRange String?
hasGovernmentContracts Boolean?
country String?
primaryObjective String?
actaExtractedData Json?
actaLookupDictionary Json?
actaUploadedAt DateTime?
onboardingCompletedAt DateTime?
strategicDiagnosticData Json?
strategicDiagnosticScores Json?
strategicDiagnosticCompletedAt DateTime?
companyProfile CompanyProfile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
documents OrganizationDocument[]
strategicDiagnosticEvidenceDocs StrategicDiagnosticEvidenceDocument[]
}
model OrganizationDocument {
id String @id @default(cuid())
organizationId String
userId String
type OrganizationDocumentType
fileName String
storedFileName String
filePath String
mimeType String
sizeBytes Int
checksumSha256 String?
extractedData Json?
extractedTextSnippet String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([organizationId, type])
@@unique([userId, type])
@@index([type])
}
model StrategicDiagnosticEvidenceDocument {
id String @id @default(cuid())
organizationId String
userId String
section StrategicDiagnosticEvidenceSection
category String
fileName String
storedFileName String
filePath String
mimeType String
sizeBytes Int
checksumSha256 String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([organizationId, section])
@@index([userId, section])
@@index([createdAt])
}
model DiagnosticModule {
id String @id @default(cuid())
key String @unique
name String
description String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
questions Question[]
results AssessmentResult[]
recommendations Recommendation[]
workshops DevelopmentWorkshop[]
}
model DevelopmentWorkshop {
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[]
@@index([moduleId, sortOrder])
@@index([isActive, sortOrder])
}
model DevelopmentWorkshopProgress {
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)
@@unique([workshopId, userId])
@@index([userId, status])
@@index([workshopId, status])
}
model DevelopmentWorkshopEvidence {
id String @id @default(cuid())
workshopId String
userId String
validationStatus WorkshopEvidenceValidationStatus @default(PENDING)
validationReason String?
validationConfidence Float?
validatedAt DateTime?
fileName String
storedFileName String
filePath String
mimeType String
sizeBytes Int
checksumSha256 String?
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)
@@index([userId, workshopId, createdAt])
@@index([workshopId, validationStatus])
}
model Question {
id String @id @default(cuid())
key String @unique
moduleId String
prompt String
helpText String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
module DiagnosticModule @relation(fields: [moduleId], references: [id], onDelete: Cascade)
answerOptions AnswerOption[]
responses Response[]
@@index([moduleId, sortOrder])
}
model AnswerOption {
id String @id @default(cuid())
key String @unique
questionId String
label String
weight Int
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
responses Response[]
@@index([questionId, sortOrder])
}
model Response {
id String @id @default(cuid())
userId String
questionId String
answerOptionId String
evidence Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
answerOption AnswerOption @relation(fields: [answerOptionId], references: [id], onDelete: Cascade)
@@unique([userId, questionId])
@@index([userId])
}
model AssessmentResult {
id String @id @default(cuid())
userId String
moduleId String?
overallScore Float?
moduleScore Float?
metadata Json?
calculatedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
module DiagnosticModule? @relation(fields: [moduleId], references: [id], onDelete: SetNull)
@@index([userId, calculatedAt])
@@index([moduleId])
}
model Recommendation {
id String @id @default(cuid())
key String @unique
moduleId String?
title String
description String
priority PriorityLevel @default(MEDIUM)
isTemplate Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
module DiagnosticModule? @relation(fields: [moduleId], references: [id], onDelete: SetNull)
@@index([moduleId])
}
model ContentPage {
id String @id @default(cuid())
type ContentPageType
slug String @unique
title String
content String
sortOrder Int @default(0)
isPublished Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([type, sortOrder])
}
model ScoringConfig {
id String @id @default(cuid())
key String @unique
lowScoreThreshold Int @default(70)
overallScoreMethod OverallScoreMethod @default(EQUAL_ALL_MODULES)
moduleWeights Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EmailVerificationToken {
id String @id @default(cuid())
userId String
token String @unique
expiresAt DateTime
consumedAt DateTime?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@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
stateName String
municipalityCode String
municipalityName String
openPortalUrl String?
openPortalType MunicipalOpenPortalType @default(GENERIC)
openSyncIntervalDays Int @default(7)
lastOpenSyncAt DateTime?
pntSubjectId String?
pntEntityId String?
pntSectorId String?
pntEntryUrl String?
backupUrl String?
scrapingEnabled Boolean @default(true)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
licitations Licitation[]
syncRuns SyncRun[]
@@unique([stateCode, municipalityCode])
@@index([stateCode, municipalityName])
@@index([isActive, scrapingEnabled])
}
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)
userPreferences LicitationUserPreference[]
normativeAnalyses NormativeAnalysisHistory[]
@@unique([municipalityId, source, sourceRecordId])
@@index([municipalityId, isOpen, closingDate])
@@index([municipalityId, publishDate])
@@index([procedureType, category])
@@index([amount])
@@index([createdAt])
}
model SyncRun {
id String @id @default(cuid())
startedAt DateTime @default(now())
finishedAt DateTime?
municipalityId String?
source LicitationSource
status SyncRunStatus @default(SUCCESS)
stats Json?
error String?
municipality Municipality? @relation(fields: [municipalityId], references: [id], onDelete: SetNull)
@@index([municipalityId, startedAt])
@@index([source, status, startedAt])
}
model CompanyProfile {
id String @id @default(cuid())
userId String @unique
organizationId String? @unique
locations Json?
categoriesSupported Json?
keywords Json?
minAmount Decimal? @db.Decimal(14, 2)
maxAmount Decimal? @db.Decimal(14, 2)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
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])
}