chore: add Coolify deployment scaffolding (Dockerfiles, prod compose, git hygiene)
- apps/api/Dockerfile: build NestJS, run prisma migrate deploy on start - apps/web/Dockerfile + nginx.conf: build Vite, serve static, proxy /api -> api - docker-compose.coolify.yml: full prod stack (postgres, redis, minio, keycloak, api, web) - .dockerignore / .gitignore / .gitattributes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,578 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Sex" AS ENUM ('F', 'M');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "MaritalStatus" AS ENUM ('casatorit', 'necasatorit', 'divortat', 'vaduv');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EmployeeStatus" AS ENUM ('activ', 'concediat', 'suspendat');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DocumentType" AS ENUM ('buletin_de_identitate', 'pasaport');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "FamilyMemberType" AS ENUM ('contact_principal', 'sot', 'sotie', 'mama', 'tata', 'copil');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "StudyType" AS ENUM ('superioare', 'medii_de_specialitate', 'secundare_tehnice', 'medii');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "StudyLevel" AS ENUM ('de_baza', 'postuniversitar');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PostUniversityType" AS ENUM ('masterat', 'rezidentiat', 'secundariat', 'altele');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DiplomaStatus" AS ENUM ('confirmata', 'neconfirmata');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "QualificationCategory" AS ENUM ('fara', 'cat_II', 'cat_I', 'superioara');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ScientificTitle" AS ENUM ('doctor', 'doctor_habilitat');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TrainingType" AS ENUM ('orientare', 'intern', 'extern_RM', 'extern_international');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DisciplinarySanctionType" AS ENUM ('avertisment', 'mustrare', 'mustrare_aspra');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ContractPeriod" AS ENUM ('determinata', 'nedeterminata', 'replasare_temporara');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ContractCategory" AS ENUM ('principal', 'secundar');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ContractType" AS ENUM ('de_baza', 'cumul');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SalaryType" AS ENUM ('fix', 'pe_ore', 'in_acord');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CampaignStatus" AS ENUM ('draft', 'scheduled', 'in_progress', 'closed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EvaluationScore" AS ENUM ('slab', 'mediu', 'bine');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ProposedCategory" AS ENUM ('fara', 'cat_II', 'cat_I', 'superioara');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "MedicalCheckupType" AS ENUM ('la_angajare', 'periodic', 'la_reluarea_activitatii', 'la_incetarea_expunerii', 'suplimentar');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "MedicalVerdict" AS ENUM ('apt', 'apt_perioada_adaptare', 'apt_conditionat', 'inapt_temporar', 'inapt');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "disability_grades" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "disability_grades_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tax_exemptions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "tax_exemptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "work_schedules" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"daysWork" INTEGER NOT NULL,
|
||||
"daysRest" INTEGER NOT NULL,
|
||||
"hoursPerDay" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "work_schedules_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "departments" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"code" TEXT,
|
||||
"parentId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "departments_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "employees" (
|
||||
"id" TEXT NOT NULL,
|
||||
"idnp" VARCHAR(13) NOT NULL,
|
||||
"nume" TEXT NOT NULL,
|
||||
"prenume" TEXT NOT NULL,
|
||||
"patronimic" TEXT,
|
||||
"numeAnterior" TEXT,
|
||||
"dataNasterii" DATE NOT NULL,
|
||||
"domiciliu" TEXT NOT NULL,
|
||||
"adresaReala" TEXT,
|
||||
"telefonPersonal" TEXT NOT NULL,
|
||||
"telefonServiciu" TEXT,
|
||||
"emailPersonal" TEXT,
|
||||
"emailCorporativ" TEXT,
|
||||
"sex" "Sex" NOT NULL,
|
||||
"codCpas" TEXT,
|
||||
"stareCivila" "MaritalStatus",
|
||||
"titluStiintific" "ScientificTitle",
|
||||
"titluUniversitar" TEXT,
|
||||
"status" "EmployeeStatus" NOT NULL DEFAULT 'activ',
|
||||
"gradDizabilitateId" TEXT,
|
||||
"recomandareInternaId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "employees_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "identity_documents" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"tipAct" "DocumentType" NOT NULL,
|
||||
"seria" TEXT,
|
||||
"nr" TEXT NOT NULL,
|
||||
"dataEmiterii" DATE NOT NULL,
|
||||
"autoritateEmitenta" TEXT NOT NULL,
|
||||
"dataExpirarii" DATE NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "identity_documents_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "family_members" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"tip" "FamilyMemberType" NOT NULL,
|
||||
"numePrenume" TEXT NOT NULL,
|
||||
"dataNasterii" DATE,
|
||||
"idnp" VARCHAR(13),
|
||||
"telefon" TEXT,
|
||||
"tipScutireId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "family_members_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "educations" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"tipStudii" "StudyType" NOT NULL,
|
||||
"institutia" TEXT NOT NULL,
|
||||
"specialitatea" TEXT NOT NULL,
|
||||
"dataAbsolvirii" DATE,
|
||||
"nrSeriaDiploma" TEXT,
|
||||
"dataEmiterii" DATE,
|
||||
"nrInregistrare" TEXT,
|
||||
"confirmare" "DiplomaStatus",
|
||||
"nivel" "StudyLevel",
|
||||
"tipPostuniversitar" "PostUniversityType",
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "educations_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "qualifications" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"categorie" "QualificationCategory" NOT NULL,
|
||||
"dataObtinerii" DATE,
|
||||
"dataUltimeiConfirmari" DATE,
|
||||
"dataExpirarii" DATE,
|
||||
"specialitate" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "qualifications_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "trainings" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"denumire" TEXT NOT NULL,
|
||||
"inceput" DATE NOT NULL,
|
||||
"sfirsit" DATE,
|
||||
"tip" "TrainingType" NOT NULL,
|
||||
"tara" TEXT,
|
||||
"nrOre" INTEGER,
|
||||
"organizatia" TEXT,
|
||||
"certificat" BOOLEAN NOT NULL DEFAULT false,
|
||||
"cost" DECIMAL(10,2),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "trainings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "disciplinary_sanctions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"tip" "DisciplinarySanctionType" NOT NULL,
|
||||
"dataAplicarii" DATE NOT NULL,
|
||||
"dataExpirarii" DATE NOT NULL,
|
||||
"isStinsa" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "disciplinary_sanctions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "benefits" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"uniformaId" TEXT,
|
||||
"halatId" TEXT,
|
||||
"ciupiciId" TEXT,
|
||||
"vestaId" TEXT,
|
||||
"ticheteMasa" BOOLEAN NOT NULL DEFAULT false,
|
||||
"valoareTichet" DECIMAL(10,2),
|
||||
"alimentatiePersonal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"abonamentTel" DECIMAL(10,2),
|
||||
"aparatTelefonId" TEXT,
|
||||
"cardCompanie" TEXT,
|
||||
"automobilServiciu" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "benefits_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "employment_contracts" (
|
||||
"id" TEXT NOT NULL,
|
||||
"nrCim" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"categorie" "ContractCategory" NOT NULL,
|
||||
"dataSemnarii" DATE NOT NULL,
|
||||
"dataAngajarii" DATE NOT NULL,
|
||||
"dataDemisiei" DATE,
|
||||
"perioada" "ContractPeriod" NOT NULL,
|
||||
"dataTerminarii" DATE,
|
||||
"functiaClasificator" TEXT,
|
||||
"codFunctie" TEXT,
|
||||
"functiaOrganigrama" TEXT,
|
||||
"tipCim" "ContractType" NOT NULL,
|
||||
"departmentId" TEXT NOT NULL,
|
||||
"regimMunca" TEXT,
|
||||
"tipSalarizare" "SalaryType",
|
||||
"salarizareDetails" JSONB,
|
||||
"clausaAditionala" JSONB,
|
||||
"workScheduleId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "employment_contracts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "cim_service_categories" (
|
||||
"id" TEXT NOT NULL,
|
||||
"contractId" TEXT NOT NULL,
|
||||
"categorieId" TEXT NOT NULL,
|
||||
"tipRemunerare" TEXT NOT NULL,
|
||||
"sumaNeta" DECIMAL(10,2),
|
||||
"procent" DECIMAL(5,2),
|
||||
|
||||
CONSTRAINT "cim_service_categories_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "evaluation_campaigns" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"departmentId" TEXT NOT NULL,
|
||||
"month" DATE NOT NULL,
|
||||
"status" "CampaignStatus" NOT NULL DEFAULT 'draft',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "evaluation_campaigns_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "evaluation_forms" (
|
||||
"id" TEXT NOT NULL,
|
||||
"campaignId" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"abilitatiClinice" "EvaluationScore",
|
||||
"judecataClinica" "EvaluationScore",
|
||||
"manopere" "EvaluationScore",
|
||||
"gestionareaSarcinilor" "EvaluationScore",
|
||||
"constiintaProfesionala" "EvaluationScore",
|
||||
"atitudineaPacienti" "EvaluationScore",
|
||||
"atitudineaColegi" "EvaluationScore",
|
||||
"atitudineaPersonalNonMed" "EvaluationScore",
|
||||
"utilizareSmartphone" "EvaluationScore",
|
||||
"respectareaProgramului" "EvaluationScore",
|
||||
"respectareaDressCode" "EvaluationScore",
|
||||
"testJci" JSONB,
|
||||
"completareaDocMed" BOOLEAN,
|
||||
"perfectioneazaCunostinte" BOOLEAN,
|
||||
"membruComitetCalitate" BOOLEAN,
|
||||
"functieDeMonitor" BOOLEAN,
|
||||
"inlocuiesteSuperiorul" BOOLEAN,
|
||||
"categorieCalculata" "ProposedCategory",
|
||||
"categorieAprobata" "ProposedCategory",
|
||||
"observatii" TEXT,
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "evaluation_forms_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "workplace_risk_cards" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"riskFactors" JSONB NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "workplace_risk_cards_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "employee_medical_profiles" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"ocupatieCorm" TEXT,
|
||||
"workplaceRiskCardId" TEXT,
|
||||
"dataUltimControlMedical" DATE,
|
||||
"expusRadiatiiIonizante" BOOLEAN NOT NULL DEFAULT false,
|
||||
"dataIntrarii" DATE,
|
||||
"expunereAnterioaraPerioda" TEXT,
|
||||
"expunereAnterioaraAni" INTEGER,
|
||||
"dozaCumulataExternaMsv" DECIMAL(10,4),
|
||||
"dozaCumulataInternaMsv" DECIMAL(10,4),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "employee_medical_profiles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "medical_checkups" (
|
||||
"id" TEXT NOT NULL,
|
||||
"employeeId" TEXT NOT NULL,
|
||||
"tip" "MedicalCheckupType" NOT NULL,
|
||||
"dataPlanificata" DATE NOT NULL,
|
||||
"dataEfectuata" DATE,
|
||||
"verdict" "MedicalVerdict",
|
||||
"recomandari" TEXT,
|
||||
"documenteGenerate" JSONB,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "medical_checkups_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "audit_logs" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"ts" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"userId" TEXT NOT NULL,
|
||||
"userRole" VARCHAR(50) NOT NULL,
|
||||
"ip" VARCHAR(45),
|
||||
"action" VARCHAR(20) NOT NULL,
|
||||
"entity" VARCHAR(50) NOT NULL,
|
||||
"entityId" VARCHAR(50) NOT NULL,
|
||||
"field" VARCHAR(100),
|
||||
"oldValue" TEXT,
|
||||
"newValue" TEXT,
|
||||
"reason" TEXT,
|
||||
|
||||
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "disability_grades_code_key" ON "disability_grades"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tax_exemptions_code_key" ON "tax_exemptions"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "work_schedules_name_key" ON "work_schedules"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "departments_code_key" ON "departments"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "departments_parentId_idx" ON "departments"("parentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "employees_idnp_key" ON "employees"("idnp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employees_idnp_idx" ON "employees"("idnp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employees_nume_prenume_idx" ON "employees"("nume", "prenume");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employees_status_idx" ON "employees"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employees_dataNasterii_idx" ON "employees"("dataNasterii");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "identity_documents_employeeId_idx" ON "identity_documents"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "identity_documents_dataExpirarii_idx" ON "identity_documents"("dataExpirarii");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "family_members_employeeId_idx" ON "family_members"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "educations_employeeId_idx" ON "educations"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "qualifications_employeeId_idx" ON "qualifications"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "qualifications_dataExpirarii_idx" ON "qualifications"("dataExpirarii");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "trainings_employeeId_idx" ON "trainings"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "disciplinary_sanctions_employeeId_idx" ON "disciplinary_sanctions"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "disciplinary_sanctions_dataExpirarii_idx" ON "disciplinary_sanctions"("dataExpirarii");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "benefits_employeeId_key" ON "benefits"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "employment_contracts_nrCim_key" ON "employment_contracts"("nrCim");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employment_contracts_employeeId_idx" ON "employment_contracts"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employment_contracts_departmentId_idx" ON "employment_contracts"("departmentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "employment_contracts_dataDemisiei_idx" ON "employment_contracts"("dataDemisiei");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "cim_service_categories_contractId_idx" ON "cim_service_categories"("contractId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "evaluation_campaigns_departmentId_idx" ON "evaluation_campaigns"("departmentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "evaluation_campaigns_month_idx" ON "evaluation_campaigns"("month");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "evaluation_forms_campaignId_idx" ON "evaluation_forms"("campaignId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "evaluation_forms_employeeId_idx" ON "evaluation_forms"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "evaluation_forms_campaignId_employeeId_key" ON "evaluation_forms"("campaignId", "employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "workplace_risk_cards_name_key" ON "workplace_risk_cards"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "employee_medical_profiles_employeeId_key" ON "employee_medical_profiles"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "medical_checkups_employeeId_idx" ON "medical_checkups"("employeeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "medical_checkups_dataPlanificata_idx" ON "medical_checkups"("dataPlanificata");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_userId_idx" ON "audit_logs"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_entity_entityId_idx" ON "audit_logs"("entity", "entityId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_ts_idx" ON "audit_logs"("ts");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "departments" ADD CONSTRAINT "departments_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "departments"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employees" ADD CONSTRAINT "employees_gradDizabilitateId_fkey" FOREIGN KEY ("gradDizabilitateId") REFERENCES "disability_grades"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employees" ADD CONSTRAINT "employees_recomandareInternaId_fkey" FOREIGN KEY ("recomandareInternaId") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "identity_documents" ADD CONSTRAINT "identity_documents_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "family_members" ADD CONSTRAINT "family_members_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "family_members" ADD CONSTRAINT "family_members_tipScutireId_fkey" FOREIGN KEY ("tipScutireId") REFERENCES "tax_exemptions"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "educations" ADD CONSTRAINT "educations_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "qualifications" ADD CONSTRAINT "qualifications_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "trainings" ADD CONSTRAINT "trainings_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "disciplinary_sanctions" ADD CONSTRAINT "disciplinary_sanctions_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employment_contracts" ADD CONSTRAINT "employment_contracts_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employment_contracts" ADD CONSTRAINT "employment_contracts_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "departments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employment_contracts" ADD CONSTRAINT "employment_contracts_workScheduleId_fkey" FOREIGN KEY ("workScheduleId") REFERENCES "work_schedules"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "cim_service_categories" ADD CONSTRAINT "cim_service_categories_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "employment_contracts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "evaluation_campaigns" ADD CONSTRAINT "evaluation_campaigns_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "departments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "evaluation_forms" ADD CONSTRAINT "evaluation_forms_campaignId_fkey" FOREIGN KEY ("campaignId") REFERENCES "evaluation_campaigns"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "evaluation_forms" ADD CONSTRAINT "evaluation_forms_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employee_medical_profiles" ADD CONSTRAINT "employee_medical_profiles_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "employee_medical_profiles" ADD CONSTRAINT "employee_medical_profiles_workplaceRiskCardId_fkey" FOREIGN KEY ("workplaceRiskCardId") REFERENCES "workplace_risk_cards"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "medical_checkups" ADD CONSTRAINT "medical_checkups_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,35 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AnexaType" AS ENUM ('ANEXA_3', 'ANEXA_4', 'ANEXA_4B', 'ANEXA_6');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "anexa_templates" (
|
||||
"id" TEXT NOT NULL,
|
||||
"type" "AnexaType" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"contentJson" JSONB NOT NULL,
|
||||
"updatedById" TEXT NOT NULL,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "anexa_templates_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "anexa_template_versions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
"contentJson" JSONB NOT NULL,
|
||||
"savedById" TEXT NOT NULL,
|
||||
"savedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"label" TEXT,
|
||||
|
||||
CONSTRAINT "anexa_template_versions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "anexa_templates_type_key" ON "anexa_templates"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "anexa_template_versions_templateId_idx" ON "anexa_template_versions"("templateId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "anexa_template_versions" ADD CONSTRAINT "anexa_template_versions_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "anexa_templates"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,47 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InventoryItemType" AS ENUM ('uniforma', 'halat', 'ciupici', 'vesta', 'aparat_telefon', 'alte');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "inventory_items" (
|
||||
"id" TEXT NOT NULL,
|
||||
"sku" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "InventoryItemType" NOT NULL,
|
||||
"size" TEXT,
|
||||
"color" TEXT,
|
||||
"pricePerUnit" DECIMAL(10,2),
|
||||
"stockQty" INTEGER NOT NULL DEFAULT 0,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "inventory_items_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "inventory_items_sku_key" ON "inventory_items"("sku");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "inventory_items_type_active_idx" ON "inventory_items"("type", "active");
|
||||
|
||||
-- Reset existing free-text IDs in benefits (no FK referenced anything real before)
|
||||
UPDATE "benefits" SET "uniformaId" = NULL WHERE "uniformaId" IS NOT NULL;
|
||||
UPDATE "benefits" SET "halatId" = NULL WHERE "halatId" IS NOT NULL;
|
||||
UPDATE "benefits" SET "ciupiciId" = NULL WHERE "ciupiciId" IS NOT NULL;
|
||||
UPDATE "benefits" SET "vestaId" = NULL WHERE "vestaId" IS NOT NULL;
|
||||
UPDATE "benefits" SET "aparatTelefonId" = NULL WHERE "aparatTelefonId" IS NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_uniformaId_fkey" FOREIGN KEY ("uniformaId") REFERENCES "inventory_items"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_halatId_fkey" FOREIGN KEY ("halatId") REFERENCES "inventory_items"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_ciupiciId_fkey" FOREIGN KEY ("ciupiciId") REFERENCES "inventory_items"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_vestaId_fkey" FOREIGN KEY ("vestaId") REFERENCES "inventory_items"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "benefits" ADD CONSTRAINT "benefits_aparatTelefonId_fkey" FOREIGN KEY ("aparatTelefonId") REFERENCES "inventory_items"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,52 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RiskExposureType" AS ENUM ('AGENT_CHIMIC', 'PULBERI', 'AGENT_BIOLOGIC', 'ZGOMOT', 'VIBRATII', 'CAMP_ELECTROMAGNETIC', 'RADIATII_OPTICE');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "workplace_risk_cards" ADD COLUMN "adresaFiliala" TEXT,
|
||||
ADD COLUMN "anexeIgienicoSanitare" JSONB,
|
||||
ADD COLUMN "caemDiviziune" TEXT,
|
||||
ADD COLUMN "caemPrimeleDouaCifre" TEXT,
|
||||
ADD COLUMN "clasaConditiilorDeMunca" TEXT,
|
||||
ADD COLUMN "cormSubgrupaMajora" TEXT,
|
||||
ADD COLUMN "directiaSectiaSectorul" TEXT,
|
||||
ADD COLUMN "echipamentLucru" TEXT,
|
||||
ADD COLUMN "evaluareDetalii" JSONB,
|
||||
ADD COLUMN "filiala" TEXT,
|
||||
ADD COLUMN "mijloaceProtectieColectiva" TEXT,
|
||||
ADD COLUMN "mijloaceProtectieIndividuala" TEXT,
|
||||
ADD COLUMN "numarLucratoriPosibili" INTEGER,
|
||||
ADD COLUMN "numarulLoculuiDeMunca" TEXT,
|
||||
ADD COLUMN "observatii" TEXT,
|
||||
ADD COLUMN "radiatiiAparatura" TEXT,
|
||||
ADD COLUMN "radiatiiGrupa" TEXT,
|
||||
ADD COLUMN "radiatiiIonizante" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "radiatiiMasuriProtectie" TEXT,
|
||||
ADD COLUMN "radiatiiSurse" TEXT,
|
||||
ADD COLUMN "radiatiiTipExpunere" TEXT,
|
||||
ALTER COLUMN "riskFactors" DROP NOT NULL;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "workplace_risk_exposures" (
|
||||
"id" TEXT NOT NULL,
|
||||
"cardId" TEXT NOT NULL,
|
||||
"tip" "RiskExposureType" NOT NULL,
|
||||
"denumire" TEXT NOT NULL,
|
||||
"cas" TEXT,
|
||||
"einecs" TEXT,
|
||||
"clasificare" TEXT,
|
||||
"zonaAfectata" TEXT,
|
||||
"timpExpunere" TEXT,
|
||||
"vep" TEXT,
|
||||
"vlep" TEXT,
|
||||
"caracteristici" TEXT,
|
||||
"procesVerbal" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "workplace_risk_exposures_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "workplace_risk_exposures_cardId_idx" ON "workplace_risk_exposures"("cardId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "workplace_risk_exposures" ADD CONSTRAINT "workplace_risk_exposures_cardId_fkey" FOREIGN KEY ("cardId") REFERENCES "workplace_risk_cards"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "AnexaType" ADD VALUE 'ANEXA_4A';
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OverexposureKind" AS ENUM ('EXCEPTIONALA', 'ACCIDENTALA');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "workplace_risk_cards" ADD COLUMN "tipFisa" TEXT NOT NULL DEFAULT 'STANDARD';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "radiation_overexposures" (
|
||||
"id" TEXT NOT NULL,
|
||||
"medicalProfileId" TEXT NOT NULL,
|
||||
"fel" "OverexposureKind" NOT NULL,
|
||||
"tipExpunere" TEXT,
|
||||
"data" DATE,
|
||||
"dozaMsv" DECIMAL(10,4),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "radiation_overexposures_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "radiation_overexposures_medicalProfileId_idx" ON "radiation_overexposures"("medicalProfileId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "radiation_overexposures" ADD CONSTRAINT "radiation_overexposures_medicalProfileId_fkey" FOREIGN KEY ("medicalProfileId") REFERENCES "employee_medical_profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE "workplace_risk_cards"
|
||||
ADD COLUMN "telefonFiliala" TEXT;
|
||||
|
||||
ALTER TABLE "medical_checkups"
|
||||
ADD COLUMN "valabilPanaLa" DATE,
|
||||
ADD COLUMN "semnatDe" TEXT;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,829 @@
|
||||
// HRM Medpark — Prisma Schema
|
||||
// Phase 1: Employee Master Data + Department + AuditLog
|
||||
// Phase 2 stubs: EmploymentContract
|
||||
// Phase 4 stubs: EvaluationCampaign, EvaluationForm
|
||||
// Phase 5 stubs: WorkplaceRiskCard, EmployeeMedicalProfile, MedicalCheckup
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// ENUMS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
enum Sex {
|
||||
F
|
||||
M
|
||||
}
|
||||
|
||||
enum MaritalStatus {
|
||||
casatorit
|
||||
necasatorit
|
||||
divortat
|
||||
vaduv
|
||||
}
|
||||
|
||||
enum EmployeeStatus {
|
||||
activ
|
||||
concediat
|
||||
suspendat
|
||||
}
|
||||
|
||||
enum DocumentType {
|
||||
buletin_de_identitate
|
||||
pasaport
|
||||
}
|
||||
|
||||
enum FamilyMemberType {
|
||||
contact_principal
|
||||
sot
|
||||
sotie
|
||||
mama
|
||||
tata
|
||||
copil
|
||||
}
|
||||
|
||||
enum StudyType {
|
||||
superioare
|
||||
medii_de_specialitate
|
||||
secundare_tehnice
|
||||
medii
|
||||
}
|
||||
|
||||
enum StudyLevel {
|
||||
de_baza
|
||||
postuniversitar
|
||||
}
|
||||
|
||||
enum PostUniversityType {
|
||||
masterat
|
||||
rezidentiat
|
||||
secundariat
|
||||
altele
|
||||
}
|
||||
|
||||
enum DiplomaStatus {
|
||||
confirmata
|
||||
neconfirmata
|
||||
}
|
||||
|
||||
enum QualificationCategory {
|
||||
fara
|
||||
cat_II
|
||||
cat_I
|
||||
superioara
|
||||
}
|
||||
|
||||
enum ScientificTitle {
|
||||
doctor
|
||||
doctor_habilitat
|
||||
}
|
||||
|
||||
enum TrainingType {
|
||||
orientare
|
||||
intern
|
||||
extern_RM
|
||||
extern_international
|
||||
}
|
||||
|
||||
enum DisciplinarySanctionType {
|
||||
avertisment
|
||||
mustrare
|
||||
mustrare_aspra
|
||||
}
|
||||
|
||||
// Phase 2
|
||||
enum ContractPeriod {
|
||||
determinata
|
||||
nedeterminata
|
||||
replasare_temporara
|
||||
}
|
||||
|
||||
enum ContractCategory {
|
||||
principal
|
||||
secundar
|
||||
}
|
||||
|
||||
enum ContractType {
|
||||
de_baza
|
||||
cumul
|
||||
}
|
||||
|
||||
enum SalaryType {
|
||||
fix
|
||||
pe_ore
|
||||
in_acord
|
||||
}
|
||||
|
||||
// Phase 4
|
||||
enum CampaignStatus {
|
||||
draft
|
||||
scheduled
|
||||
in_progress
|
||||
closed
|
||||
}
|
||||
|
||||
enum EvaluationScore {
|
||||
slab
|
||||
mediu
|
||||
bine
|
||||
}
|
||||
|
||||
enum ProposedCategory {
|
||||
fara
|
||||
cat_II
|
||||
cat_I
|
||||
superioara
|
||||
}
|
||||
|
||||
// Phase 5
|
||||
enum MedicalCheckupType {
|
||||
la_angajare
|
||||
periodic
|
||||
la_reluarea_activitatii
|
||||
la_incetarea_expunerii
|
||||
suplimentar
|
||||
}
|
||||
|
||||
enum MedicalVerdict {
|
||||
apt
|
||||
apt_perioada_adaptare
|
||||
apt_conditionat
|
||||
inapt_temporar
|
||||
inapt
|
||||
}
|
||||
|
||||
enum AnexaType {
|
||||
ANEXA_3
|
||||
ANEXA_4
|
||||
ANEXA_4A
|
||||
ANEXA_4B
|
||||
ANEXA_6
|
||||
}
|
||||
|
||||
// Tipuri de factori cu tabel de expunere în Anexa 4 (NU-10-MS-2026)
|
||||
enum RiskExposureType {
|
||||
AGENT_CHIMIC
|
||||
PULBERI
|
||||
AGENT_BIOLOGIC
|
||||
ZGOMOT
|
||||
VIBRATII
|
||||
CAMP_ELECTROMAGNETIC
|
||||
RADIATII_OPTICE
|
||||
}
|
||||
|
||||
// Tipul supraexpunerii la radiații ionizante (Anexa 4B)
|
||||
enum OverexposureKind {
|
||||
EXCEPTIONALA
|
||||
ACCIDENTALA
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// СПРАВОЧНИКИ
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model DisabilityGrade {
|
||||
id String @id @default(uuid())
|
||||
code String @unique
|
||||
name String
|
||||
employees Employee[]
|
||||
|
||||
@@map("disability_grades")
|
||||
}
|
||||
|
||||
model TaxExemption {
|
||||
id String @id @default(uuid())
|
||||
code String @unique
|
||||
description String
|
||||
familyMembers FamilyMember[]
|
||||
|
||||
@@map("tax_exemptions")
|
||||
}
|
||||
|
||||
model WorkSchedule {
|
||||
id String @id @default(uuid())
|
||||
name String @unique // "5/2 8h", "7/7 12h"
|
||||
daysWork Int
|
||||
daysRest Int
|
||||
hoursPerDay Int
|
||||
contracts EmploymentContract[]
|
||||
|
||||
@@map("work_schedules")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// DEPARTMENT — иерархия (adjacency list)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Department {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
code String? @unique
|
||||
parentId String?
|
||||
parent Department? @relation("DeptTree", fields: [parentId], references: [id])
|
||||
children Department[] @relation("DeptTree")
|
||||
|
||||
contracts EmploymentContract[]
|
||||
campaigns EvaluationCampaign[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([parentId])
|
||||
@@map("departments")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// EMPLOYEE — ядро системы
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Employee {
|
||||
id String @id @default(uuid())
|
||||
// IDNP — 13 цифр, алгоритм контрольной суммы валидируется на app-уровне
|
||||
idnp String @unique @db.VarChar(13)
|
||||
|
||||
// A. Личная информация
|
||||
nume String
|
||||
prenume String
|
||||
patronimic String?
|
||||
numeAnterior String?
|
||||
dataNasterii DateTime @db.Date
|
||||
domiciliu String
|
||||
adresaReala String?
|
||||
telefonPersonal String
|
||||
telefonServiciu String?
|
||||
emailPersonal String?
|
||||
emailCorporativ String?
|
||||
sex Sex
|
||||
codCpas String?
|
||||
stareCivila MaritalStatus?
|
||||
|
||||
// Научное/университетское звание (уровень Employee, не Qualification)
|
||||
titluStiintific ScientificTitle?
|
||||
titluUniversitar String?
|
||||
|
||||
status EmployeeStatus @default(activ)
|
||||
|
||||
gradDizabilitateId String?
|
||||
gradDizabilitate DisabilityGrade? @relation(fields: [gradDizabilitateId], references: [id])
|
||||
|
||||
// Кто рекомендовал (самоссылка)
|
||||
// Бизнес-правило: нельзя выбрать супруга текущего сотрудника — проверка на service-уровне
|
||||
recomandareInternaId String?
|
||||
recomandareInterna Employee? @relation("Recomandari", fields: [recomandareInternaId], references: [id])
|
||||
recomandat Employee[] @relation("Recomandari")
|
||||
|
||||
// Связанные сущности
|
||||
identityDocuments IdentityDocument[]
|
||||
familyMembers FamilyMember[]
|
||||
educations Education[]
|
||||
qualifications Qualification[]
|
||||
trainings Training[]
|
||||
disciplinarySanctions DisciplinarySanction[]
|
||||
contracts EmploymentContract[]
|
||||
benefit Benefit?
|
||||
evaluationForms EvaluationForm[]
|
||||
medicalProfile EmployeeMedicalProfile?
|
||||
medicalCheckups MedicalCheckup[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([idnp])
|
||||
@@index([nume, prenume])
|
||||
@@index([status])
|
||||
@@index([dataNasterii])
|
||||
@@map("employees")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// B. IDENTITY DOCUMENT
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model IdentityDocument {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
tipAct DocumentType
|
||||
seria String?
|
||||
nr String
|
||||
dataEmiterii DateTime @db.Date
|
||||
autoritateEmitenta String
|
||||
dataExpirarii DateTime @db.Date
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Cron-задача за 30 дней до dataExpirarii → HR Inbox
|
||||
@@index([employeeId])
|
||||
@@index([dataExpirarii])
|
||||
@@map("identity_documents")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// C. FAMILY MEMBERS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model FamilyMember {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
tip FamilyMemberType
|
||||
numePrenume String
|
||||
dataNasterii DateTime? @db.Date
|
||||
idnp String? @db.VarChar(13)
|
||||
telefon String? // обязателен для contact_principal — проверка на service-уровне
|
||||
|
||||
// Скидки FISC (только для copil)
|
||||
tipScutireId String?
|
||||
tipScutire TaxExemption? @relation(fields: [tipScutireId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([employeeId])
|
||||
@@map("family_members")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// D. EDUCATION
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Education {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
tipStudii StudyType
|
||||
institutia String
|
||||
specialitatea String
|
||||
dataAbsolvirii DateTime? @db.Date
|
||||
nrSeriaDiploma String?
|
||||
dataEmiterii DateTime? @db.Date
|
||||
nrInregistrare String?
|
||||
confirmare DiplomaStatus?
|
||||
nivel StudyLevel?
|
||||
tipPostuniversitar PostUniversityType?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([employeeId])
|
||||
@@map("educations")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// E. QUALIFICATIONS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Qualification {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
categorie QualificationCategory
|
||||
dataObtinerii DateTime? @db.Date
|
||||
dataUltimeiConfirmari DateTime? @db.Date
|
||||
dataExpirarii DateTime? @db.Date
|
||||
specialitate String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Cron-задача за 90/30/7 дней до dataExpirarii → HR + manager
|
||||
@@index([employeeId])
|
||||
@@index([dataExpirarii])
|
||||
@@map("qualifications")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// F. TRAINING
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Training {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
denumire String
|
||||
inceput DateTime @db.Date
|
||||
sfirsit DateTime? @db.Date
|
||||
tip TrainingType
|
||||
tara String?
|
||||
nrOre Int?
|
||||
organizatia String?
|
||||
certificat Boolean @default(false)
|
||||
cost Decimal? @db.Decimal(10, 2)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([employeeId])
|
||||
@@map("trainings")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// G. DISCIPLINARY SANCTIONS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model DisciplinarySanction {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
tip DisciplinarySanctionType
|
||||
dataAplicarii DateTime @db.Date
|
||||
// auto-calc: dataAplicarii + 6 months — вычисляется на service-уровне при создании
|
||||
dataExpirarii DateTime @db.Date
|
||||
// set true cron-ом после dataExpirarii; до этого — активна при расчёте performance
|
||||
isStinsa Boolean @default(false)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([employeeId])
|
||||
@@index([dataExpirarii])
|
||||
@@map("disciplinary_sanctions")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// H. BENEFITS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model Benefit {
|
||||
id String @id @default(uuid())
|
||||
employeeId String @unique
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
|
||||
uniformaId String?
|
||||
uniforma InventoryItem? @relation("BenefitUniforma", fields: [uniformaId], references: [id])
|
||||
halatId String?
|
||||
halat InventoryItem? @relation("BenefitHalat", fields: [halatId], references: [id])
|
||||
ciupiciId String?
|
||||
ciupici InventoryItem? @relation("BenefitCiupici", fields: [ciupiciId], references: [id])
|
||||
vestaId String?
|
||||
vesta InventoryItem? @relation("BenefitVesta", fields: [vestaId], references: [id])
|
||||
|
||||
ticheteMasa Boolean @default(false)
|
||||
valoareTichet Decimal? @db.Decimal(10, 2)
|
||||
alimentatiePersonal Boolean @default(false)
|
||||
abonamentTel Decimal? @db.Decimal(10, 2)
|
||||
aparatTelefonId String?
|
||||
aparatTelefon InventoryItem? @relation("BenefitAparatTel", fields: [aparatTelefonId], references: [id])
|
||||
cardCompanie String?
|
||||
automobilServiciu String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("benefits")
|
||||
}
|
||||
|
||||
enum InventoryItemType {
|
||||
uniforma
|
||||
halat
|
||||
ciupici
|
||||
vesta
|
||||
aparat_telefon
|
||||
alte
|
||||
}
|
||||
|
||||
model InventoryItem {
|
||||
id String @id @default(uuid())
|
||||
sku String @unique
|
||||
name String
|
||||
type InventoryItemType
|
||||
size String?
|
||||
color String?
|
||||
pricePerUnit Decimal? @db.Decimal(10, 2)
|
||||
stockQty Int @default(0)
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
uniformaBenefits Benefit[] @relation("BenefitUniforma")
|
||||
halatBenefits Benefit[] @relation("BenefitHalat")
|
||||
ciupiciBenefits Benefit[] @relation("BenefitCiupici")
|
||||
vestaBenefits Benefit[] @relation("BenefitVesta")
|
||||
aparatTelBenefits Benefit[] @relation("BenefitAparatTel")
|
||||
|
||||
@@index([type, active])
|
||||
@@map("inventory_items")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// PHASE 2 STUB: EMPLOYMENT CONTRACT
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model EmploymentContract {
|
||||
id String @id @default(uuid())
|
||||
nrCim String @unique
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id])
|
||||
categorie ContractCategory
|
||||
dataSemnarii DateTime @db.Date
|
||||
dataAngajarii DateTime @db.Date
|
||||
dataDemisiei DateTime? @db.Date
|
||||
perioada ContractPeriod
|
||||
dataTerminarii DateTime? @db.Date
|
||||
functiaClasificator String? // CORM код
|
||||
codFunctie String?
|
||||
functiaOrganigrama String?
|
||||
tipCim ContractType
|
||||
departmentId String
|
||||
department Department @relation(fields: [departmentId], references: [id])
|
||||
regimMunca String?
|
||||
tipSalarizare SalaryType?
|
||||
// Условные поля salariu_fix / pe_ore / in_acord хранятся как JSONB
|
||||
salarizareDetails Json?
|
||||
clausaAditionala Json?
|
||||
workScheduleId String?
|
||||
workSchedule WorkSchedule? @relation(fields: [workScheduleId], references: [id])
|
||||
|
||||
categoriiServicii CimServiceCategory[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Бизнес-правило: zile_concediu = MAX среди всех CIM сотрудника — проверка на service-уровне
|
||||
@@index([employeeId])
|
||||
@@index([departmentId])
|
||||
@@index([dataDemisiei])
|
||||
@@map("employment_contracts")
|
||||
}
|
||||
|
||||
model CimServiceCategory {
|
||||
id String @id @default(uuid())
|
||||
contractId String
|
||||
contract EmploymentContract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
||||
categorieId String
|
||||
tipRemunerare String // 'tarif' | 'procent'
|
||||
sumaNeta Decimal? @db.Decimal(10, 2)
|
||||
procent Decimal? @db.Decimal(5, 2)
|
||||
|
||||
@@index([contractId])
|
||||
@@map("cim_service_categories")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// PHASE 4 STUB: PERFORMANCE EVALUATION
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model EvaluationCampaign {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
departmentId String
|
||||
department Department @relation(fields: [departmentId], references: [id])
|
||||
month DateTime @db.Date // первый день месяца кампании
|
||||
status CampaignStatus @default(draft)
|
||||
forms EvaluationForm[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([departmentId])
|
||||
@@index([month])
|
||||
@@map("evaluation_campaigns")
|
||||
}
|
||||
|
||||
model EvaluationForm {
|
||||
id String @id @default(uuid())
|
||||
campaignId String
|
||||
campaign EvaluationCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id])
|
||||
|
||||
// A. Competente clinice (slab/mediu/bine)
|
||||
abilitatiClinice EvaluationScore?
|
||||
judecataClinica EvaluationScore?
|
||||
manopere EvaluationScore?
|
||||
gestionareaSarcinilor EvaluationScore?
|
||||
|
||||
// B. Comunicare si empatie
|
||||
constiintaProfesionala EvaluationScore?
|
||||
atitudineaPacienti EvaluationScore?
|
||||
atitudineaColegi EvaluationScore?
|
||||
atitudineaPersonalNonMed EvaluationScore?
|
||||
|
||||
// C. Disciplina
|
||||
utilizareSmartphone EvaluationScore?
|
||||
respectareaProgramului EvaluationScore?
|
||||
respectareaDressCode EvaluationScore?
|
||||
|
||||
// D. Documentatie si complianta
|
||||
testJci Json? // { score, max_score, percent, completed_at, source, external_id }
|
||||
completareaDocMed Boolean?
|
||||
perfectioneazaCunostinte Boolean?
|
||||
|
||||
// E. Candidat EXPERT (Da/Nu)
|
||||
membruComitetCalitate Boolean?
|
||||
functieDeMonitor Boolean?
|
||||
inlocuiesteSuperiorul Boolean?
|
||||
|
||||
// F. Verdict final
|
||||
categorieCalculata ProposedCategory?
|
||||
categorieAprobata ProposedCategory? // override de nursing_director
|
||||
observatii String?
|
||||
|
||||
completedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([campaignId, employeeId])
|
||||
@@index([campaignId])
|
||||
@@index([employeeId])
|
||||
@@map("evaluation_forms")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// PHASE 5 STUB: MEDICAL CONTROL
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model WorkplaceRiskCard {
|
||||
id String @id @default(uuid())
|
||||
name String @unique // "Medic profil chirurgical cu gărzi de noapte"
|
||||
riskFactors Json? // legacy: { chimici, fizici, biologici, ergonomici, psihosociali }
|
||||
profiles EmployeeMedicalProfile[]
|
||||
|
||||
// ── Anexa 4 — antet (Fișa de evaluare a riscurilor profesionale) ──
|
||||
filiala String?
|
||||
adresaFiliala String?
|
||||
telefonFiliala String?
|
||||
caemPrimeleDouaCifre String?
|
||||
cormSubgrupaMajora String?
|
||||
directiaSectiaSectorul String?
|
||||
numarulLoculuiDeMunca String?
|
||||
caemDiviziune String?
|
||||
clasaConditiilorDeMunca String?
|
||||
numarLucratoriPosibili Int?
|
||||
// STANDARD (Anexa 4) | DISTANTA_DIGITAL (Anexa 4A — muncă la distanță/platforme digitale)
|
||||
tipFisa String @default("STANDARD")
|
||||
|
||||
// ── Anexa 4 — bloc descriptiv (checkbox-uri / descrieri) ──
|
||||
evaluareDetalii Json?
|
||||
|
||||
// ── Anexa 4 — radiații ionizante (per loc de muncă) ──
|
||||
radiatiiIonizante Boolean @default(false)
|
||||
radiatiiGrupa String? // A | B
|
||||
radiatiiAparatura String?
|
||||
radiatiiSurse String? // inchise | deschise
|
||||
radiatiiTipExpunere String? // X externă | gamma externă | internă | externă și internă
|
||||
radiatiiMasuriProtectie String?
|
||||
|
||||
// ── Anexa 4 — subsol ──
|
||||
mijloaceProtectieColectiva String?
|
||||
mijloaceProtectieIndividuala String?
|
||||
echipamentLucru String?
|
||||
observatii String?
|
||||
anexeIgienicoSanitare Json? // { vestiar, chiuveta, wc, dus, salaMese, recreere }
|
||||
|
||||
exposures WorkplaceRiskExposure[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("workplace_risk_cards")
|
||||
}
|
||||
|
||||
// Rând din tabelele factoriale ale Anexei 4
|
||||
model WorkplaceRiskExposure {
|
||||
id String @id @default(uuid())
|
||||
cardId String
|
||||
card WorkplaceRiskCard @relation(fields: [cardId], references: [id], onDelete: Cascade)
|
||||
tip RiskExposureType
|
||||
denumire String
|
||||
cas String? // doar chimic / pulberi
|
||||
einecs String? // doar chimic / pulberi
|
||||
clasificare String? // doar agent biologic
|
||||
zonaAfectata String? // vibrații / câmp EM / radiații optice
|
||||
timpExpunere String?
|
||||
vep String? // valoarea de expunere profesională
|
||||
vlep String? // valoarea-limită de expunere profesională obligatorie
|
||||
caracteristici String?
|
||||
procesVerbal String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([cardId])
|
||||
@@map("workplace_risk_exposures")
|
||||
}
|
||||
|
||||
model EmployeeMedicalProfile {
|
||||
id String @id @default(uuid())
|
||||
employeeId String @unique
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
ocupatieCorm String?
|
||||
workplaceRiskCardId String?
|
||||
workplaceRiskCard WorkplaceRiskCard? @relation(fields: [workplaceRiskCardId], references: [id])
|
||||
dataUltimControlMedical DateTime? @db.Date
|
||||
|
||||
// Câmpuri radiații ionizante
|
||||
expusRadiatiiIonizante Boolean @default(false)
|
||||
dataIntrarii DateTime? @db.Date
|
||||
expunereAnterioaraPerioda String? // se completează o singură dată la angajare
|
||||
expunereAnterioaraAni Int?
|
||||
dozaCumulataExternaMsv Decimal? @db.Decimal(10, 4)
|
||||
dozaCumulataInternaMsv Decimal? @db.Decimal(10, 4)
|
||||
// dozaTotalaMsv = externa + interna — câmp calculat, nu stocat
|
||||
|
||||
// Supraexpuneri excepționale/accidentale (Anexa 4B)
|
||||
overexposures RadiationOverexposure[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("employee_medical_profiles")
|
||||
}
|
||||
|
||||
// Supraexpunere la radiații ionizante — rând din Anexa 4B (per lucrător)
|
||||
model RadiationOverexposure {
|
||||
id String @id @default(uuid())
|
||||
medicalProfileId String
|
||||
medicalProfile EmployeeMedicalProfile @relation(fields: [medicalProfileId], references: [id], onDelete: Cascade)
|
||||
fel OverexposureKind // EXCEPTIONALA | ACCIDENTALA
|
||||
tipExpunere String? // X externă | gamma externă | internă | externă și internă
|
||||
data DateTime? @db.Date
|
||||
dozaMsv Decimal? @db.Decimal(10, 4)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([medicalProfileId])
|
||||
@@map("radiation_overexposures")
|
||||
}
|
||||
|
||||
model MedicalCheckup {
|
||||
id String @id @default(uuid())
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id])
|
||||
tip MedicalCheckupType
|
||||
dataPlanificata DateTime @db.Date
|
||||
dataEfectuata DateTime? @db.Date
|
||||
verdict MedicalVerdict?
|
||||
recomandari String?
|
||||
valabilPanaLa DateTime? @db.Date
|
||||
semnatDe String?
|
||||
// Ссылки на S3-документы: [{ name, url, type }]
|
||||
documenteGenerate Json?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([employeeId])
|
||||
@@index([dataPlanificata])
|
||||
@@map("medical_checkups")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// AUDIT LOG — append-only, 5 ani retentie
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model AuditLog {
|
||||
id BigInt @id @default(autoincrement())
|
||||
ts DateTime @default(now())
|
||||
userId String
|
||||
userRole String @db.VarChar(50)
|
||||
ip String? @db.VarChar(45) // IPv4 или IPv6
|
||||
action String @db.VarChar(20) // READ | CREATE | UPDATE | DELETE | EXPORT
|
||||
entity String @db.VarChar(50)
|
||||
entityId String @db.VarChar(50)
|
||||
field String? @db.VarChar(100)
|
||||
// PII-значения шифруются на app-уровне (pgcrypto / KMS) перед записью
|
||||
oldValue String?
|
||||
newValue String?
|
||||
reason String? // обязателен для READ медицинских данных (GDPR)
|
||||
|
||||
@@index([userId])
|
||||
@@index([entity, entityId])
|
||||
@@index([ts])
|
||||
@@map("audit_logs")
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// ANEXA TEMPLATE EDITOR
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
model AnexaTemplate {
|
||||
id String @id @default(uuid())
|
||||
type AnexaType @unique
|
||||
name String
|
||||
contentJson Json
|
||||
updatedById String
|
||||
updatedAt DateTime @updatedAt
|
||||
versions AnexaTemplateVersion[]
|
||||
|
||||
@@map("anexa_templates")
|
||||
}
|
||||
|
||||
model AnexaTemplateVersion {
|
||||
id String @id @default(uuid())
|
||||
templateId String
|
||||
template AnexaTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||||
contentJson Json
|
||||
savedById String
|
||||
savedAt DateTime @default(now())
|
||||
label String?
|
||||
|
||||
@@index([templateId])
|
||||
@@map("anexa_template_versions")
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding reference data...');
|
||||
|
||||
// ── Disability Grades (grade de dizabilitate MD) ─────────────────
|
||||
await prisma.disabilityGrade.createMany({
|
||||
data: [
|
||||
{ code: 'GRAD_I', name: 'Grad I (sever)' },
|
||||
{ code: 'GRAD_II', name: 'Grad II (accentuat)' },
|
||||
{ code: 'GRAD_III', name: 'Grad III (mediu)' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
console.log(' ✓ DisabilityGrade (3)');
|
||||
|
||||
// ── Tax Exemptions (scutiri Codul Fiscal RM) ─────────────────────
|
||||
await prisma.taxExemption.createMany({
|
||||
data: [
|
||||
{ code: 'PE', description: 'Scutire personală (art. 33 CF)' },
|
||||
{ code: 'PI', description: 'Scutire personală majorată (art. 33 alin. 2 CF)' },
|
||||
{ code: 'SO', description: 'Scutire pentru soț/soție (art. 34 CF)' },
|
||||
{ code: 'MP1', description: 'Scutire pentru 1 copil minor (art. 35 CF)' },
|
||||
{ code: 'MP2', description: 'Scutire pentru 2 copii minori (art. 35 CF)' },
|
||||
{ code: 'MP3', description: 'Scutire pentru 3+ copii minori (art. 35 CF)' },
|
||||
{ code: 'INVALID', description: 'Scutire persoană cu dizabilitate (art. 33 alin. 2 lit. a CF)' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
console.log(' ✓ TaxExemption (7)');
|
||||
|
||||
// ── Work Schedules ───────────────────────────────────────────────
|
||||
await prisma.workSchedule.createMany({
|
||||
data: [
|
||||
{ name: '5/2 — 8h/zi', daysWork: 5, daysRest: 2, hoursPerDay: 8 },
|
||||
{ name: '5/2 — 7h/zi', daysWork: 5, daysRest: 2, hoursPerDay: 7 },
|
||||
{ name: 'Gărzi 24h (1/3)', daysWork: 1, daysRest: 3, hoursPerDay: 24 },
|
||||
{ name: 'Gărzi 12h zi (1/1)', daysWork: 1, daysRest: 1, hoursPerDay: 12 },
|
||||
{ name: 'Gărzi 12h noapte (1/1)',daysWork: 1, daysRest: 1, hoursPerDay: 12 },
|
||||
{ name: '7/7 — 12h/zi', daysWork: 7, daysRest: 7, hoursPerDay: 12 },
|
||||
{ name: 'Rotație 2/2 — 12h', daysWork: 2, daysRest: 2, hoursPerDay: 12 },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
console.log(' ✓ WorkSchedule (7)');
|
||||
|
||||
// ── Departments — Medpark International Hospital ─────────────────
|
||||
// Level 0: hospital root
|
||||
const root = await prisma.department.upsert({
|
||||
where: { code: 'MEDPARK' },
|
||||
update: {},
|
||||
create: { name: 'Medpark International Hospital', code: 'MEDPARK' },
|
||||
});
|
||||
|
||||
// Helper to upsert a department
|
||||
const dept = async (name: string, code: string, parentId?: string) =>
|
||||
prisma.department.upsert({
|
||||
where: { code },
|
||||
update: {},
|
||||
create: { name, code, parentId: parentId ?? null },
|
||||
});
|
||||
|
||||
// Administration
|
||||
const admin = await dept('Administrare', 'ADMIN', root.id);
|
||||
await dept('Resurse Umane', 'HR', admin.id);
|
||||
await dept('Financiar-Contabil', 'FIN', admin.id);
|
||||
await dept('Juridic', 'JUR', admin.id);
|
||||
await dept('IT', 'IT', admin.id);
|
||||
await dept('Achiziții', 'ACHIZ', admin.id);
|
||||
|
||||
// Medical divisions
|
||||
const med = await dept('Bloc Medical', 'MED', root.id);
|
||||
|
||||
const terapie = await dept('Terapie și Medicină Internă', 'TERAP', med.id);
|
||||
await dept('Cardiologie', 'CARDIO', terapie.id);
|
||||
await dept('Gastroenterologie', 'GASTRO', terapie.id);
|
||||
await dept('Endocrinologie', 'ENDO', terapie.id);
|
||||
await dept('Neurologie', 'NEURO', terapie.id);
|
||||
await dept('Pneumologie', 'PNEUMO', terapie.id);
|
||||
await dept('Reumatologie', 'REUMA', terapie.id);
|
||||
|
||||
const chir = await dept('Chirurgie', 'CHIR', med.id);
|
||||
await dept('Chirurgie Generală', 'CHIR_GEN', chir.id);
|
||||
await dept('Chirurgie Vasculară', 'CHIR_VAS', chir.id);
|
||||
await dept('Ortopedie și Traumatologie','ORTOPED', chir.id);
|
||||
await dept('Urologie', 'UROL', chir.id);
|
||||
await dept('ORL', 'ORL', chir.id);
|
||||
await dept('Oftalmologie', 'OFTALMO', chir.id);
|
||||
|
||||
const ped = await dept('Pediatrie', 'PED', med.id);
|
||||
await dept('Pediatrie Generală', 'PED_GEN', ped.id);
|
||||
await dept('Neonatologie', 'NEONAT', ped.id);
|
||||
|
||||
const obst = await dept('Obstetrică-Ginecologie', 'OBG', med.id);
|
||||
await dept('Obstetrică', 'OBSTET', obst.id);
|
||||
await dept('Ginecologie', 'GINECO', obst.id);
|
||||
|
||||
await dept('Oncologie', 'ONCOL', med.id);
|
||||
await dept('Hemodializă', 'HEMODIAL', med.id);
|
||||
await dept('Psihiatrie', 'PSIHIAT', med.id);
|
||||
await dept('Dermatologie', 'DERMA', med.id);
|
||||
await dept('Medicină Sportivă și Reabilitare', 'REAB', med.id);
|
||||
|
||||
// Diagnostics
|
||||
const diag = await dept('Diagnostic', 'DIAG', root.id);
|
||||
await dept('Laborator Clinic', 'LAB', diag.id);
|
||||
await dept('Imagistică Medicală (CT/RMN/Rx)', 'IMAG', diag.id);
|
||||
await dept('Endoscopie', 'ENDOSC', diag.id);
|
||||
await dept('Ecografie', 'ECO', diag.id);
|
||||
await dept('Cardiologie Funcțională (ECG/Holter)', 'ECG', diag.id);
|
||||
|
||||
// Support
|
||||
const suport = await dept('Servicii Suport', 'SUPORT', root.id);
|
||||
await dept('Urgențe (UPU)', 'UPU', suport.id);
|
||||
await dept('Anestezie și Terapie Intensivă (ATI)', 'ATI', suport.id);
|
||||
await dept('Bloc Operator', 'BLOC_OP', suport.id);
|
||||
await dept('Sterilizare', 'STERIL', suport.id);
|
||||
await dept('Farmacie', 'FARMACIE', suport.id);
|
||||
await dept('Nutriție și Dietetică', 'NUTRIT', suport.id);
|
||||
await dept('Serviciu Social', 'SOC', suport.id);
|
||||
await dept('Curățenie și Dezinfecție', 'CURATENIE',suport.id);
|
||||
await dept('Securitate', 'SECUR', suport.id);
|
||||
await dept('Tehnică Medicală', 'TEH_MED', suport.id);
|
||||
|
||||
// Ambulatory
|
||||
const ambul = await dept('Centru Ambulator', 'AMBUL', root.id);
|
||||
await dept('Medicină de Familie', 'MED_FAM', ambul.id);
|
||||
await dept('Consultații Specializate', 'CONSULT', ambul.id);
|
||||
await dept('Fizioterapie', 'FIZIOTER', ambul.id);
|
||||
|
||||
const deptCount = await prisma.department.count();
|
||||
console.log(` ✓ Department (${deptCount})`);
|
||||
|
||||
// ── Anexa Templates — minimal seed ─────────────────────────
|
||||
const heading = (text: string, level = 2) => ({
|
||||
type: 'heading',
|
||||
attrs: { level, textAlign: 'center' },
|
||||
content: [{ type: 'text', text }],
|
||||
});
|
||||
const para = (content: object[], textAlign: string = 'left') => ({
|
||||
type: 'paragraph',
|
||||
attrs: { textAlign },
|
||||
content,
|
||||
});
|
||||
const txt = (text: string, marks?: { type: string }[]) =>
|
||||
marks ? { type: 'text', text, marks } : { type: 'text', text };
|
||||
const chip = (key: string, label: string) => ({
|
||||
type: 'variableChip',
|
||||
attrs: { key, label },
|
||||
});
|
||||
const cell = (content: object[]) => ({ type: 'tableCell', content });
|
||||
const row = (cells: object[]) => ({ type: 'tableRow', content: cells });
|
||||
const headerRow = (labels: string[]) =>
|
||||
row(labels.map((l) => cell([para([txt(l, [{ type: 'bold' }])], 'center')])));
|
||||
|
||||
// ── Anexa 3: Fișa de solicitare ─────────────────────────────────
|
||||
const anexa3 = {
|
||||
type: 'doc',
|
||||
content: [
|
||||
heading('FIȘA DE SOLICITARE A EXAMENULUI MEDICAL'),
|
||||
para([txt('Unitatea economică: '), chip('company.name', 'Denumirea unității')]),
|
||||
para([txt('IDNO: '), chip('company.idno', 'IDNO'), txt(' Adresa: '), chip('company.address', 'Adresa')]),
|
||||
para([txt('Tipul examenului: '), chip('tipExamen', 'Tipul examenului')]),
|
||||
para([txt('Departament: '), chip('department.name', 'Departament'), txt(' Carta de risc: '), chip('riskCard.name', 'Carta de risc')]),
|
||||
para([txt('Data: '), chip('document.date', 'Data documentului'), txt(' Nr.: '), chip('document.number', 'Număr')]),
|
||||
para([txt('Lista angajaților:', [{ type: 'bold' }])]),
|
||||
{
|
||||
type: 'table',
|
||||
attrs: { repeatRows: true },
|
||||
content: [
|
||||
headerRow(['Nr.', 'Nume Prenume', 'IDNP', 'Anul nașterii', 'Ocupația', 'Tipul examenului']),
|
||||
row([
|
||||
cell([para([chip('row.index', 'Nr.')])]),
|
||||
cell([para([chip('row.employeeName', 'Nume Prenume')])]),
|
||||
cell([para([chip('row.idnp', 'IDNP')])]),
|
||||
cell([para([chip('row.birthYear', 'Anul nașterii')])]),
|
||||
cell([para([chip('row.occupation', 'Ocupația')])]),
|
||||
cell([para([chip('row.tipExamen', 'Tipul examenului')])]),
|
||||
]),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// ── Anexa 4: Fișa de evaluare a riscurilor profesionale (NU-10-MS-2026) ──
|
||||
const cb = (key: string, label: string) => [chip(`a4.cb.${key}`, '☐'), txt(' ' + label)];
|
||||
const factorTable = (rowsKey: string, cols: string[], rowChips: string[]) => ({
|
||||
type: 'table',
|
||||
attrs: { repeatRows: true, rowsKey },
|
||||
content: [
|
||||
headerRow(cols),
|
||||
row(rowChips.map((k) => cell([para([chip(k, '—')])]))),
|
||||
],
|
||||
});
|
||||
|
||||
const anexa4 = {
|
||||
type: 'doc',
|
||||
content: [
|
||||
// ── Antet ──
|
||||
para([txt('Unitatea economică/instituția: '), chip('a4.unitatea', 'Denumirea unității')]),
|
||||
para([txt('Adresa, telefon, fax, e-mail: '), chip('a4.adresa', 'Adresa')]),
|
||||
para([txt('Filiala: '), chip('a4.filiala', '—'), txt(' Adresa filialei: '), chip('a4.adresaFiliala', '—'), txt(' CAEM (primele 2 cifre): '), chip('a4.caem2', '—')]),
|
||||
heading('FIȘA de evaluare a riscurilor profesionale', 2),
|
||||
para([txt('Ocupația (subgrupa majoră CORM): '), chip('a4.cormSubgrupa', '—')]),
|
||||
para([txt('Direcția/secția/sectorul: '), chip('a4.directiaSectia', '—')]),
|
||||
para([txt('Numărul locului de muncă: '), chip('a4.numarLoc', '—'), txt(' CAEM (nivel diviziune): '), chip('a4.caemDiviziune', '—')]),
|
||||
para([txt('Numărul de lucrători care pot activa la acest loc de muncă: '), chip('a4.numarLucratori', '—'), txt(' Clasa condițiilor de muncă: '), chip('a4.clasa', '—')]),
|
||||
|
||||
// ── Descrierea activității ──
|
||||
heading('Descrierea activității', 3),
|
||||
para([txt('Lucrul în echipă: '), ...cb('echipa', 'da'), txt(' Nr. ore/zi: '), chip('a4.val.oreZi', '—'), txt(' Nr. schimburi: '), chip('a4.val.schimburi', '—')]),
|
||||
para([...cb('schimbNoapte', 'schimb de noapte'), txt(' '), ...cb('pauzeOrganizate', 'pauze organizate')]),
|
||||
para([txt('Riscuri: '), ...cb('riscInfectare', 'infectare'), txt(' '), ...cb('riscElectrocutare', 'electrocutare'), txt(' '), ...cb('riscTensiuneInalta', 'tensiune înaltă'), txt(' '), ...cb('riscInecare', 'înecare'), txt(' '), ...cb('riscAsfixiere', 'asfixiere')]),
|
||||
para([...cb('riscStrivire', 'strivire'), txt(' '), ...cb('riscTaiere', 'tăiere'), txt(' '), ...cb('riscIntepare', 'înțepare'), txt(' '), ...cb('riscLovire', 'lovire'), txt(' '), ...cb('riscMuscatura', 'mușcătură'), txt(' '), ...cb('riscMicrotraumatisme', 'microtraumatisme repetate')]),
|
||||
para([txt('Conduce mașina instituției: '), ...cb('conduceMasina', 'da'), txt(' categorie: '), chip('a4.val.conduceMasinaCategorie', '—'), txt(' '), ...cb('conduceUtilajeIntrauzinal', 'conduce utilaje numai intrauzinal')]),
|
||||
|
||||
// ── Spațiul de lucru ──
|
||||
heading('Descrierea spațiului de lucru', 3),
|
||||
para([txt('Dimensiunile încăperii: L '), chip('a4.val.spatiuL', '—'), txt(' l '), chip('a4.val.spatiul', '—'), txt(' H '), chip('a4.val.spatiuH', '—'), txt(' m')]),
|
||||
para([txt('Suprafața de lucru: '), ...cb('suprafataVerticala', 'verticală'), txt(' '), ...cb('suprafataOrizontala', 'orizontală'), txt(' '), ...cb('suprafataOblica', 'oblică')]),
|
||||
para([txt('Muncă: '), ...cb('muncaIzolare', 'în condiții de izolare'), txt(' '), ...cb('muncaInaltime', 'la înălțime'), txt(' '), ...cb('muncaInMiscare', 'în mișcare')]),
|
||||
|
||||
// ── Efort fizic ──
|
||||
heading('Efort fizic', 3),
|
||||
para([txt('Poziție preponderent: '), ...cb('pozitieOrtostatica', 'ortostatică'), txt(' '), ...cb('pozitieAsezat', 'așezat'), txt(' '), ...cb('pozitieAplecata', 'aplecată'), txt(' '), ...cb('pozitieMixta', 'mixtă'), txt(' '), ...cb('pozitieFortata', 'forțată/nefiziologică')]),
|
||||
para([txt('Suprasolicitări musculo-articulare (coloană): '), ...cb('coloanaCervicala', 'cervicală'), txt(' '), ...cb('coloanaToracala', 'toracală'), txt(' '), ...cb('coloanaLombara', 'lombară')]),
|
||||
para([txt('Manipulare manuală a maselor: '), ...cb('manipulareRidicare', 'ridicare'), txt(' '), ...cb('manipulareCoborare', 'coborâre'), txt(' '), ...cb('manipulareImpingere', 'împingere'), txt(' '), ...cb('manipulareTragere', 'tragere'), txt(' '), ...cb('manipularePurtare', 'purtare'), txt(' '), ...cb('manipulareDeplasare', 'deplasare')]),
|
||||
para([txt('Greutate maximă manipulată manual: '), chip('a4.val.greutateMaxima', '—')]),
|
||||
para([txt('Suprasolicitări: '), ...cb('suprasolicitariVizuale', 'vizuale'), txt(' '), ...cb('suprasolicitariAuditive', 'auditive'), txt(' '), ...cb('suprasolicitariNeuropsihice', 'neuropsihosenzoriale')]),
|
||||
|
||||
// ── Factori de risc cu tabel ──
|
||||
heading('AGENȚI CHIMICI', 3),
|
||||
para([...cb('chimici_da', 'da'), txt(' '), ...cb('chimici_nu', 'nu'), txt(' (se atașează Fișa cu date de securitate, în limba română)')]),
|
||||
factorTable('chimici',
|
||||
['Agentul chimic', 'CAS', 'EINECS', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.cas', 'row.einecs', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
heading('PULBERI', 3),
|
||||
para([...cb('pulberi_da', 'da'), txt(' '), ...cb('pulberi_nu', 'nu')]),
|
||||
factorTable('pulberi',
|
||||
['Pulberi', 'CAS', 'EINECS', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.cas', 'row.einecs', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
heading('AGENȚI BIOLOGICI', 3),
|
||||
para([...cb('biologici_da', 'da'), txt(' '), ...cb('biologici_nu', 'nu')]),
|
||||
factorTable('biologici',
|
||||
['Agent biologic', 'Clasificare', 'Note'],
|
||||
['row.denumire', 'row.clasificare', 'row.caracteristici']),
|
||||
|
||||
heading('ZGOMOT PROFESIONAL', 3),
|
||||
para([...cb('zgomot_da', 'da'), txt(' '), ...cb('zgomot_nu', 'nu')]),
|
||||
factorTable('zgomot',
|
||||
['Tipul', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
heading('VIBRAȚII MECANICE', 3),
|
||||
para([...cb('vibratii_da', 'da'), txt(' '), ...cb('vibratii_nu', 'nu')]),
|
||||
factorTable('vibratii',
|
||||
['Tipul', 'Zona afectată', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.zona', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
// ── Microclimat (descriptiv) ──
|
||||
heading('MICROCLIMAT', 3),
|
||||
para([...cb('microclimatInterior', 'lucrări interior'), txt(' '), ...cb('microclimatExterior', 'lucru exterior/sub cerul liber')]),
|
||||
para([txt('Radiații calorice (perioada rece): '), ...cb('radiatiiCaloriceRece', 'da'), txt(' Radiații calorice (perioada caldă): '), ...cb('radiatiiCaloriceCalda', 'da')]),
|
||||
|
||||
// ── Radiații ionizante ──
|
||||
heading('RADIAȚII IONIZANTE', 3),
|
||||
para([...cb('radiatii_da', 'da'), txt(' '), ...cb('radiatii_nu', 'nu'), txt(' Grupa: '), chip('a4.rad.grupa', '—')]),
|
||||
para([txt('Aparatură folosită: '), chip('a4.rad.aparatura', '—'), txt(' Surse: '), chip('a4.rad.surse', '—')]),
|
||||
para([txt('Tip de expunere: '), chip('a4.rad.tipExpunere', '—'), txt(' Măsuri de protecție: '), chip('a4.rad.masuriProtectie', '—')]),
|
||||
|
||||
// ── Câmp electromagnetic ──
|
||||
heading('CÂMP ELECTROMAGNETIC', 3),
|
||||
para([...cb('campEM_da', 'da'), txt(' '), ...cb('campEM_nu', 'nu')]),
|
||||
factorTable('campEM',
|
||||
['Tipul', 'Zona afectată', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.zona', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
// ── Iluminat ──
|
||||
heading('ILUMINAT', 3),
|
||||
para([...cb('iluminatSuficient', 'suficient'), txt(' '), ...cb('iluminatInsuficient', 'insuficient'), txt(' '), ...cb('iluminatNatural', 'natural'), txt(' '), ...cb('iluminatArtificial', 'artificial'), txt(' '), ...cb('iluminatMixt', 'mixt')]),
|
||||
|
||||
// ── Radiații optice artificiale ──
|
||||
heading('RADIAȚII OPTICE ARTIFICIALE', 3),
|
||||
para([...cb('optice_da', 'da'), txt(' '), ...cb('optice_nu', 'nu')]),
|
||||
factorTable('optice',
|
||||
['Tipul', 'Zona afectată', 'Timp expunere', 'VEP', 'VLEP obligatorie', 'Caracteristici'],
|
||||
['row.denumire', 'row.zona', 'row.timp', 'row.vep', 'row.vlep', 'row.caracteristici']),
|
||||
|
||||
// ── Subsol ──
|
||||
heading('Protecție și dotări', 3),
|
||||
para([txt('Mijloace de protecție colectivă: '), chip('a4.protectieColectiva', '—')]),
|
||||
para([txt('Mijloace de protecție individuală: '), chip('a4.protectieIndividuala', '—')]),
|
||||
para([txt('Echipament de lucru: '), chip('a4.echipament', '—')]),
|
||||
para([txt('Anexe igienico-sanitare: '), ...cb('anexe.vestiar', 'vestiar'), txt(' '), ...cb('anexe.chiuveta', 'chiuvetă'), txt(' '), ...cb('anexe.wc', 'WC'), txt(' '), ...cb('anexe.dus', 'duș'), txt(' '), ...cb('anexe.salaMese', 'sală de mese'), txt(' '), ...cb('anexe.recreere', 'spațiu de recreere')]),
|
||||
para([txt('Observații: '), chip('a4.observatii', '—')]),
|
||||
para([txt('Data completării: '), chip('document.date', 'Data')]),
|
||||
para([txt('Angajatorul (nume, prenume, semnătura): ____________________')]),
|
||||
para([txt('Instrucțiuni de completare: răspuns afirmativ [☑]; răspuns negativ [☐].', [{ type: 'italic' }])]),
|
||||
],
|
||||
};
|
||||
|
||||
// ── Anexa 4B: Supliment radiații ionizante ───────────────────────
|
||||
const anexa4b = {
|
||||
type: 'doc',
|
||||
content: [
|
||||
heading('SUPLIMENT — EXPUNERE LA RADIAȚII IONIZANTE'),
|
||||
para([txt('Unitatea economică: '), chip('company.name', 'Denumirea unității')]),
|
||||
para([txt('Data: '), chip('document.date', 'Data documentului')]),
|
||||
para([txt('Personal expus radiațiilor ionizante:', [{ type: 'bold' }])]),
|
||||
{
|
||||
type: 'table',
|
||||
attrs: { repeatRows: true },
|
||||
content: [
|
||||
headerRow(['Nr.', 'Nume Prenume', 'IDNP', 'Data intrării', 'Perioada anterioară', 'Ani', 'Doza ext. (mSv)', 'Doza int. (mSv)', 'Total (mSv)']),
|
||||
row([
|
||||
cell([para([chip('row.index', 'Nr.')])]),
|
||||
cell([para([chip('row.employeeName', 'Nume Prenume')])]),
|
||||
cell([para([chip('row.idnp', 'IDNP')])]),
|
||||
cell([para([chip('row.entryDate', 'Data intrării')])]),
|
||||
cell([para([chip('row.priorPeriod', 'Perioada anterioară')])]),
|
||||
cell([para([chip('row.priorYears', 'Ani')])]),
|
||||
cell([para([chip('row.externalMsv', 'Doza ext.')])]),
|
||||
cell([para([chip('row.internalMsv', 'Doza int.')])]),
|
||||
cell([para([chip('row.totalMsv', 'Total')])]),
|
||||
]),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// ── Anexa 6: Verdict medic de familie (per-employee) ─────────────
|
||||
const anexa6 = {
|
||||
type: 'doc',
|
||||
content: [
|
||||
heading('FIȘĂ DE APTITUDINE — VERDICTUL MEDICULUI DE FAMILIE'),
|
||||
para([txt('Angajat: '), chip('employee.fullName', 'Nume Prenume')]),
|
||||
para([txt('IDNP: '), chip('employee.idnp', 'IDNP'), txt(' Data nașterii: '), chip('employee.birthDate', 'Data nașterii')]),
|
||||
para([txt('Ocupația: '), chip('employee.occupation', 'Ocupația'), txt(' Departament: '), chip('employee.department', 'Departament')]),
|
||||
para([txt('Tipul examenului: '), chip('tipExamen', 'Tipul examenului')]),
|
||||
para([txt('Data examinării: '), chip('document.date', 'Data')]),
|
||||
para([txt('Verdict:', [{ type: 'bold' }])]),
|
||||
para([chip('verdict.checkbox.apt', '☐'), txt(' Apt')]),
|
||||
para([chip('verdict.checkbox.apt_perioada_adaptare', '☐'), txt(' Apt în perioada de adaptare')]),
|
||||
para([chip('verdict.checkbox.apt_conditionat', '☐'), txt(' Apt condiționat')]),
|
||||
para([chip('verdict.checkbox.inapt_temporar', '☐'), txt(' Inapt temporar')]),
|
||||
para([chip('verdict.checkbox.inapt', '☐'), txt(' Inapt')]),
|
||||
para([txt('Recomandări: '), chip('verdict.recomandari', 'Recomandări')]),
|
||||
para([txt(' ')]),
|
||||
para([txt('Semnătura medicului de familie: ____________________')]),
|
||||
],
|
||||
};
|
||||
|
||||
const SYS = '00000000-0000-0000-0000-000000000000';
|
||||
const templates: Array<{ type: 'ANEXA_3' | 'ANEXA_4' | 'ANEXA_4B' | 'ANEXA_6'; name: string; doc: object }> = [
|
||||
{ type: 'ANEXA_3', name: 'Fișa de solicitare a examenului medical', doc: anexa3 },
|
||||
{ type: 'ANEXA_4', name: 'Fișa de evaluare a locului de muncă', doc: anexa4 },
|
||||
{ type: 'ANEXA_4B', name: 'Supliment radiații ionizante', doc: anexa4b },
|
||||
{ type: 'ANEXA_6', name: 'Verdict medic de familie', doc: anexa6 },
|
||||
];
|
||||
|
||||
for (const t of templates) {
|
||||
await prisma.anexaTemplate.upsert({
|
||||
where: { type: t.type },
|
||||
update: { name: t.name, contentJson: t.doc as never },
|
||||
create: { type: t.type, name: t.name, contentJson: t.doc as never, updatedById: SYS },
|
||||
});
|
||||
}
|
||||
console.log(' ✓ AnexaTemplate (4)');
|
||||
|
||||
// ── Inventory items (depozit Vestimentație + Echipament) ─────────
|
||||
const inventory = [
|
||||
{ sku: 'UN-CHIR-S-AL', name: 'Uniformă chirurgie S albastru', type: 'uniforma' as const, size: 'S', color: 'albastru', stockQty: 50 },
|
||||
{ sku: 'UN-CHIR-M-AL', name: 'Uniformă chirurgie M albastru', type: 'uniforma' as const, size: 'M', color: 'albastru', stockQty: 50 },
|
||||
{ sku: 'UN-CHIR-L-AL', name: 'Uniformă chirurgie L albastru', type: 'uniforma' as const, size: 'L', color: 'albastru', stockQty: 50 },
|
||||
{ sku: 'UN-ATI-M-VE', name: 'Uniformă ATI M verde', type: 'uniforma' as const, size: 'M', color: 'verde', stockQty: 30 },
|
||||
{ sku: 'HA-MED-M-AL', name: 'Halat medical M alb', type: 'halat' as const, size: 'M', color: 'alb', stockQty: 50 },
|
||||
{ sku: 'HA-MED-L-AL', name: 'Halat medical L alb', type: 'halat' as const, size: 'L', color: 'alb', stockQty: 50 },
|
||||
{ sku: 'HA-LAB-M-AL', name: 'Halat laborator M alb', type: 'halat' as const, size: 'M', color: 'alb', stockQty: 30 },
|
||||
{ sku: 'HA-LAB-L-AL', name: 'Halat laborator L alb', type: 'halat' as const, size: 'L', color: 'alb', stockQty: 30 },
|
||||
{ sku: 'CI-38-AL', name: 'Ciupici 38-40 albi', type: 'ciupici' as const, size: '38-40', color: 'alb', stockQty: 80 },
|
||||
{ sku: 'CI-41-AL', name: 'Ciupici 41-43 albi', type: 'ciupici' as const, size: '41-43', color: 'alb', stockQty: 80 },
|
||||
{ sku: 'CI-44-AL', name: 'Ciupici 44-46 albi', type: 'ciupici' as const, size: '44-46', color: 'alb', stockQty: 80 },
|
||||
{ sku: 'VE-S-TE', name: 'Vestă S teal', type: 'vesta' as const, size: 'S', color: 'teal', stockQty: 20 },
|
||||
{ sku: 'VE-M-TE', name: 'Vestă M teal', type: 'vesta' as const, size: 'M', color: 'teal', stockQty: 20 },
|
||||
{ sku: 'AT-SAMS-A15', name: 'Samsung Galaxy A15', type: 'aparat_telefon' as const, stockQty: 15 },
|
||||
{ sku: 'AT-IPHONE-SE', name: 'iPhone SE 2022', type: 'aparat_telefon' as const, stockQty: 10 },
|
||||
];
|
||||
for (const item of inventory) {
|
||||
await prisma.inventoryItem.upsert({
|
||||
where: { sku: item.sku },
|
||||
update: {},
|
||||
create: item,
|
||||
});
|
||||
}
|
||||
console.log(` ✓ InventoryItem (${inventory.length})`);
|
||||
|
||||
// ── Demo data pentru prezentare ──────────────────────────────
|
||||
console.log('\n🎭 Seeding demo data...');
|
||||
|
||||
// Risk cards
|
||||
const chirExposures = [
|
||||
{ tip: 'AGENT_CHIMIC' as const, denumire: 'Glutaraldehidă (dezinfectant)', cas: '111-30-8', einecs: '203-856-5', timpExpunere: '2 h/zi', vep: '0,03 ppm', vlep: '0,1 ppm', caracteristici: 'iritant respirator' },
|
||||
{ tip: 'AGENT_BIOLOGIC' as const, denumire: 'Virusuri hematogene (HBV, HCV, HIV)', clasificare: 'grupa 3', caracteristici: 'risc de infectare prin înțepare/tăiere' },
|
||||
];
|
||||
const chirHeader = {
|
||||
filiala: 'Sediul central',
|
||||
caemPrimeleDouaCifre: '86',
|
||||
cormSubgrupaMajora: 'Personal medical — secție chirurgie',
|
||||
directiaSectiaSectorul: 'Bloc Medical / Chirurgie Generală',
|
||||
numarulLoculuiDeMunca: 'CH-01',
|
||||
caemDiviziune: '86.10',
|
||||
clasaConditiilorDeMunca: '3.2',
|
||||
numarLucratoriPosibili: 12,
|
||||
evaluareDetalii: {
|
||||
echipa: true, oreZi: '8', schimburi: '2', schimbNoapte: true, pauzeOrganizate: true,
|
||||
riscInfectare: true, riscTaiere: true, riscIntepare: true,
|
||||
pozitieOrtostatica: true, manipulareRidicare: true, suprasolicitariVizuale: true,
|
||||
},
|
||||
anexeIgienicoSanitare: { vestiar: true, chiuveta: true, wc: true, dus: true, salaMese: true },
|
||||
mijloaceProtectieIndividuala: 'Mănuși, mască, halat steril',
|
||||
echipamentLucru: 'Uniformă chirurgicală',
|
||||
};
|
||||
const rcChir = await prisma.workplaceRiskCard.upsert({
|
||||
where: { name: 'Secție chirurgie generală' },
|
||||
update: { ...chirHeader, exposures: { deleteMany: {}, create: chirExposures } },
|
||||
create: { name: 'Secție chirurgie generală', ...chirHeader, exposures: { create: chirExposures } },
|
||||
});
|
||||
|
||||
const imagExposures = [
|
||||
{ tip: 'CAMP_ELECTROMAGNETIC' as const, denumire: 'Câmp electromagnetic RMN', zonaAfectata: 'corp întreg', timpExpunere: '4 h/zi', vep: '—', vlep: 'conform NU-10', caracteristici: 'câmp magnetic static intens' },
|
||||
];
|
||||
const imagHeader = {
|
||||
filiala: 'Sediul central',
|
||||
caemPrimeleDouaCifre: '86',
|
||||
cormSubgrupaMajora: 'Personal imagistică medicală',
|
||||
directiaSectiaSectorul: 'Diagnostic / Imagistică Medicală',
|
||||
numarulLoculuiDeMunca: 'IMG-01',
|
||||
caemDiviziune: '86.90',
|
||||
clasaConditiilorDeMunca: '3.3',
|
||||
numarLucratoriPosibili: 8,
|
||||
radiatiiIonizante: true,
|
||||
radiatiiGrupa: 'A',
|
||||
radiatiiSurse: 'închise',
|
||||
radiatiiTipExpunere: 'X externă',
|
||||
radiatiiAparatura: 'CT, aparat Rx',
|
||||
radiatiiMasuriProtectie: 'șorț cu plumb, ecran de protecție, dozimetru individual',
|
||||
evaluareDetalii: {
|
||||
echipa: true, oreZi: '7', schimburi: '2',
|
||||
riscElectrocutare: true, pozitieAsezat: true, suprasolicitariVizuale: true,
|
||||
},
|
||||
anexeIgienicoSanitare: { vestiar: true, chiuveta: true, wc: true },
|
||||
mijloaceProtectieIndividuala: 'Șorț cu plumb, ochelari, dozimetru',
|
||||
};
|
||||
const rcImag = await prisma.workplaceRiskCard.upsert({
|
||||
where: { name: 'Radiologie și imagistică' },
|
||||
update: { ...imagHeader, exposures: { deleteMany: {}, create: imagExposures } },
|
||||
create: { name: 'Radiologie și imagistică', ...imagHeader, exposures: { create: imagExposures } },
|
||||
});
|
||||
console.log(' ✓ WorkplaceRiskCard demo (2) — cu antet Anexa 4 + factori');
|
||||
|
||||
// Lookup departments & inventory items
|
||||
const chirGenDept = await prisma.department.findUnique({ where: { code: 'CHIR_GEN' } });
|
||||
const imagDept = await prisma.department.findUnique({ where: { code: 'IMAG' } });
|
||||
const uniformaS = await prisma.inventoryItem.findUnique({ where: { sku: 'UN-CHIR-S-AL' } });
|
||||
const halatM = await prisma.inventoryItem.findUnique({ where: { sku: 'HA-MED-M-AL' } });
|
||||
|
||||
// 4 demo employees (IDNPs pre-validated cu algoritmul de sumă de control MD)
|
||||
const emp1 = await prisma.employee.upsert({
|
||||
where: { idnp: '1985061500016' },
|
||||
update: {},
|
||||
create: {
|
||||
idnp: '1985061500016', nume: 'Popescu', prenume: 'Alexandru',
|
||||
sex: 'M', dataNasterii: new Date('1985-06-15'),
|
||||
domiciliu: 'mun. Chișinău, str. Ștefan cel Mare 1',
|
||||
telefonPersonal: '+37369100001', status: 'activ',
|
||||
},
|
||||
});
|
||||
const emp2 = await prisma.employee.upsert({
|
||||
where: { idnp: '1990032200017' },
|
||||
update: {},
|
||||
create: {
|
||||
idnp: '1990032200017', nume: 'Ionescu', prenume: 'Maria',
|
||||
sex: 'F', dataNasterii: new Date('1990-03-22'),
|
||||
domiciliu: 'mun. Chișinău, str. Mihai Viteazul 5',
|
||||
telefonPersonal: '+37369100002', status: 'activ',
|
||||
},
|
||||
});
|
||||
const emp3 = await prisma.employee.upsert({
|
||||
where: { idnp: '1978110800016' },
|
||||
update: {},
|
||||
create: {
|
||||
idnp: '1978110800016', nume: 'Rusu', prenume: 'Viorel',
|
||||
sex: 'M', dataNasterii: new Date('1978-11-08'),
|
||||
domiciliu: 'mun. Chișinău, str. Alba Iulia 12',
|
||||
telefonPersonal: '+37369100003', status: 'activ',
|
||||
},
|
||||
});
|
||||
const emp4 = await prisma.employee.upsert({
|
||||
where: { idnp: '2001091400010' },
|
||||
update: {},
|
||||
create: {
|
||||
idnp: '2001091400010', nume: 'Cojocaru', prenume: 'Elena',
|
||||
sex: 'F', dataNasterii: new Date('2001-09-14'),
|
||||
domiciliu: 'mun. Chișinău, str. Trandafirilor 3',
|
||||
telefonPersonal: '+37369100004', status: 'activ',
|
||||
},
|
||||
});
|
||||
console.log(' ✓ Employee demo (4)');
|
||||
|
||||
// Employment contracts
|
||||
if (chirGenDept) {
|
||||
await prisma.employmentContract.upsert({
|
||||
where: { nrCim: 'CIM-DEMO-001' },
|
||||
update: {},
|
||||
create: {
|
||||
nrCim: 'CIM-DEMO-001', employeeId: emp1.id,
|
||||
categorie: 'principal', tipCim: 'de_baza', perioada: 'nedeterminata',
|
||||
dataSemnarii: new Date('2020-01-10'), dataAngajarii: new Date('2020-01-15'),
|
||||
departmentId: chirGenDept.id, functiaOrganigrama: 'Chirurg',
|
||||
salarizareDetails: { tip: 'fix', salariu: 18000, zileConcediu: 28 },
|
||||
},
|
||||
});
|
||||
await prisma.employmentContract.upsert({
|
||||
where: { nrCim: 'CIM-DEMO-002' },
|
||||
update: {},
|
||||
create: {
|
||||
nrCim: 'CIM-DEMO-002', employeeId: emp2.id,
|
||||
categorie: 'principal', tipCim: 'de_baza', perioada: 'nedeterminata',
|
||||
dataSemnarii: new Date('2021-03-01'), dataAngajarii: new Date('2021-03-05'),
|
||||
departmentId: chirGenDept.id, functiaOrganigrama: 'Asistentă medicală',
|
||||
salarizareDetails: { tip: 'fix', salariu: 10000, zileConcediu: 28 },
|
||||
},
|
||||
});
|
||||
}
|
||||
if (imagDept) {
|
||||
await prisma.employmentContract.upsert({
|
||||
where: { nrCim: 'CIM-DEMO-003' },
|
||||
update: {},
|
||||
create: {
|
||||
nrCim: 'CIM-DEMO-003', employeeId: emp3.id,
|
||||
categorie: 'principal', tipCim: 'de_baza', perioada: 'nedeterminata',
|
||||
dataSemnarii: new Date('2018-06-01'), dataAngajarii: new Date('2018-06-10'),
|
||||
departmentId: imagDept.id, functiaOrganigrama: 'Radiolog',
|
||||
salarizareDetails: { tip: 'fix', salariu: 20000, zileConcediu: 35 },
|
||||
},
|
||||
});
|
||||
await prisma.employmentContract.upsert({
|
||||
where: { nrCim: 'CIM-DEMO-004' },
|
||||
update: {},
|
||||
create: {
|
||||
nrCim: 'CIM-DEMO-004', employeeId: emp4.id,
|
||||
categorie: 'principal', tipCim: 'de_baza', perioada: 'nedeterminata',
|
||||
dataSemnarii: new Date('2023-09-01'), dataAngajarii: new Date('2023-09-15'),
|
||||
departmentId: imagDept.id, functiaOrganigrama: 'Asistentă radiologie',
|
||||
salarizareDetails: { tip: 'fix', salariu: 9500, zileConcediu: 28 },
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(' ✓ EmploymentContract demo (4)');
|
||||
|
||||
// Medical profiles:
|
||||
// emp1 — chirurgie, niciodată examinat
|
||||
// emp2 — chirurgie, examinat acum 15 luni (expirat)
|
||||
// emp3 — radiologie, examinat acum 11 luni + radiații (expiră curând)
|
||||
// emp4 — radiologie, niciodată examinat + radiații
|
||||
await prisma.employeeMedicalProfile.upsert({
|
||||
where: { employeeId: emp1.id }, update: {},
|
||||
create: { employeeId: emp1.id, workplaceRiskCardId: rcChir.id, expusRadiatiiIonizante: false },
|
||||
});
|
||||
await prisma.employeeMedicalProfile.upsert({
|
||||
where: { employeeId: emp2.id }, update: {},
|
||||
create: {
|
||||
employeeId: emp2.id, workplaceRiskCardId: rcChir.id,
|
||||
dataUltimControlMedical: new Date('2025-02-14'),
|
||||
expusRadiatiiIonizante: false,
|
||||
},
|
||||
});
|
||||
const emp3Radiatii = {
|
||||
workplaceRiskCardId: rcImag.id,
|
||||
dataUltimControlMedical: new Date('2025-06-14'),
|
||||
expusRadiatiiIonizante: true,
|
||||
dataIntrarii: new Date('2019-02-01'),
|
||||
expunereAnterioaraPerioda: '2015–2018',
|
||||
expunereAnterioaraAni: 3,
|
||||
dozaCumulataExternaMsv: 4.2500,
|
||||
dozaCumulataInternaMsv: 0.8000,
|
||||
};
|
||||
const emp3Supra = [
|
||||
{ fel: 'EXCEPTIONALA' as const, tipExpunere: 'X externă', data: new Date('2023-05-12'), dozaMsv: 2.5000 },
|
||||
{ fel: 'ACCIDENTALA' as const, tipExpunere: 'gamma externă', data: new Date('2024-09-03'), dozaMsv: 1.2000 },
|
||||
];
|
||||
await prisma.employeeMedicalProfile.upsert({
|
||||
where: { employeeId: emp3.id },
|
||||
update: { ...emp3Radiatii, overexposures: { deleteMany: {}, create: emp3Supra } },
|
||||
create: { employeeId: emp3.id, ...emp3Radiatii, overexposures: { create: emp3Supra } },
|
||||
});
|
||||
await prisma.employeeMedicalProfile.upsert({
|
||||
where: { employeeId: emp4.id }, update: {},
|
||||
create: { employeeId: emp4.id, workplaceRiskCardId: rcImag.id, expusRadiatiiIonizante: true },
|
||||
});
|
||||
console.log(' ✓ EmployeeMedicalProfile demo (4)');
|
||||
|
||||
// Pending checkups for inbox (verdict = null)
|
||||
// emp1 — la_angajare, acum 5 zile (depășit → roșu)
|
||||
// emp2 — periodic, peste 3 zile
|
||||
// emp3 — la_reluarea_activitatii, mâine
|
||||
const day = (offsetDays: number) => {
|
||||
const d = new Date('2026-05-14');
|
||||
d.setDate(d.getDate() + offsetDays);
|
||||
return d;
|
||||
};
|
||||
for (const [empId, tip, offset] of [
|
||||
[emp1.id, 'la_angajare', -5],
|
||||
[emp2.id, 'periodic', 3],
|
||||
[emp3.id, 'la_reluarea_activitatii', 1],
|
||||
] as [string, string, number][]) {
|
||||
const exists = await prisma.medicalCheckup.findFirst({ where: { employeeId: empId, verdict: null } });
|
||||
if (!exists) {
|
||||
await prisma.medicalCheckup.create({
|
||||
data: { employeeId: empId, tip: tip as never, dataPlanificata: day(offset) },
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(' ✓ MedicalCheckup demo — pending inbox (3)');
|
||||
|
||||
// ── Evaluation campaigns demo (modulul de evaluare nursing) ──────
|
||||
// Campania A — Chirurgie Generală, IN_PROGRESS:
|
||||
// emp2 (Ionescu Maria) — formular complet, scoruri bune + 1 criteriu EXPERT
|
||||
// → categorie calculată "superioara", ÎNCĂ NEAPROBATĂ
|
||||
// (nursing_director o poate aproba — demo aprobare)
|
||||
// emp1 (Popescu Alexandru) — formular parțial (în lucru) → "fara"
|
||||
if (chirGenDept) {
|
||||
const campMonth = new Date('2026-05-01');
|
||||
let camp = await prisma.evaluationCampaign.findFirst({
|
||||
where: { departmentId: chirGenDept.id, month: campMonth },
|
||||
});
|
||||
if (!camp) {
|
||||
camp = await prisma.evaluationCampaign.create({
|
||||
data: {
|
||||
name: 'Evaluare anuală nursing — Chirurgie Generală 2026',
|
||||
departmentId: chirGenDept.id,
|
||||
month: campMonth,
|
||||
status: 'in_progress',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.evaluationForm.upsert({
|
||||
where: { campaignId_employeeId: { campaignId: camp.id, employeeId: emp2.id } },
|
||||
update: {},
|
||||
create: {
|
||||
campaignId: camp.id, employeeId: emp2.id,
|
||||
abilitatiClinice: 'bine', judecataClinica: 'bine', manopere: 'bine', gestionareaSarcinilor: 'mediu',
|
||||
constiintaProfesionala: 'bine', atitudineaPacienti: 'bine', atitudineaColegi: 'bine', atitudineaPersonalNonMed: 'mediu',
|
||||
utilizareSmartphone: 'bine', respectareaProgramului: 'bine', respectareaDressCode: 'bine',
|
||||
testJci: { score: 18, max_score: 20, percent: 90, completed_at: '2026-05-10', source: 'academy_ocean', external_id: 'AO-DEMO-001' },
|
||||
completareaDocMed: true, perfectioneazaCunostinte: true,
|
||||
membruComitetCalitate: true, functieDeMonitor: false, inlocuiesteSuperiorul: false,
|
||||
categorieCalculata: 'superioara',
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.evaluationForm.upsert({
|
||||
where: { campaignId_employeeId: { campaignId: camp.id, employeeId: emp1.id } },
|
||||
update: {},
|
||||
create: {
|
||||
campaignId: camp.id, employeeId: emp1.id,
|
||||
abilitatiClinice: 'bine', judecataClinica: 'mediu', manopere: 'mediu',
|
||||
categorieCalculata: 'fara',
|
||||
},
|
||||
});
|
||||
console.log(' ✓ EvaluationCampaign demo — Chirurgie (in_progress, 2 formulare)');
|
||||
}
|
||||
|
||||
// Campania B — Imagistică, CLOSED (istoric read-only):
|
||||
// emp3 (Rusu Viorel) — formular finalizat și aprobat → "cat_I"
|
||||
if (imagDept) {
|
||||
const campMonth = new Date('2025-11-01');
|
||||
let camp = await prisma.evaluationCampaign.findFirst({
|
||||
where: { departmentId: imagDept.id, month: campMonth },
|
||||
});
|
||||
if (!camp) {
|
||||
camp = await prisma.evaluationCampaign.create({
|
||||
data: {
|
||||
name: 'Evaluare anuală nursing — Imagistică 2025',
|
||||
departmentId: imagDept.id,
|
||||
month: campMonth,
|
||||
status: 'closed',
|
||||
},
|
||||
});
|
||||
}
|
||||
await prisma.evaluationForm.upsert({
|
||||
where: { campaignId_employeeId: { campaignId: camp.id, employeeId: emp3.id } },
|
||||
update: {},
|
||||
create: {
|
||||
campaignId: camp.id, employeeId: emp3.id,
|
||||
abilitatiClinice: 'bine', judecataClinica: 'bine', manopere: 'bine', gestionareaSarcinilor: 'bine',
|
||||
constiintaProfesionala: 'bine', atitudineaPacienti: 'mediu', atitudineaColegi: 'bine', atitudineaPersonalNonMed: 'bine',
|
||||
utilizareSmartphone: 'bine', respectareaProgramului: 'bine', respectareaDressCode: 'mediu',
|
||||
completareaDocMed: true, perfectioneazaCunostinte: true,
|
||||
membruComitetCalitate: false, functieDeMonitor: false, inlocuiesteSuperiorul: false,
|
||||
categorieCalculata: 'cat_I',
|
||||
categorieAprobata: 'cat_I',
|
||||
observatii: 'Performanță constantă, recomandat pentru categoria I.',
|
||||
completedAt: new Date('2025-11-20'),
|
||||
},
|
||||
});
|
||||
console.log(' ✓ EvaluationCampaign demo — Imagistică (closed, 1 formular aprobat)');
|
||||
}
|
||||
|
||||
// Benefit cu vestimentație pentru emp1
|
||||
if (uniformaS && halatM) {
|
||||
await prisma.benefit.upsert({
|
||||
where: { employeeId: emp1.id },
|
||||
update: {},
|
||||
create: {
|
||||
employeeId: emp1.id,
|
||||
uniformaId: uniformaS.id,
|
||||
halatId: halatM.id,
|
||||
ticheteMasa: true,
|
||||
valoareTichet: 65,
|
||||
alimentatiePersonal: false,
|
||||
abonamentTel: 150,
|
||||
},
|
||||
});
|
||||
console.log(' ✓ Benefit demo (1) — Popescu Alexandru: uniformă + halat');
|
||||
}
|
||||
|
||||
console.log('\n✅ Seed complete.');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => { console.error(e); process.exit(1); })
|
||||
.finally(() => prisma.$disconnect());
|
||||
Reference in New Issue
Block a user