Files
Danil Suhomlinov 33800292aa 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>
2026-06-08 17:42:45 +03:00
..

Șabloane DOCX — Control medical (docxtemplater)

Сюда кладутся .docx-шаблоны (вы их делаете в Word, вставляя плейсхолдеры). Движок генерации загружает нужный файл, подставляет данные и отдаёт готовый документ.

  • Где лежат: apps/api/templates/docx/
  • Движок: docxtemplater + pizzip (уже в зависимостях).
  • Синтаксис плейсхолдеров: фигурные скобки {...}.

Имена файлов (фиксированные — по ним движок находит шаблон)

Файл Источник (регламент) На что генерируется
anexa-3.docx Anexa 3 — Fișa de solicitare 1 на группу (карту риска)
anexa-4.docx Anexa 4 — Fișa de evaluare a riscurilor 1 на карту риска (tipFisa = STANDARD)
anexa-4a.docx Anexa 4A — muncă la distanță/digital 1 на карту риска (tipFisa = DISTANTA_DIGITAL)
anexa-4b.docx Anexa 4B — supliment radiații 1 на каждого облучаемого сотрудника
anexa-6.docx Anexa 6 — Fișa de aptitudine 1 на каждого сотрудника (вердикт врача)

Синтаксис docxtemplater (шпаргалка для Word)

Нужно Пишете в шаблоне
Простое значение {numePrenume}
Таблица/список (повтор строки) в строке таблицы: {#chimici}{denumire} {vlep}{/chimici}
Чек-бокс {cbEchipa} — движок подставит или
Условный блок {#esteRadiatii}…текст…{/esteRadiatii} (показать, если true); {^esteRadiatii}…{/esteRadiatii} (если false)

⚠️ Циклы в таблицах: {#name} ставится в первую ячейку строки-шаблона, {/name} — в последнюю ячейку той же строки. Эта строка повторится для каждого элемента массива.

⚠️ Не оставляйте «висячих» { без пары — docxtemplater упадёт. Один плейсхолдер не должен быть разорван форматированием Word (выделите и наберите его одним стилем).


Контракт данных по приложениям

Ниже — точная форма data, которую движок передаёт в каждый шаблон, и откуда берётся каждое значение. Вставляйте в .docx плейсхолдеры именно с этими именами.

Общее (есть во всех)

Плейсхолдер Значение Источник
{unitatea} Medpark International Hospital константа
{idno} 1003600035476 константа
{adresa} str. Nicolae Testemițanu 29, Chișinău константа
{dataCompletarii} дата генерации (DD.MM.YYYY) now()

Контекст генерации batch

Эти поля заполняются в modal-е «Inițiere control medical» и используются в Anexa 3 / 4 / 4A / 4B: {telefon} {fax} {email} {solicitant} {functia}.

{telefonFiliala} берётся из WorkplaceRiskCard.telefonFiliala.


anexa-3.docx — Fișa de solicitare

Уровень: группа сотрудников (по карте риска).

Шапка: {unitatea} {idno} {adresa} {telefon} {fax} {email} {filiala} {adresaFiliala} {telefonFiliala} Подвал: {dataCompletarii} {solicitant} {functia}

Таблица сотрудников — строку оборачиваете {#angajati}{/angajati}:

Плейсхолдер (в строке) Значение Источник
{nr} порядковый № счётчик
{numePrenume} Фамилия Имя Employee.nume/prenume
{anNastere} год рождения Employee.dataNasterii
{idnp} IDNP Employee.idnp
{tipExamen} тип контроля (текст) Batch.tip
{ocupatieCorm} occupația CORM EmployeeMedicalProfile.ocupatieCorm
{caem} CAEM (diviziune) WorkplaceRiskCard.caemDiviziune
{numarLoc} № locului de muncă WorkplaceRiskCard.numarulLoculuiDeMunca
{factorRisc} факторы (через запятую) WorkplaceRiskCard.exposures[].denumire

anexa-4.docx — Fișa de evaluare a riscurilor profesionale

Уровень: карта риска (WorkplaceRiskCard).

Шапка: {unitatea} {adresa} {telefon} {fax} {email} {filiala} {adresaFiliala} {telefonFiliala} {caem2} {cormSubgrupa} {directiaSectia} {numarLoc} {caemDiviziune} {numarLucratori} {clasa}

Чек-боксы описательного блока (/) — источник WorkplaceRiskCard.evaluareDetalii: {cbEchipa} {cbSchimbNoapte} {cbPauze} {cbInfectare} {cbElectrocutare} {cbTensiuneInalta} {cbInecare} {cbAsfixiere} {cbStrivire} {cbTaiere} {cbIntepare} {cbLovire} {cbMuscatura} {cbMicrotraumatisme} {cbConduceMasina} {categorieConducere} {cbUtilajeIntrauzinal} {spatiuL} {spatiul} {spatiuH} {cbSuprafVerticala} {cbSuprafOrizontala} {cbSuprafOblica} {cbMuncaIzolare} {cbMuncaInaltime} {cbMuncaMiscare} {cbPozitieOrtostatica} {cbPozitieAsezat} {cbPozitieAplecata} {cbPozitieMixta} {cbPozitieFortata} {cbColoanaCervicala} {cbColoanaToracala} {cbColoanaLombara} {cbManipRidicare} {cbManipCoborare} {cbManipImpingere} {cbManipTragere} {cbManipPurtare} {cbManipDeplasare} {greutateMaxima} {cbVizuale} {cbAuditive} {cbNeuropsihice} Microclimat/iluminat: {cbMicroclimatInterior} {cbMicroclimatExterior} {cbCaloriceRece} {cbCaloriceCalda} {cbIluminatSuficient} {cbIluminatInsuficient} {cbIluminatNatural} {cbIluminatArtificial} {cbIluminatMixt}

Факторные таблицы — каждая = свой цикл; источник WorkplaceRiskCard.exposures (сгруппированы по tip): Секции также имеют чек-боксы наличия данных: {cbChimici}/{cbChimiciNu}, {cbPulberi}/{cbPulberiNu}, {cbBiologici}/{cbBiologiciNu}, {cbZgomot}/{cbZgomotNu}, {cbVibratii}/{cbVibratiiNu}, {cbCampEM}/{cbCampEMNu}, {cbOptice}/{cbOpticeNu}.

Цикл Поля строки
{#chimici}…{/chimici} {denumire} {cas} {einecs} {timp} {vep} {vlep} {caracteristici}
{#pulberi}…{/pulberi} те же
{#biologici}…{/biologici} {denumire} {clasificare} {note}
{#zgomot}…{/zgomot} {denumire} {timp} {vep} {vlep} {caracteristici}
{#vibratii}…{/vibratii} {denumire} {zona} {timp} {vep} {vlep} {caracteristici}
{#campEM}…{/campEM} {denumire} {zona} {timp} {vep} {vlep} {caracteristici}
{#optice}…{/optice} {denumire} {zona} {timp} {vep} {vlep} {caracteristici}

Радиация ионизирующая (источник WorkplaceRiskCard.radiatii*): {cbRadiatii} {cbRadiatiiNu} {radGrupa} {radSurse} {radTipExpunere} {radAparatura} {radMasuri}

Подвал: {protectieColectiva} {protectieIndividuala} {echipament} {cbVestiar} {cbChiuveta} {cbWc} {cbDus} {cbSalaMese} {cbRecreere} {observatii}


anexa-4a.docx — muncă la distanță / platforme digitale

Уровень: карта риска (tipFisa = DISTANTA_DIGITAL). Без факторных таблиц.

Шапка: {unitatea} {adresa} {telefon} {fax} {email} {filiala} {adresaFiliala} {telefonFiliala} {caem2} {cormSubgrupa} {directiaSectia} {numarLoc} {caemDiviziune} {clasa} Тело: {cbEchipa} {oreZi} {schimburi} {cbSchimbNoapte} {cbPauze} {cbLucruMonitor} {cbPlatformeDigitale} {cbConduceMasina} {categorieConducere} {operatiuni} {cbDeplasari} {deplasariDescriere} {cbManipRidicare}…{cbManipDeplasare} {greutateMaxima} {cbVizuale} {cbAuditive} {cbNeuropsihice} {alteRiscuri} Подвал: {dataCompletarii}


anexa-4b.docx — Supliment radiații ionizante

Уровень: один облучаемый сотрудник. Источник: EmployeeMedicalProfile + overexposures.

Шапка: {unitatea} {adresa} {telefon} {fax} {email} {filiala} {adresaFiliala} {telefonFiliala} {caem2} Контекст места: {cormSubgrupa} {directiaSectia} {numarLoc} {caemDiviziune} Сотрудник: {numePrenume} {idnp}

Плейсхолдер Значение Источник
{cbRadiatii} ☑/☐ profile.expusRadiatiiIonizante
{dataIntrarii} дата profile.dataIntrarii
{expAnterioaraPerioada} период profile.expunereAnterioaraPerioda
{expAnterioaraAni} лет profile.expunereAnterioaraAni
{dozaExterna} mSv profile.dozaCumulataExternaMsv
{dozaInterna} mSv profile.dozaCumulataInternaMsv
{dozaTotala} mSv (ext+int) вычисляется

Циклы supraexpuneri (источник overexposures, разделены по fel):

  • {#supraexpExceptionale} {tipExpunere} {data} {doza} {/supraexpExceptionale}
  • {#supraexpAccidentale} {tipExpunere} {data} {doza} {/supraexpAccidentale}

Подвал: {dataCompletarii}


anexa-6.docx — Fișa de aptitudine

Уровень: один сотрудник. Источник: MedicalCheckup (вердикт врача).

Шапка/сотрудник: {unitatea} {adresa} {numePrenume} {idnp} {anNastere} {ocupatieCorm} {departament} {caemDiviziune} {numarLoc} {factorRisc} {tipExamen} {dataCompletarii} Вердикт (чек-боксы): {cbApt} {cbAptAdaptare} {cbAptConditionat} {cbInaptTemporar} {cbInapt} {recomandari} {valabilPanaLa} {semnatDe}. {valabilPanaLa} и {semnatDe} заполняет medic_familie при finalizarea controlului.


Статус движка

DocxTemplateService.render(type, data) уже подключён. Если файл apps/api/templates/docx/anexa-*.docx существует, bulk-generation использует официальный .docx-шаблон через docxtemplater; старый docx+tiptap-JSON renderer остаётся fallback-ом.