The documents module in src/cluster_erp/modules/documents/ keeps controlled documents. A document has a type, may be linked to any record by entity type and id, and carries numbered versions. One version at a time is active; a new version becomes active either at once or through an approval chain. The module also provides the "documents attached to this record" capability that the web app finds by metadata.
At a glance¶
| Item | Value | Source |
|---|---|---|
| Package | src/cluster_erp/modules/documents/ |
folder |
| Module class | Module |
src/cluster_erp/modules/documents/module.py:18 |
name / version |
documents / 0.1.0 |
src/cluster_erp/modules/documents/module.py:19-25 |
| API prefix | /api/v1/documents (ModuleBase.api_prefix), with the app gate require_app("documents") |
src/cluster_erp/core/module_base.py:37-38, src/cluster_erp/modules/documents/module.py:48-63 |
| Sub-modules | 2: documents, settings; plus privacy_handlers.py |
folder listing |
| Tables | 3: documents_documents, documents_versions, documents_types |
__tablename__ in the two models.py files |
| Entities in metadata | 3 keys in entities |
src/cluster_erp/modules/documents/module.py:267-271 |
| Routes | 23 route decorators in the package | grep for @...router.get/post/put/patch/delete |
| Actions | 5 ActionMeta entries |
grep "ActionMeta(" in the package |
| Migrations | 7 files | files in migrations/versions/ whose text contains one of the three table names |
| Contracts | consumes 5, produces 6 | see the section "Events and queries" |
| Web app presence | Catalogue app documents; the capability record_documents used by the record documents panel |
src/cluster_erp/modules/app_store/catalog.json (entry documents), src/cluster_erp/modules/documents/module.py:228-230 |
| Desktop presence | generic widgets only | src/cluster_erp/desktop/modules/ has no documents folder |
What the module does¶
- Keeps documents with a business code, a type, a title, translations, a description, and an optional link to any record through
entity_typeandentity_id(src/cluster_erp/modules/documents/documents/models.py:26-49). - Keeps numbered versions per document; each version points to a file in the core attachments store (
src/cluster_erp/modules/documents/documents/models.py:52-87). Storage is described on attachments, notes, and storage. - Activates a version directly or after an approval chain decides, and marks the previous active version as superseded (
src/cluster_erp/modules/documents/documents/services.py:255-337). - Keeps document types with allowed file extensions and seeds a default set (
src/cluster_erp/modules/documents/settings/models.py:11-28,src/cluster_erp/modules/documents/module.py:37-46). - Answers other modules: the documents of a record, and the file behind a version (
src/cluster_erp/modules/documents/module.py:98-149).
Sub-modules¶
| Sub-module | Purpose | Main entities | Router | Source |
|---|---|---|---|---|
documents |
Documents and their versions | Document, DocumentVersion |
/documents, /versions |
src/cluster_erp/modules/documents/documents/routes.py:38-383 |
settings |
Document types | DocumentType |
/settings/document-types |
src/cluster_erp/modules/documents/settings/routes.py:20-107 |
Data model¶
| Entity | Table | Key fields | Notes | Source |
|---|---|---|---|---|
Document |
documents_documents |
code, document_type_id, entity_type, entity_id, title, translations, description, is_active, extra |
code unique; extra holds tenant field values |
src/cluster_erp/modules/documents/documents/models.py:26-49 |
DocumentVersion |
documents_versions |
code, document_id, version_no, status, valid_from, valid_to, attachment_id, change_notes, approved_by, approved_at |
version_no unique per document among live rows |
src/cluster_erp/modules/documents/documents/models.py:52-87 |
DocumentType |
documents_types |
code, name, translations, description, allowed_extensions, is_active |
code unique among live rows |
src/cluster_erp/modules/documents/settings/models.py:11-28 |
Document codes come from the sequence document and version codes from document_version (src/cluster_erp/modules/documents/documents/services.py:110-111, src/cluster_erp/modules/documents/documents/services.py:208). A new version takes the next number after the highest live version (src/cluster_erp/modules/documents/documents/services.py:200-221).
Screens and metadata¶
get_metadata declares a sidebar group Documents (Turkish Belgeler) with a list section Documents and a settings section with the tab document_type (src/cluster_erp/modules/documents/module.py:232-265). The module publishes provides={"record_documents": {...}}, so the web client finds the record documents feature by capability, not by module name (src/cluster_erp/modules/documents/module.py:228-230). The record documents panel and the viewer are described on records, documents, viewer, and share.
Status and actions¶
A version is draft, active, superseded, or withdrawn (src/cluster_erp/modules/documents/documents/services.py:200-337). The diagram shows the version and approval chain.
flowchart TD
V["DocumentVersion draft"] --> S["POST /versions/id/submit-approval"]
S --> Q{"GetApprovalChainForAction: document_version, activate"}
Q -->|no chain| ACT["DocumentVersionService.activate"]
Q -->|chain| SUB["SubmitForApproval"]
SUB --> AC["ApprovalCompleted event"]
AC -->|final_status approved| ACT
AC -->|other status| STAY["version stays draft"]
V --> D["POST /versions/id/activate"]
D --> ACT
ACT --> SUP["previous active version: superseded"]
ACT --> NEW["this version: active, approved_by, approved_at"]
NEW --> W["POST /versions/id/withdraw: withdrawn"]In prose:
- A version starts as a draft. Only a draft can be edited or deleted (
src/cluster_erp/modules/documents/documents/services.py:223-253). - Submit for Approval asks
GetApprovalChainForActionfor the resourcedocument_versionand the actionactivate. Without a chain, it activates the version at once. With a chain, it callsSubmitForApprovaland the version stays a draft (src/cluster_erp/modules/documents/documents/routes.py:302-351). - When the auth module publishes
ApprovalCompletedfor adocument_versionwithfinal_statusapproved, the handler reads the decider throughGetApprovalRequestByIdand activates the version. Any other final status leaves it as a draft (src/cluster_erp/modules/documents/module.py:151-204). - Activate needs a draft with an attachment. It marks the current active version
superseded, sets the new oneactivewith the approver and time, and emitsDocumentVersionSupersededandDocumentVersionActivated(src/cluster_erp/modules/documents/documents/services.py:255-295,src/cluster_erp/modules/documents/documents/services.py:316-337). - Withdraw moves an active version to
withdrawn(src/cluster_erp/modules/documents/documents/services.py:297-314).
| Action | Label | Condition | Endpoint | Permission | Source |
|---|---|---|---|---|---|
view_version |
View | none | GET /versions/{id}/file |
document_version.read |
src/cluster_erp/modules/documents/documents/metadata.py:266-272, src/cluster_erp/modules/documents/documents/routes.py:240-268 |
submit_approval |
Submit for Approval | status is draft |
POST /versions/{id}/submit-approval |
document_version.submit_approval |
src/cluster_erp/modules/documents/documents/metadata.py:273-280 |
activate |
Activate | status is draft |
POST /versions/{id}/activate |
document_version.activate |
src/cluster_erp/modules/documents/documents/metadata.py:281-288, src/cluster_erp/modules/documents/documents/routes.py:354-369 |
withdraw |
Withdraw | status is active |
POST /versions/{id}/withdraw |
document_version.withdraw |
src/cluster_erp/modules/documents/documents/metadata.py:289-296, src/cluster_erp/modules/documents/documents/routes.py:371-383 |
seed_defaults |
Seed Defaults | none | POST /settings/document-types/seed-defaults |
document_type.seed |
src/cluster_erp/modules/documents/settings/metadata.py:30-36, src/cluster_erp/modules/documents/settings/routes.py:52-58 |
Approval chains are described on approvals and approval chains.
Business rules¶
- A version without an attachment cannot be activated (
src/cluster_erp/modules/documents/documents/services.py:269-274). - At most one version per document is active; activation supersedes the previous one (
src/cluster_erp/modules/documents/documents/services.py:316-337). - Tenant field values in
extraare validated on create (src/cluster_erp/modules/documents/documents/services.py:110-115). See tenant fields. GET /versions/{id}/filestreams the file of the version from storage and answers not found when the file is missing (src/cluster_erp/modules/documents/documents/routes.py:240-268).- Deleting a document or a draft version sets
deleted_at; bulk delete uses the core soft-delete helper (src/cluster_erp/modules/documents/documents/services.py:150-162,src/cluster_erp/modules/documents/documents/services.py:241-253).
Events and queries¶
The module consumes 5 contracts and produces 6, counted as on the fleet module page. The full list is on the contracts reference.
| Direction | Contract class | Handler or emitter | Source |
|---|---|---|---|
| subscribes | ApprovalCompleted |
_handle_approval_completed |
src/cluster_erp/modules/documents/module.py:74-77, src/cluster_erp/modules/documents/module.py:151-204 |
| answers query | ResolveDocumentVersionFile |
_handle_resolve_version_file |
src/cluster_erp/modules/documents/module.py:78, src/cluster_erp/modules/documents/module.py:127-149 |
| answers query | GetDocumentsForEntity |
_handle_get_documents_for_entity |
src/cluster_erp/modules/documents/module.py:79, src/cluster_erp/modules/documents/module.py:98-125 |
| subscribes | CollectSubjectData, AnonymiseSubject |
privacy_handlers.py |
src/cluster_erp/modules/documents/module.py:81-96 |
| emits | DocumentCreated |
DocumentService.create |
src/cluster_erp/modules/documents/documents/services.py:119-126 |
| emits | DocumentVersionActivated, DocumentVersionSuperseded |
activate, _supersede_active |
src/cluster_erp/modules/documents/documents/services.py:284-293, src/cluster_erp/modules/documents/documents/services.py:328-337 |
| queries | GetApprovalChainForAction, SubmitForApproval, GetApprovalRequestById |
submit route, approval handler | src/cluster_erp/modules/documents/documents/routes.py:325-350, src/cluster_erp/modules/documents/module.py:178-183 |
ResolveDocumentVersionFile serves the intranet delivery download, and GetDocumentsForEntity serves the intranet "my documents" list (src/cluster_erp/modules/intranet/delivery/routes.py:143-150, src/cluster_erp/modules/intranet/my_documents/services.py:17-28).
Permissions¶
| Permission key | Used by | Source |
|---|---|---|
document.read, .create, .update, .delete, .bulk_delete |
documents | src/cluster_erp/modules/documents/documents/routes.py:59-180 |
document_version.read, .create, .update, .delete, .submit_approval, .activate, .withdraw |
versions | src/cluster_erp/modules/documents/documents/routes.py:194-377 |
document_type.read, .create, .update, .delete, .bulk_delete, .seed |
document types | src/cluster_erp/modules/documents/settings/routes.py:23-102 |
Privacy, retention, and history¶
privacy_handlers.py lists the documents filed against a person (entity type party). Anonymisation is an operator decision (include_documents); when included, the documents are retired, their title and description replaced, and the version files reported for removal (src/cluster_erp/modules/documents/privacy_handlers.py:1-9). See privacy and data-subject requests. The module has no retention_handlers.py. Changes are captured by change history.
Analytics facts¶
This module has no analytics_facts.py.
Settings and reference data¶
Document types are reference data. On start the module seeds the default set through DocumentTypeService.seed_defaults, and Seed Defaults runs it on demand (src/cluster_erp/modules/documents/module.py:37-46, src/cluster_erp/modules/documents/settings/routes.py:52-58). seed_data.py holds 15 default types, for example form, instruction, procedure, and technical_drawing, each with an English name and a Turkish translation (src/cluster_erp/modules/documents/settings/seed_data.py:1-113).
Tests¶
Found by name (web-client/_e2e_doc*.mjs, web-client/_e2e_record_docs.mjs) and by a grep for documents in tests/*.py.
| Kind | File | Covers |
|---|---|---|
| e2e | web-client/_e2e_doc_version.mjs |
versions and activation |
| e2e | web-client/_e2e_doc_viewer.mjs |
the document viewer |
| e2e | web-client/_e2e_record_docs.mjs |
documents attached to records |
| e2e | web-client/_e2e_intranet_mydocs.mjs |
the intranet lens over documents |
| pytest | tests/test_erasure_ledger.py, tests/test_metadata_driven_rules.py, tests/test_secure_defaults.py, tests/test_order_shipment_chain.py |
files that mention documents |
Discrepancies¶
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| document | Document |
A controlled document with a type, optionally linked to a record. | Belge | src/cluster_erp/modules/documents/documents/models.py:26 |
| document version | DocumentVersion |
One numbered revision of a document with its file and status. | Belge sürümü | src/cluster_erp/modules/documents/documents/models.py:52 |
| document type | DocumentType |
Reference row that classifies documents and lists allowed file extensions. | Belge türü | src/cluster_erp/modules/documents/settings/models.py:11 |
Related pages¶
- Modules index
- Records, documents, viewer, and share
- Attachments, notes, and storage
- Intranet my documents
- API reference for this module
Verified against commit 92f8051 on 2026-11-13.