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,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")
|
||||
}
|
||||
Reference in New Issue
Block a user