Documents
Beyond payslips and annual statements, HRMQ generates the standard Dutch HR letters from templates hosted in docudesk:
arbeidsovereenkomst— employment contractaanbiedingsbrief— offer letterwerkgeversverklaring— employer's statement (e.g. for a mortgage application)getuigschrift— reference/testimonial letter
HRMQ ships no template engine of its own — HrDocumentService
assembles the object references and calls docudesk's
DocumentService::generateDocument() same-instance, duck-typed via the
DI container, never over HTTP. Every generation attempt is recorded as a
GeneratedDocument — pending, generated, failed, or
skipped-no-docudesk.
Template selection: config first, discovery second, fail closed
For each document type, HRMQ first checks a configured template UUID
(SettingsService getter documents_template_{documentType}); if unset,
it falls back to discovering exactly one docudesk template tagged
namespace: hrmq with a matching category. Zero or multiple matches
fail the attempt closed with a diagnostic naming the ambiguity — nothing
is ever rendered against a guess.
Idempotency
At most one GeneratedDocument in pending or generated status exists
per idempotency key. The four letter types key on (contractId, documentType) — or (employeeId, documentType) when there's no
contract. Re-running generation for an already-documented contract is a
no-op; a failed or skipped-no-docudesk record never blocks a retry.
Generating documents
occ hrmq:documents:generate
occ hrmq:documents:generate --type=werkgeversverklaring --employee=<employee-id>
With no options, the default backlog is every permanent
EmploymentContract with writtenContract: true that has no active
arbeidsovereenkomst document yet. The letter types other than
arbeidsovereenkomst have no backlog semantics of their own and require
--employee. See Payslips for the
--type loonstrook and --type jaaropgaaf backlog behaviour.
A single guarded endpoint (POST /api/documents/generate) backs the
"Genereer arbeidsovereenkomst" action on EmploymentContractDetail and
the "Genereer PDF" action on PayslipDetail — resolved under the
caller's RBAC before any docudesk call, so an unknown or unauthorized
contract/payslip id returns 404 with nothing rendered.
Graceful degradation
When docudesk isn't installed, every attempt records
skipped-no-docudesk — no exception, exit code 0. Once docudesk is
installed, the next run supersedes the skip and renders normally. HRMQ
carries no info.xml or composer dependency on docudesk.
Machine-checked evidence
nl-contract-schriftelijk (severity recommended, source BW art.
7:655) flags a permanent contract with writtenContract: true that has no
active, generated arbeidsovereenkomst document referencing it —
temporary contracts are never flagged.
occ hrmq:rules:audit
Pages
GeneratedDocuments (under Personeel, labeled "Documenten") lists
documentType, status, employeeId, and generatedAt.
GeneratedDocumentDetail shows the record's fields, resolves the related
Employee and EmploymentContract, and offers the standard Files and Audit
Trail sidebar tabs.