Document Sequences & Numbering
Developer guide for the Enezaframework. Part of the Developer documentation.
Table of Contents
- Overview
- How
SequenceMixinWorks SequenceConfigReference- Using a Sequence-Enabled Base
- Failure Modes
- Change History
- Related Guides
Overview
Master and document records get human-readable business numbers (CUST-0001, INV-00001) from a
SequenceMixin that reads a SequenceConfig row and increments its counter on the first save.
Base models that include the mixin: EnezaMaster and EnezaDocument. Base models that do not:
EnezaTransactionalTable, EnezaSetup, EnezaParameter, EnezaGeneral, EnezaMasterDetails, and
EnezaDocumentLines.
sequenceDiagram
participant App
participant Model as Model.save()
participant Mixin as SequenceMixin
participant Config as SequenceConfig
App->>Model: save() new instance
Model->>Mixin: generate_sequence()
Mixin->>Config: filter(documents=document_name, active=True).first()
Config-->>Mixin: config (prefix, delimiter, number_length, nextnumber)
Mixin->>Config: nextnumber += 1; save()
Mixin-->>Model: "PREFIX-0001"
Model->>Model: set sequence_field_name; save()How SequenceMixin Works
Defined in enezaframework/services/Mixin.py. On the first save of a new instance (no existing row),
it fills the sequence field if it is empty:
class SequenceMixin:
def generate_sequence(self):
sequence_field_name = self.sequence_field_name
document_name = self.document_name
config = SequenceConfig.objects.filter(documents=document_name, active=True).first()
...
return f"{config.prefix}{config.delimiter}{formatted_number}"
def save(self, *args, **kwargs):
sequence_field_name = self.sequence_field_name
currentobject = self.__class__.objects.filter(pk=self.pk)
if not currentobject:
if not getattr(self, sequence_field_name, None):
setattr(self, sequence_field_name, self.generate_sequence())
super().save(*args, **kwargs)
Two attributes are required on the concrete model:
| Attribute | Purpose |
|---|---|
sequence_field_name |
Name of the field that stores the generated number (for example "vendorno"). |
document_name |
Value matched against SequenceConfig.documents to select the configuration. |
number_length is applied with zero-padding, and the result is
prefix + delimiter + zero-padded number.
SequenceConfig Reference
Defined in enezaframework/models/enezasequence.py. It is a concrete EnezaModel, so it has the
standard UUID key, audit fields, and history.
| Field | Type | Description |
|---|---|---|
documents |
CharField |
The document/model key; matched against the model's document_name. |
prefix |
CharField |
Prefix such as "IV" or "CR". |
delimiter |
CharField |
Separator, default "-". |
number_length |
PositiveIntegerField |
Zero-pad width, default 4. |
active |
BooleanField |
Whether this config is used. |
nextnumber |
PositiveIntegerField |
The next number to issue, default 1. |
modules |
CharField |
Module/choices grouping for the configuration. |
entity |
ForeignKey to Entity |
Entity the configuration belongs to, nullable. |
Constraint: UniqueConstraint(fields=["documents", "active"]) named unique_model_name_active. In
practice, only one active config per document key can exist.
SequenceConfig can be maintained through the admin (see the System Administrator module) or created
programmatically:
from enezaframework.models.enezasequence import SequenceConfig
config = SequenceConfig.objects.create(
documents="VendorTable",
prefix="VEND",
delimiter="-",
number_length=4,
active=True,
nextnumber=1,
modules="accountpayable",
)
Using a Sequence-Enabled Base
Declare the two mixin attributes on the model, plus the field that stores the number.
# accountpayable/models/vendortable.py
from django.db import models
from enezaframework.models import EnezaMaster
class VendorTable(EnezaMaster):
sequence_field_name = "vendorno"
document_name = "VendorTable"
vendorno = models.CharField(
max_length=50,
unique=True,
blank=True,
verbose_name="Vendor No.",
)
searchname = models.CharField(
max_length=255,
verbose_name="Search Name",
)
class Meta:
verbose_name = "Vendor"
verbose_name_plural = "Vendors"
def __str__(self):
return self.searchname
Leave the sequence field empty (or blank=True, not required in forms) so the mixin fills it on the
first save.
Failure Modes
| Symptom | Cause |
|---|---|
ValueError: No Sequence configuration found for model '...' |
No active SequenceConfig with documents == self.document_name. |
AttributeError: 'X' object has no attribute 'sequence_field_name' |
The model inherits a sequence base but does not declare sequence_field_name. |
| Number not generated | The model is not using a sequence base, or the field already has a value, or the row is not new. |
Always create and activate the matching SequenceConfig before saving the first record.
The user is solely responsible for testing and validating sequence generation in their environment; the developer/agent does not run the application.
Change History
Dated changes that affect this guide, taken from
docs/Release note/CHANGELOG.md and the release notes. For the
complete history, see the changelog.
| Date | Version | What changed |
|---|---|---|
| 2026-06-27 | — | HistoricalSequenceConfig model gained document relationships and updated field choices (release note "Workflow Type Registry Developer Guide and various migrations"). |
No dated changes to SequenceMixin or SequenceConfig's core fields appear in the changelog for the
0.2.7–0.2.19 window.