Documents
Render reusable document templates with placeholders, print, and barcode output.
Looking for forms?
The working forms module is backend/plugins/frontline_api/src/modules/form/; see Forms & Surveys. Core keeps only a legacy shell in modules/forms/ (the fieldsCombinedByContentType query plus fields/fieldsGroups helpers used by documents and properties).
The documents module
backend/core-api/src/modules/documents/:
graphql/schema.ts, queries.ts, mutations.ts
db/definitions/documents.ts, db/models/Documents.ts
customResolvers/document.ts
trpc/document.ts # documents.find, findOne, print
routes.ts # GET /print
blocksToHtml.ts, barcode.ts, utils.ts
A Document (documents.ts) has contentType (<plugin>:<module> like core:product), subType, name, code, content (editor blocks), replacer (placeholder map), tagIds, createdUserId. The GraphQL type also resolves createdUser and approvalLockState.
| Operation | Key arguments |
|---|---|
documents | contentType, subType, searchValue, dateFilters, userIds, tagIds, limit, cursor params; returns DocumentListResponse |
documentsDetail | _id! |
documentsTypes | none; returns types across every plugin's meta.documents |
documentsGetEditorAttributes | contentType!; returns the placeholder list for the editor |
documentsTotalCount | searchValue, contentType |
documentsProcess | _id, replacerIds, config; renders the template, returns an HTML string |
documentsSave | _id, contentType, subType, name!, content, replacer, code; upsert |
documentsRemove | _id! |
blocksToHtml and barcode render printable output; routes.ts serves GET /print for browser printing and download flows.
How a plugin contributes document types
A plugin contributes three pieces, and core dispatches on the contentType prefix (<plugin>:<module>):
meta/documents.ts:documents.typesentries{ label, contentType, subTypes? }.documentsTypesaggregates them from every active plugin. Sales declares{ label: 'Sales', contentType: 'sales:deal' }.- A
documents.editorAttributestRPC procedure:documentsGetEditorAttributescallssendTRPCMessage({ pluginName, module: 'documents', action: 'editorAttributes' })for non-core types. Sales implements it inmodules/sales/trpc/document.ts, returning deal attributes likeproductsInfo,totalAmount,stageName,assignedUsers, plus schema fields andcustomFieldsData.<fieldId>entries. - A
<module>.replaceContenttRPC procedure:documentsProcessroutes tomodule: <moduleName>, action: 'replaceContent'for non-core types (sales exposes it onmodules/sales/trpc/deal.ts). The procedure receivesreplacerIds,content,config, andcontentType, and returns rendered blocks;config.dateFormatcontrols date rendering (core defaults toYYYY-MM-DD).
Core's own declaration (src/meta/documents.ts) registers core:contact.customer, core:contact.company, core:product, core:user, core:broadcast, keeps editorAttributes/replaceContent as direct functions in the meta object, and builds product barcodes through meta/document/productReplacer.ts.
Permissions
manageDocuments (create/edit), removeDocuments (delete); documentsRead is always: true. Owner scope own tracks createdUserId.
Example
mutation SaveTemplate {
documentsSave(
contentType: "core:product"
name: "Product sheet"
code: "PROD-SHEET"
content: "<p>{{ name }} — {{ code }} — {{ unitPrice }}</p>"
) { _id }
}
query Render($id: String) {
documentsProcess(_id: $id, replacerIds: ["<productId>"], config: { dateFormat: "YYYY-MM-DD" })
}