Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Platform

Reference

Base model and ids

The columns every Cluster ERP table inherits from BaseModel, ULID primary keys stored as UUID, and the sequence codes such as PTY-000001 and SO-2026-00001.

This page lists the columns that every business table inherits from BaseModel in core/models.py, how primary keys are generated in core/ulid.py, and how business codes are generated in core/sequence.py. Soft delete semantics are explained on soft delete and deactivation; the change history on change history.

Scope and source

  • Defined in: src/cluster_erp/core/models.py (Base, BaseModel), src/cluster_erp/core/ulid.py (new_ulid), src/cluster_erp/core/sequence.py (Sequence, next_code, next_value, seed_sequences)
  • Count: 11 columns on BaseModel (read from the class body); 69 entity prefixes in ENTITY_PREFIXES and 19 entity types in YEARLY_ENTITIES (counted from the dictionary and set literals)

BaseModel columns

Base is the SQLAlchemy declarative base (src/cluster_erp/core/models.py:13-14). BaseModel is an abstract subclass that adds the columns below (src/cluster_erp/core/models.py:17-78).

Column Type Default Meaning Source
id UUID new_ulid() Primary key; a ULID stored as a UUID src/cluster_erp/core/models.py:20-24
tenant_id UUID, nullable, indexed none Not set by any code in core/, server/, or modules/ (searched with grep -rn tenant_id); its use is not determined from code src/cluster_erp/core/models.py:25-29
created_at timestamp with time zone now() on the server Creation time src/cluster_erp/core/models.py:30-34
created_by UUID, nullable none The acting user, stamped by the change-history hook on insert src/cluster_erp/core/models.py:35-38, src/cluster_erp/core/change_history.py:216-224
updated_at timestamp with time zone now(), refreshed on update Last change time src/cluster_erp/core/models.py:39-44
updated_by UUID, nullable none The acting user, stamped by the change-history hook on update src/cluster_erp/core/models.py:45-48, src/cluster_erp/core/change_history.py:225-226
deleted_at timestamp with time zone, nullable none NULL means active; a value means deactivated src/cluster_erp/core/models.py:49-57
deleted_by UUID, nullable none Who deactivated the record src/cluster_erp/core/models.py:58-61, src/cluster_erp/core/change_history.py:249-250
deactivation_reason String(64), nullable none A code from the deactivation reason catalogue src/cluster_erp/core/models.py:62-68
deactivation_note Text, nullable none Free text, required only when the reason demands it src/cluster_erp/core/models.py:69-72
version Integer 1 Row version for optimistic concurrency checks src/cluster_erp/core/models.py:73-78

version is not managed by SQLAlchemy's version counter: no __mapper_args__ declares one (searched with grep -rn __mapper_args__). Services raise it themselves; 140 lines in core/ and modules/ contain version + 1 (counted with grep -rn "version + 1"), for example src/cluster_erp/modules/bom/boms/services.py:152. check_version compares a client's version with the row and raises a 409 conflict on mismatch (src/cluster_erp/core/errors.py:411-430).

Some infrastructure tables subclass Base directly and do not carry these columns, for example core_change_history and audit_log (src/cluster_erp/core/change_history.py:78-98, src/cluster_erp/core/audit.py:15-69).

Primary keys

new_ulid() returns ULID().to_uuid() from the python-ulid package (src/cluster_erp/core/ulid.py:8-14, pyproject.toml:45). Its docstring states that the result is time-sortable and fits PostgreSQL's native uuid type (src/cluster_erp/core/ulid.py:9-13).

The change-history hook assigns the id of a new object before the flush, so the "create" history row carries the entity id (src/cluster_erp/core/change_history.py:273-297, src/cluster_erp/core/change_history.py:327).

Business codes

Business codes such as PTY-000001 come from the table core_sequences, one row per entity type (src/cluster_erp/core/sequence.py:112-120).

Column Type Meaning Source
entity_type String(50), unique The key, for example party src/cluster_erp/core/sequence.py:115
prefix String(10) The code prefix, for example PTY src/cluster_erp/core/sequence.py:116
current_value BigInteger The last issued number src/cluster_erp/core/sequence.py:117
padding Integer Zero-padding width src/cluster_erp/core/sequence.py:118
yearly_reset Boolean Whether the counter restarts each calendar year src/cluster_erp/core/sequence.py:119
last_reset_year Integer The year of the last reset src/cluster_erp/core/sequence.py:120

The functions:

Function Behaviour Source
seed_sequences(session) Adds a row for every entity type in ENTITY_PREFIXES that has none. Yearly types get padding 5, others padding 6. Runs at server start-up src/cluster_erp/core/sequence.py:123-151, src/cluster_erp/server/app.py:1058-1066
next_code(session, entity_type) Locks the row with SELECT... FOR UPDATE, increments it, and returns PREFIX-NNNNNN or, for yearly types, PREFIX-YYYY-NNNNN src/cluster_erp/core/sequence.py:154-171, src/cluster_erp/core/sequence.py:182-199
next_value(session, entity_type) Same locking and reset, returns (year, value, padding) without the prefix src/cluster_erp/core/sequence.py:174-179

A yearly counter resets to zero when the current UTC year differs from last_reset_year (src/cluster_erp/core/sequence.py:194-197). An unknown entity type raises ValueError (src/cluster_erp/core/sequence.py:188-192).

The 19 yearly entity types are sales_order, purchase_order, request_for_quotation, quotation, work_order, shipment, non_conformity, production_order, goods_movement, usage_decision, costing_analysis, purchase_request, purchase_rfq, purchasing_quotation, purchase_contract, mrp_run, customer_subscription, packing_list, and delivery_note (src/cluster_erp/core/sequence.py:88-108). The other 50 prefixes are not yearly.

Constraints and validation

  • core_sequences is excluded from the change history (src/cluster_erp/core/change_history.py:42-52).
  • The FOR UPDATE lock serialises concurrent callers on the same row, so codes have no gaps or collisions within a committed sequence of calls (src/cluster_erp/core/sequence.py:157-158, src/cluster_erp/core/sequence.py:185).

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
ULID new_ulid A time-sortable unique identifier, stored as a PostgreSQL uuid. ULID src/cluster_erp/core/ulid.py:8-14
business code next_code A human-readable record code such as PTY-000001 or SO-2026-00001. iş kodu src/cluster_erp/core/sequence.py:154-171
yearly sequence YEARLY_ENTITIES A code counter that restarts every calendar year and includes the year. yıllık sıra src/cluster_erp/core/sequence.py:88-108

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13