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

@@ -0,0 +1,58 @@
-- CreateEnum
CREATE TYPE "ProposalStatus" AS ENUM ('DRAFT', 'IN_PROGRESS', 'SUBMITTED', 'ARCHIVED');
-- CreateTable
CREATE TABLE "Proposal" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"sourceLicitationId" TEXT,
"title" TEXT NOT NULL,
"issuingEntity" TEXT NOT NULL,
"summary" TEXT NOT NULL DEFAULT '',
"status" "ProposalStatus" NOT NULL DEFAULT 'DRAFT',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Proposal_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProposalDocument" (
"id" TEXT NOT NULL,
"proposalId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"storedFileName" TEXT NOT NULL,
"filePath" TEXT NOT NULL,
"mimeType" TEXT NOT NULL,
"sizeBytes" INTEGER NOT NULL,
"checksumSha256" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProposalDocument_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "Proposal_userId_status_idx" ON "Proposal"("userId", "status");
-- CreateIndex
CREATE INDEX "Proposal_updatedAt_idx" ON "Proposal"("updatedAt");
-- CreateIndex
CREATE INDEX "Proposal_sourceLicitationId_idx" ON "Proposal"("sourceLicitationId");
-- CreateIndex
CREATE INDEX "ProposalDocument_proposalId_createdAt_idx" ON "ProposalDocument"("proposalId", "createdAt");
-- CreateIndex
CREATE INDEX "ProposalDocument_userId_createdAt_idx" ON "ProposalDocument"("userId", "createdAt");
-- AddForeignKey
ALTER TABLE "Proposal" ADD CONSTRAINT "Proposal_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProposalDocument" ADD CONSTRAINT "ProposalDocument_proposalId_fkey" FOREIGN KEY ("proposalId") REFERENCES "Proposal"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProposalDocument" ADD CONSTRAINT "ProposalDocument_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "LicitationSource" ADD VALUE IF NOT EXISTS 'LICITAYA';

View File

@@ -0,0 +1,81 @@
-- CreateEnum
CREATE TYPE "LicitationReviewStatus" AS ENUM ('NEW', 'REVIEWED', 'INTERESTED', 'DISCARDED');
-- CreateEnum
CREATE TYPE "NormativeDocumentType" AS ENUM ('BASES_LICITACION', 'CONVOCATORIA', 'REGLAMENTO', 'LEY', 'OTRO');
-- CreateEnum
CREATE TYPE "NormativeAnalysisMethod" AS ENUM ('DIRECT', 'OCR');
-- CreateEnum
CREATE TYPE "NormativeConfidence" AS ENUM ('LOW', 'MEDIUM', 'HIGH');
-- CreateEnum
CREATE TYPE "NormativeRiskLevel" AS ENUM ('ALTO', 'MEDIO', 'BAJO');
-- CreateTable
CREATE TABLE "LicitationUserPreference" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"licitationId" TEXT NOT NULL,
"status" "LicitationReviewStatus" NOT NULL DEFAULT 'NEW',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LicitationUserPreference_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "NormativeAnalysisHistory" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"sourceLicitationId" TEXT,
"fileName" TEXT NOT NULL,
"documentType" "NormativeDocumentType" NOT NULL,
"issuingEntity" TEXT,
"methodUsed" "NormativeAnalysisMethod" NOT NULL,
"numPages" INTEGER NOT NULL,
"warnings" JSONB,
"extractedChars" INTEGER NOT NULL,
"confidence" "NormativeConfidence" NOT NULL,
"viabilityScore" INTEGER NOT NULL,
"riskLevel" "NormativeRiskLevel" NOT NULL,
"executiveSummary" TEXT NOT NULL,
"result" JSONB NOT NULL,
"analyzedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "NormativeAnalysisHistory_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "LicitationUserPreference_userId_licitationId_key" ON "LicitationUserPreference"("userId", "licitationId");
-- CreateIndex
CREATE INDEX "LicitationUserPreference_userId_status_idx" ON "LicitationUserPreference"("userId", "status");
-- CreateIndex
CREATE INDEX "LicitationUserPreference_licitationId_status_idx" ON "LicitationUserPreference"("licitationId", "status");
-- CreateIndex
CREATE INDEX "NormativeAnalysisHistory_userId_analyzedAt_idx" ON "NormativeAnalysisHistory"("userId", "analyzedAt");
-- CreateIndex
CREATE INDEX "NormativeAnalysisHistory_userId_deletedAt_idx" ON "NormativeAnalysisHistory"("userId", "deletedAt");
-- CreateIndex
CREATE INDEX "NormativeAnalysisHistory_sourceLicitationId_idx" ON "NormativeAnalysisHistory"("sourceLicitationId");
-- AddForeignKey
ALTER TABLE "LicitationUserPreference" ADD CONSTRAINT "LicitationUserPreference_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LicitationUserPreference" ADD CONSTRAINT "LicitationUserPreference_licitationId_fkey" FOREIGN KEY ("licitationId") REFERENCES "Licitation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "NormativeAnalysisHistory" ADD CONSTRAINT "NormativeAnalysisHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "NormativeAnalysisHistory" ADD CONSTRAINT "NormativeAnalysisHistory_sourceLicitationId_fkey" FOREIGN KEY ("sourceLicitationId") REFERENCES "Licitation"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "Proposal"
ADD COLUMN "workflowDraft" JSONB,
ADD COLUMN "currentStep" INTEGER NOT NULL DEFAULT 1,
ADD COLUMN "completionPercent" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN "readyForSubmissionAt" TIMESTAMP(3);

View File

@@ -0,0 +1,8 @@
-- RenameIndex (safe guard for environments where the old truncated index never existed)
DO $$
BEGIN
IF to_regclass('"OfficialNormativeSuggestion_stateCode_municipalityCode_createdA"') IS NOT NULL THEN
ALTER INDEX "OfficialNormativeSuggestion_stateCode_municipalityCode_createdA"
RENAME TO "OfficialNormativeSuggestion_stateCode_municipalityCode_crea_idx";
END IF;
END $$;

View File

@@ -0,0 +1,134 @@
-- CreateEnum
CREATE TYPE "OfficialNormativeSourceType" AS ENUM ('LEY', 'REGLAMENTO', 'LINEAMIENTO', 'PORTAL');
-- CreateEnum
CREATE TYPE "NormativeVerificationStatus" AS ENUM ('SUCCESS', 'WARNING', 'FAILED');
-- CreateTable
CREATE TABLE "OfficialNormativeSource" (
"id" TEXT NOT NULL,
"stateCode" TEXT NOT NULL,
"stateName" TEXT NOT NULL,
"municipalityCode" TEXT,
"municipalityName" TEXT,
"authorityName" TEXT NOT NULL,
"title" TEXT NOT NULL,
"officialUrl" TEXT NOT NULL,
"sourceType" "OfficialNormativeSourceType" NOT NULL,
"versionLabel" TEXT,
"isPilot" BOOLEAN NOT NULL DEFAULT false,
"lastKnownHash" TEXT,
"lastVerifiedAt" TIMESTAMP(3),
"nextCheckAt" TIMESTAMP(3),
"lastStatus" "NormativeVerificationStatus",
"lastMessage" TEXT,
"lastChangedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "OfficialNormativeSource_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OfficialNormativeSuggestion" (
"id" TEXT NOT NULL,
"stateCode" TEXT NOT NULL,
"municipalityCode" TEXT,
"authorityName" TEXT NOT NULL,
"title" TEXT NOT NULL,
"officialUrl" TEXT NOT NULL,
"sourceType" "OfficialNormativeSourceType" NOT NULL,
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "OfficialNormativeSuggestion_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "OfficialNormativeSource_stateCode_municipalityCode_isPilot_idx" ON "OfficialNormativeSource"("stateCode", "municipalityCode", "isPilot");
-- CreateIndex
CREATE INDEX "OfficialNormativeSource_nextCheckAt_idx" ON "OfficialNormativeSource"("nextCheckAt");
-- CreateIndex
CREATE INDEX "OfficialNormativeSource_lastStatus_lastVerifiedAt_idx" ON "OfficialNormativeSource"("lastStatus", "lastVerifiedAt");
-- CreateIndex
CREATE INDEX "OfficialNormativeSuggestion_stateCode_municipalityCode_createdAt_idx" ON "OfficialNormativeSuggestion"("stateCode", "municipalityCode", "createdAt");
-- Seed pilot sources (Nuevo Leon)
INSERT INTO "OfficialNormativeSource" (
"id",
"stateCode",
"stateName",
"municipalityCode",
"municipalityName",
"authorityName",
"title",
"officialUrl",
"sourceType",
"versionLabel",
"isPilot",
"createdAt",
"updatedAt"
)
VALUES
(
'nl-ley-adquisiciones-estatal',
'NL',
'Nuevo Leon',
NULL,
NULL,
'Gobierno del Estado de Nuevo Leon',
'Ley de Adquisiciones, Arrendamientos y Contratacion de Servicios del Estado de Nuevo Leon',
'https://www.hcnl.gob.mx/trabajo_legislativo/leyes/',
'LEY',
NULL,
true,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
'nl-reglamento-adquisiciones-estatal',
'NL',
'Nuevo Leon',
NULL,
NULL,
'Gobierno del Estado de Nuevo Leon',
'Reglamento de la Ley de Adquisiciones del Estado de Nuevo Leon',
'https://www.nl.gob.mx/',
'REGLAMENTO',
NULL,
true,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
'spgg-reglamento-adquisiciones',
'NL',
'Nuevo Leon',
'SPGG',
'San Pedro Garza Garcia',
'Municipio de San Pedro Garza Garcia',
'Reglamento de Adquisiciones y Contratacion de Servicios de San Pedro Garza Garcia',
'https://www.sanpedro.gob.mx/',
'REGLAMENTO',
NULL,
true,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
)
ON CONFLICT ("id") DO UPDATE
SET
"stateCode" = EXCLUDED."stateCode",
"stateName" = EXCLUDED."stateName",
"municipalityCode" = EXCLUDED."municipalityCode",
"municipalityName" = EXCLUDED."municipalityName",
"authorityName" = EXCLUDED."authorityName",
"title" = EXCLUDED."title",
"officialUrl" = EXCLUDED."officialUrl",
"sourceType" = EXCLUDED."sourceType",
"versionLabel" = EXCLUDED."versionLabel",
"isPilot" = EXCLUDED."isPilot",
"updatedAt" = CURRENT_TIMESTAMP;

View File

@@ -0,0 +1,376 @@
-- CreateEnum
CREATE TYPE "ContractStatus" AS ENUM ('ACTIVE', 'COMPLETED', 'PAUSED', 'CANCELLED');
-- CreateEnum
CREATE TYPE "ContractDeliverableStatus" AS ENUM ('PENDING', 'DELIVERED', 'APPROVED', 'REJECTED', 'OVERDUE');
-- CreateEnum
CREATE TYPE "ContractPaymentStatus" AS ENUM ('REGISTERED', 'CONFIRMED', 'DISPUTED');
-- CreateEnum
CREATE TYPE "ContractDocumentKind" AS ENUM ('SIGNED_CONTRACT', 'ADDENDUM', 'DELIVERABLE_EVIDENCE', 'PAYMENT_EVIDENCE', 'OTHER');
-- CreateEnum
CREATE TYPE "LegalCaseType" AS ENUM ('CONTRACT_BREACH', 'PAYMENT_RETENTION', 'UNJUST_SANCTION', 'CONTRACT_DISPUTE');
-- CreateEnum
CREATE TYPE "LegalCaseSeverity" AS ENUM ('LOW', 'MEDIUM', 'HIGH');
-- CreateEnum
CREATE TYPE "LegalCaseStatus" AS ENUM ('OPEN', 'IN_PROGRESS', 'ESCALATED', 'RESOLVED', 'CLOSED');
-- CreateEnum
CREATE TYPE "LegalJurisdictionLevel" AS ENUM ('FEDERAL', 'STATE', 'MUNICIPAL');
-- CreateEnum
CREATE TYPE "AuditSimulationStatus" AS ENUM ('DRAFT', 'COMPLETED');
-- CreateEnum
CREATE TYPE "AuditSimulationSectionStatus" AS ENUM ('READY', 'WARNING', 'CRITICAL');
-- CreateTable
CREATE TABLE "ContractRecord" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"sourceProposalId" TEXT,
"title" TEXT NOT NULL,
"counterpartyEntity" TEXT NOT NULL,
"contractNumber" TEXT,
"contractType" TEXT NOT NULL,
"startDate" TIMESTAMP(3),
"endDate" TIMESTAMP(3),
"totalAmount" DECIMAL(14,2),
"currency" TEXT NOT NULL DEFAULT 'MXN',
"status" "ContractStatus" NOT NULL DEFAULT 'ACTIVE',
"description" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ContractRecord_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ContractDeliverable" (
"id" TEXT NOT NULL,
"contractId" TEXT NOT NULL,
"title" TEXT NOT NULL,
"dueDate" TIMESTAMP(3),
"amountLinked" DECIMAL(14,2),
"status" "ContractDeliverableStatus" NOT NULL DEFAULT 'PENDING',
"deliveredAt" TIMESTAMP(3),
"approvedAt" TIMESTAMP(3),
"notes" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ContractDeliverable_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ContractPayment" (
"id" TEXT NOT NULL,
"contractId" TEXT NOT NULL,
"amount" DECIMAL(14,2) NOT NULL,
"paymentDate" TIMESTAMP(3) NOT NULL,
"invoiceNumber" TEXT,
"concept" TEXT NOT NULL DEFAULT '',
"status" "ContractPaymentStatus" NOT NULL DEFAULT 'REGISTERED',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ContractPayment_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ContractDocument" (
"id" TEXT NOT NULL,
"contractId" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"filePath" TEXT NOT NULL,
"mimeType" TEXT NOT NULL,
"sizeBytes" INTEGER NOT NULL,
"checksumSha256" TEXT,
"kind" "ContractDocumentKind" NOT NULL DEFAULT 'OTHER',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ContractDocument_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ContractExtractionHistory" (
"id" TEXT NOT NULL,
"contractId" TEXT,
"userId" TEXT NOT NULL,
"engine" TEXT NOT NULL,
"model" TEXT,
"resultJson" JSONB NOT NULL,
"warningsJson" JSONB,
"analyzedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ContractExtractionHistory_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LegalCase" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"contractId" TEXT,
"caseType" "LegalCaseType" NOT NULL,
"severity" "LegalCaseSeverity" NOT NULL,
"counterparty" TEXT NOT NULL,
"description" TEXT NOT NULL,
"amountAtRisk" DECIMAL(14,2),
"status" "LegalCaseStatus" NOT NULL DEFAULT 'OPEN',
"openedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"resolvedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LegalCase_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LegalDiagnosis" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"legalCaseId" TEXT,
"stepIndex" INTEGER NOT NULL DEFAULT 1,
"totalSteps" INTEGER NOT NULL DEFAULT 4,
"answersJson" JSONB NOT NULL,
"recommendedRouteJson" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LegalDiagnosis_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LegalEscalationStepLog" (
"id" TEXT NOT NULL,
"legalCaseId" TEXT NOT NULL,
"routeStepKey" TEXT NOT NULL,
"completedAt" TIMESTAMP(3),
"notes" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LegalEscalationStepLog_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LegalDocument" (
"id" TEXT NOT NULL,
"legalCaseId" TEXT,
"userId" TEXT NOT NULL,
"templateKey" TEXT,
"aiGenerated" BOOLEAN NOT NULL DEFAULT false,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LegalDocument_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LegalDirectoryEntity" (
"id" TEXT NOT NULL,
"jurisdictionLevel" "LegalJurisdictionLevel" NOT NULL,
"name" TEXT NOT NULL,
"scopeTagsJson" JSONB NOT NULL,
"websiteUrl" TEXT,
"phone" TEXT,
"email" TEXT,
"stateCode" TEXT,
"municipalityCode" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LegalDirectoryEntity_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AuditSimulation" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"auditType" TEXT NOT NULL,
"status" "AuditSimulationStatus" NOT NULL DEFAULT 'DRAFT',
"overallScore" INTEGER,
"completedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "AuditSimulation_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AuditSimulationSection" (
"id" TEXT NOT NULL,
"simulationId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"score" INTEGER,
"status" "AuditSimulationSectionStatus" NOT NULL DEFAULT 'READY',
"findingsJson" JSONB,
"recommendationsJson" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "AuditSimulationSection_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AuditChecklistResponse" (
"id" TEXT NOT NULL,
"simulationId" TEXT NOT NULL,
"questionKey" TEXT NOT NULL,
"answer" TEXT NOT NULL,
"evidenceRefsJson" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "AuditChecklistResponse_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstitutionalDossierSnapshot" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"generatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"payloadJson" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "InstitutionalDossierSnapshot_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "ContractRecord_userId_status_endDate_idx" ON "ContractRecord"("userId", "status", "endDate");
-- CreateIndex
CREATE INDEX "ContractRecord_sourceProposalId_idx" ON "ContractRecord"("sourceProposalId");
-- CreateIndex
CREATE INDEX "ContractRecord_updatedAt_idx" ON "ContractRecord"("updatedAt");
-- CreateIndex
CREATE INDEX "ContractDeliverable_contractId_status_dueDate_idx" ON "ContractDeliverable"("contractId", "status", "dueDate");
-- CreateIndex
CREATE INDEX "ContractDeliverable_dueDate_idx" ON "ContractDeliverable"("dueDate");
-- CreateIndex
CREATE INDEX "ContractPayment_contractId_paymentDate_idx" ON "ContractPayment"("contractId", "paymentDate");
-- CreateIndex
CREATE INDEX "ContractPayment_status_paymentDate_idx" ON "ContractPayment"("status", "paymentDate");
-- CreateIndex
CREATE INDEX "ContractDocument_contractId_kind_createdAt_idx" ON "ContractDocument"("contractId", "kind", "createdAt");
-- CreateIndex
CREATE INDEX "ContractExtractionHistory_userId_analyzedAt_idx" ON "ContractExtractionHistory"("userId", "analyzedAt");
-- CreateIndex
CREATE INDEX "ContractExtractionHistory_contractId_analyzedAt_idx" ON "ContractExtractionHistory"("contractId", "analyzedAt");
-- CreateIndex
CREATE INDEX "LegalCase_userId_status_severity_idx" ON "LegalCase"("userId", "status", "severity");
-- CreateIndex
CREATE INDEX "LegalCase_contractId_idx" ON "LegalCase"("contractId");
-- CreateIndex
CREATE INDEX "LegalDiagnosis_userId_updatedAt_idx" ON "LegalDiagnosis"("userId", "updatedAt");
-- CreateIndex
CREATE INDEX "LegalDiagnosis_legalCaseId_idx" ON "LegalDiagnosis"("legalCaseId");
-- CreateIndex
CREATE UNIQUE INDEX "LegalEscalationStepLog_legalCaseId_routeStepKey_key" ON "LegalEscalationStepLog"("legalCaseId", "routeStepKey");
-- CreateIndex
CREATE INDEX "LegalEscalationStepLog_legalCaseId_completedAt_idx" ON "LegalEscalationStepLog"("legalCaseId", "completedAt");
-- CreateIndex
CREATE INDEX "LegalDocument_userId_createdAt_idx" ON "LegalDocument"("userId", "createdAt");
-- CreateIndex
CREATE INDEX "LegalDocument_legalCaseId_createdAt_idx" ON "LegalDocument"("legalCaseId", "createdAt");
-- CreateIndex
CREATE INDEX "LegalDirectoryEntity_jurisdictionLevel_isActive_idx" ON "LegalDirectoryEntity"("jurisdictionLevel", "isActive");
-- CreateIndex
CREATE INDEX "LegalDirectoryEntity_stateCode_municipalityCode_isActive_idx" ON "LegalDirectoryEntity"("stateCode", "municipalityCode", "isActive");
-- CreateIndex
CREATE INDEX "AuditSimulation_userId_status_updatedAt_idx" ON "AuditSimulation"("userId", "status", "updatedAt");
-- CreateIndex
CREATE UNIQUE INDEX "AuditSimulationSection_simulationId_key_key" ON "AuditSimulationSection"("simulationId", "key");
-- CreateIndex
CREATE INDEX "AuditSimulationSection_status_idx" ON "AuditSimulationSection"("status");
-- CreateIndex
CREATE INDEX "AuditChecklistResponse_simulationId_questionKey_idx" ON "AuditChecklistResponse"("simulationId", "questionKey");
-- CreateIndex
CREATE INDEX "InstitutionalDossierSnapshot_userId_generatedAt_idx" ON "InstitutionalDossierSnapshot"("userId", "generatedAt");
-- AddForeignKey
ALTER TABLE "ContractRecord" ADD CONSTRAINT "ContractRecord_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractRecord" ADD CONSTRAINT "ContractRecord_sourceProposalId_fkey" FOREIGN KEY ("sourceProposalId") REFERENCES "Proposal"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractDeliverable" ADD CONSTRAINT "ContractDeliverable_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "ContractRecord"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractPayment" ADD CONSTRAINT "ContractPayment_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "ContractRecord"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractDocument" ADD CONSTRAINT "ContractDocument_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "ContractRecord"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractExtractionHistory" ADD CONSTRAINT "ContractExtractionHistory_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "ContractRecord"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ContractExtractionHistory" ADD CONSTRAINT "ContractExtractionHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalCase" ADD CONSTRAINT "LegalCase_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalCase" ADD CONSTRAINT "LegalCase_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "ContractRecord"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalDiagnosis" ADD CONSTRAINT "LegalDiagnosis_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalDiagnosis" ADD CONSTRAINT "LegalDiagnosis_legalCaseId_fkey" FOREIGN KEY ("legalCaseId") REFERENCES "LegalCase"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalEscalationStepLog" ADD CONSTRAINT "LegalEscalationStepLog_legalCaseId_fkey" FOREIGN KEY ("legalCaseId") REFERENCES "LegalCase"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalDocument" ADD CONSTRAINT "LegalDocument_legalCaseId_fkey" FOREIGN KEY ("legalCaseId") REFERENCES "LegalCase"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LegalDocument" ADD CONSTRAINT "LegalDocument_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AuditSimulation" ADD CONSTRAINT "AuditSimulation_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AuditSimulationSection" ADD CONSTRAINT "AuditSimulationSection_simulationId_fkey" FOREIGN KEY ("simulationId") REFERENCES "AuditSimulation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AuditChecklistResponse" ADD CONSTRAINT "AuditChecklistResponse_simulationId_fkey" FOREIGN KEY ("simulationId") REFERENCES "AuditSimulation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstitutionalDossierSnapshot" ADD CONSTRAINT "InstitutionalDossierSnapshot_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,9 @@
-- Normalize index name across environments where Postgres truncated long identifiers.
DO $$
BEGIN
IF to_regclass('"OfficialNormativeSuggestion_stateCode_municipalityCode_createdA"') IS NOT NULL
AND to_regclass('"OfficialNormativeSuggestion_stateCode_municipalityCode_crea_idx"') IS NULL THEN
ALTER INDEX "OfficialNormativeSuggestion_stateCode_municipalityCode_createdA"
RENAME TO "OfficialNormativeSuggestion_stateCode_municipalityCode_crea_idx";
END IF;
END $$;

View File

@@ -0,0 +1,42 @@
-- CreateEnum
CREATE TYPE "AiSuggestionStatus" AS ENUM ('GENERATED', 'ACCEPTED', 'DISMISSED', 'EXPIRED');
-- CreateTable
CREATE TABLE "AiSuggestion" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"moduleKey" TEXT NOT NULL,
"featureKey" TEXT NOT NULL,
"subjectType" TEXT NOT NULL,
"subjectId" TEXT NOT NULL,
"inputHash" TEXT NOT NULL,
"requestJson" JSONB NOT NULL,
"responseJson" JSONB NOT NULL,
"confidence" DOUBLE PRECISION,
"engine" TEXT NOT NULL,
"model" TEXT,
"usageJson" JSONB,
"warningsJson" JSONB,
"promptVersion" TEXT NOT NULL,
"status" "AiSuggestionStatus" NOT NULL DEFAULT 'GENERATED',
"actedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "AiSuggestion_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ai_suggestion_dedupe" ON "AiSuggestion"("userId", "moduleKey", "featureKey", "subjectType", "subjectId", "inputHash");
-- CreateIndex
CREATE INDEX "AiSuggestion_userId_moduleKey_featureKey_createdAt_idx" ON "AiSuggestion"("userId", "moduleKey", "featureKey", "createdAt");
-- CreateIndex
CREATE INDEX "AiSuggestion_status_updatedAt_idx" ON "AiSuggestion"("status", "updatedAt");
-- CreateIndex
CREATE INDEX "AiSuggestion_inputHash_idx" ON "AiSuggestion"("inputHash");
-- AddForeignKey
ALTER TABLE "AiSuggestion" ADD CONSTRAINT "AiSuggestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,69 @@
-- CreateEnum
CREATE TYPE "ModulePlanKey" AS ENUM ('PLAN_2_4', 'PLAN_5_7', 'PLAN_8_10');
-- CreateEnum
CREATE TYPE "ModulePlanPurchaseStatus" AS ENUM ('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED', 'EXPIRED');
-- CreateTable
CREATE TABLE "ModulePlanPurchase" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"planKey" "ModulePlanKey" NOT NULL,
"status" "ModulePlanPurchaseStatus" NOT NULL DEFAULT 'PENDING',
"externalReference" TEXT NOT NULL,
"mercadoPreferenceId" TEXT,
"mercadoPaymentId" TEXT,
"mercadoOrderId" TEXT,
"checkoutUrl" TEXT,
"amount" DECIMAL(14,2) NOT NULL,
"currency" TEXT NOT NULL DEFAULT 'MXN',
"requestJson" JSONB,
"responseJson" JSONB,
"approvedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ModulePlanPurchase_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ModulePlanSubscription" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"planKey" "ModulePlanKey" NOT NULL,
"sourcePurchaseId" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"startsAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ModulePlanSubscription_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ModulePlanPurchase_externalReference_key" ON "ModulePlanPurchase"("externalReference");
-- CreateIndex
CREATE INDEX "ModulePlanPurchase_userId_planKey_createdAt_idx" ON "ModulePlanPurchase"("userId", "planKey", "createdAt");
-- CreateIndex
CREATE INDEX "ModulePlanPurchase_status_updatedAt_idx" ON "ModulePlanPurchase"("status", "updatedAt");
-- CreateIndex
CREATE INDEX "ModulePlanPurchase_mercadoPaymentId_idx" ON "ModulePlanPurchase"("mercadoPaymentId");
-- CreateIndex
CREATE UNIQUE INDEX "ModulePlanSubscription_userId_planKey_key" ON "ModulePlanSubscription"("userId", "planKey");
-- CreateIndex
CREATE INDEX "ModulePlanSubscription_userId_isActive_expiresAt_idx" ON "ModulePlanSubscription"("userId", "isActive", "expiresAt");
-- AddForeignKey
ALTER TABLE "ModulePlanPurchase" ADD CONSTRAINT "ModulePlanPurchase_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ModulePlanSubscription" ADD CONSTRAINT "ModulePlanSubscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ModulePlanSubscription" ADD CONSTRAINT "ModulePlanSubscription_sourcePurchaseId_fkey" FOREIGN KEY ("sourcePurchaseId") REFERENCES "ModulePlanPurchase"("id") ON DELETE SET NULL ON UPDATE CASCADE;

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])
}

View File

@@ -1,4 +1,4 @@
import { PrismaClient, ContentPageType, OverallScoreMethod, PriorityLevel } from "@prisma/client";
import { PrismaClient, ContentPageType, LegalJurisdictionLevel, OverallScoreMethod, PriorityLevel } from "@prisma/client";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -389,6 +389,99 @@ const contentPageSeeds = [
},
];
const legalDirectorySeeds = [
{
jurisdictionLevel: LegalJurisdictionLevel.FEDERAL,
name: "Secretaria de la Funcion Publica (SFP)",
scopeTagsJson: ["inconformidades", "sanciones", "organo-interno-control"],
websiteUrl: "https://www.gob.mx/sfp",
phone: null,
email: null,
stateCode: null,
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.FEDERAL,
name: "Tribunal Federal de Justicia Administrativa (TFJA)",
scopeTagsJson: ["juicio-contencioso", "nulidad", "sanciones-administrativas"],
websiteUrl: "https://www.tfja.gob.mx",
phone: null,
email: null,
stateCode: null,
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.FEDERAL,
name: "Auditoria Superior de la Federacion (ASF)",
scopeTagsJson: ["auditoria", "fiscalizacion", "cuenta-publica"],
websiteUrl: "https://www.asf.gob.mx",
phone: null,
email: null,
stateCode: null,
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.FEDERAL,
name: "CompraNet",
scopeTagsJson: ["contrataciones", "licitaciones", "expedientes"],
websiteUrl: "https://compranet.hacienda.gob.mx",
phone: null,
email: null,
stateCode: null,
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.FEDERAL,
name: "Sistema Nacional Anticorrupcion",
scopeTagsJson: ["anticorrupcion", "denuncias", "integridad"],
websiteUrl: "https://www.sna.org.mx",
phone: null,
email: null,
stateCode: null,
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.STATE,
name: "Secretaria de la Contraloria y Transparencia Gubernamental de Nuevo Leon",
scopeTagsJson: ["inconformidades", "contraloria", "responsabilidades"],
websiteUrl: "https://www.nl.gob.mx/dependencias/contraloria",
phone: null,
email: null,
stateCode: "NL",
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.STATE,
name: "Tribunal de Justicia Administrativa de Nuevo Leon",
scopeTagsJson: ["juicio-administrativo", "sanciones", "nulidad"],
websiteUrl: "https://tjanl.gob.mx",
phone: null,
email: null,
stateCode: "NL",
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.STATE,
name: "Auditoria Superior del Estado de Nuevo Leon",
scopeTagsJson: ["auditoria", "fiscalizacion", "cuenta-publica-estatal"],
websiteUrl: "https://www.asenl.gob.mx",
phone: null,
email: null,
stateCode: "NL",
municipalityCode: null,
},
{
jurisdictionLevel: LegalJurisdictionLevel.STATE,
name: "Sistema Estatal Anticorrupcion de Nuevo Leon",
scopeTagsJson: ["anticorrupcion", "politica-estatal", "coordinacion"],
websiteUrl: "https://www.seanl.mx",
phone: null,
email: null,
stateCode: "NL",
municipalityCode: null,
},
];
async function upsertDiagnosticStructure() {
const moduleKeys = moduleSeeds.map((moduleSeed) => moduleSeed.key);
@@ -605,6 +698,48 @@ async function upsertDefaultScoringConfig() {
});
}
async function upsertLegalDirectory() {
for (const item of legalDirectorySeeds) {
const existing = await prisma.legalDirectoryEntity.findFirst({
where: {
jurisdictionLevel: item.jurisdictionLevel,
name: item.name,
stateCode: item.stateCode,
municipalityCode: item.municipalityCode,
},
select: { id: true },
});
if (existing) {
await prisma.legalDirectoryEntity.update({
where: { id: existing.id },
data: {
scopeTagsJson: item.scopeTagsJson,
websiteUrl: item.websiteUrl,
phone: item.phone,
email: item.email,
isActive: true,
},
});
continue;
}
await prisma.legalDirectoryEntity.create({
data: {
jurisdictionLevel: item.jurisdictionLevel,
name: item.name,
scopeTagsJson: item.scopeTagsJson,
websiteUrl: item.websiteUrl,
phone: item.phone,
email: item.email,
stateCode: item.stateCode,
municipalityCode: item.municipalityCode,
isActive: true,
},
});
}
}
async function loadMunicipalitySeeds() {
const filePath = path.join(__dirname, "data", "municipalities.json");
const content = await readFile(filePath, "utf-8");
@@ -697,6 +832,7 @@ async function main() {
await upsertRecommendations();
await upsertContentPages();
await upsertDefaultScoringConfig();
await upsertLegalDirectory();
const municipalitySeedCount = await upsertMunicipalities();
const moduleCount = await prisma.diagnosticModule.count();
@@ -705,6 +841,7 @@ async function main() {
const workshopCount = await prisma.developmentWorkshop.count();
const recommendationCount = await prisma.recommendation.count();
const contentPageCount = await prisma.contentPage.count();
const legalDirectoryCount = await prisma.legalDirectoryEntity.count({ where: { isActive: true } });
const municipalityCount = await prisma.municipality.count({ where: { isActive: true } });
console.log("Seed completed", {
@@ -714,6 +851,7 @@ async function main() {
workshops: workshopCount,
recommendations: recommendationCount,
contentPages: contentPageCount,
legalDirectory: legalDirectoryCount,
municipalities: municipalityCount,
municipalitySeedsProcessed: municipalitySeedCount,
});