documents
Filed documents with title, category, issue and expiry date and one file, shown on the records they are linked to. A document is visible only through a linked record the viewer can read, and only while the record that owns its file is readable too, so linking never widens access to the file. Documents arrive through capture rules (a file saved into a configured field) or through the system-only import for migrations, which keeps the caller’s id, refuses an id another record already uses, and stamps createdAt with the import time (issuedAt carries the business date; the event store does not backdate); users link, unlink, edit and delete them. Read a record’s documents with for-record, count expired and expiring ones with expiry-overview, list them with an expiry state and an expiring filter with overview (the documents list screen), and add the documents tab to a record screen with documentsTab().
Quick example
Section titled “Quick example”From recipes-documents — the smallest working mount:
// kumiko-feature-version: 1// Documents Sample//// A `contract` entity with a file field becomes a document source without knowing about the// documents feature: the capture rule below files every saved contract file as a document of// category "contract" and links it to the contract and to its property. Who sees the document// follows from who may read the linked records and the contract that owns the file.
import { createDocumentsFeature } from "@cosmicdrift/kumiko-bundled-features/documents";import { createEntity, createFileField, createTextField, defineEntityCreateHandler, defineEntityDetailHandler, defineEntityListHandler, defineEntityUpdateHandler, defineFeature,} from "@cosmicdrift/kumiko-framework/engine";
const text = () => createTextField({ personal: false, reason: "is_business_data", required: true, maxLength: 120 });
export const propertyEntity = createEntity({ table: "read_sample_documents_properties", fields: { name: text() }, access: { read: { PropertyReader: "all", TenantAdmin: "all" } },});
export const contractEntity = createEntity({ table: "read_sample_documents_contracts", fields: { name: text(), propertyId: text(), file: createFileField() }, access: { read: { ContractReader: "all", TenantAdmin: "all" } },});
const access = { roles: ["PropertyReader", "ContractReader", "TenantAdmin"] };
export const propertyManagement = defineFeature("property-management", (r) => { r.requires({ features: ["files", "documents"] }); r.entity({ name: "property", ...propertyEntity }); r.entity({ name: "contract", ...contractEntity }); for (const [name, entity] of [ ["property", propertyEntity], ["contract", contractEntity], ] as const) { r.writeHandler(defineEntityCreateHandler(name, entity, { access })); r.writeHandler(defineEntityUpdateHandler(name, entity, { access: { roles: ["TenantAdmin"] } })); r.queryHandler(defineEntityListHandler(name, entity, { access })); r.queryHandler(defineEntityDetailHandler(name, entity, { access })); }});
export const documents = createDocumentsFeature({ categories: ["contract", "insurance"], linkTargets: ["property", "contract"], access, capture: [ { entity: "contract", field: "file", category: "contract", title: (row) => `Contract ${String(row["name"])}`, links: (row) => [{ entityType: "property", entityId: String(row["propertyId"]) }], }, ],});📄 On GitHub: samples/recipes/documents/src/feature.ts
How it fits
Section titled “How it fits”What this feature needs to run (Requires, top) and the write commands this feature provides (Provides, bottom).
flowchart TB
n_documents["documents"]
subgraph how_reqs["Requires"]
n_files["files"]
end
subgraph how_provides["Provides"]
n_cmd_documents_write_filed_document_create(["create"])
n_cmd_documents_write_filed_document_delete(["delete"])
n_cmd_documents_write_filed_document_update(["update"])
n_cmd_documents_write_import(["import"])
n_cmd_documents_write_link(["link"])
n_cmd_documents_write_unlink(["unlink"])
end
n_files --> n_documents
n_documents --> n_cmd_documents_write_filed_document_create
n_documents --> n_cmd_documents_write_filed_document_delete
n_documents --> n_cmd_documents_write_filed_document_update
n_documents --> n_cmd_documents_write_import
n_documents --> n_cmd_documents_write_link
n_documents --> n_cmd_documents_write_unlink
Provides — write commands this feature registers (dispatch them through the command bus):
documents:write:filed-document:createdocuments:write:filed-document:deletedocuments:write:filed-document:updatedocuments:write:importdocuments:write:linkdocuments:write:unlink
Getting started
Section titled “Getting started”Start with recipes-documents for a step-by-step walkthrough with runnable code and integration tests.
Dependencies
Section titled “Dependencies”- Requires:
files - Activation: always on (not toggleable)