478 lines
16 KiB
Plaintext
478 lines
16 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
|
|
}
|
|
|
|
enum LicitationProcedureType {
|
|
LICITACION_PUBLICA
|
|
INVITACION_RESTRINGIDA
|
|
ADJUDICACION_DIRECTA
|
|
UNKNOWN
|
|
}
|
|
|
|
enum LicitationCategory {
|
|
GOODS
|
|
SERVICES
|
|
WORKS
|
|
MIXED
|
|
UNKNOWN
|
|
}
|
|
|
|
enum SyncRunStatus {
|
|
SUCCESS
|
|
PARTIAL
|
|
FAILED
|
|
}
|
|
|
|
enum MunicipalOpenPortalType {
|
|
GENERIC
|
|
SAN_PEDRO_ASPX
|
|
}
|
|
|
|
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[]
|
|
verificationTokens EmailVerificationToken[]
|
|
responses Response[]
|
|
results AssessmentResult[]
|
|
}
|
|
|
|
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 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)
|
|
|
|
@@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)
|
|
}
|