This document describes the native versioning feature implemented in the spp_dms
module for OpenSPP.
December 14, 2025
File: /home/user/openspp-modules-v2/spp_dms/models/dms_file_version.py
Key Features:
- Stores complete file content snapshots
- Tracks version number, checksum, size, and mimetype
- Records creator and creation timestamp
- Supports version comments for change descriptions
- Enforces only one current version per file via constraint
- Automatic checksum calculation using SHA512
Fields:
file_id: Link to parent file (cascade delete)version_number: Sequential version numbercontent: Binary content (stored as attachment)checksum: SHA512 hash for integritysize: File size in bytesmimetype: MIME typecreated_by_id: User who created the versioncreated_date: Creation timestampcomment: Description of changesis_current: Boolean flag for current version
Constraints:
- Unique version number per file (SQL constraint)
- Only one current version per file (Python constraint)
File: /home/user/openspp-modules-v2/spp_dms/models/dms_file.py
New Fields:
version_ids: One2many to versionscurrent_version_number: Computed fieldis_versioned: Enable/disable versioning flagversion_count: Computed count of versions
New Methods:
_create_new_version(comment=None): Create version snapshotaction_restore_version(version_id): Restore from versionaction_enable_versioning(): Enable versioning + create initial versionaction_disable_versioning(): Disable versioning (preserves history)action_view_versions(): Open version list viewwrite(): Override to auto-create versions on content changes
Auto-versioning Behavior:
- When
is_versioned=Trueand content changes, automatically creates a new version - Uses context flag
skip_auto_versionto prevent recursive version creation - Gracefully handles failures in auto-versioning (logs warning, doesn't block save)
Files:
/home/user/openspp-modules-v2/spp_dms/wizard/restore_version_wizard.py/home/user/openspp-modules-v2/spp_dms/wizard/restore_version_wizard_views.xml
Purpose: Provides user-friendly interface for version restoration
Features:
- Displays version metadata before restore
- Shows warning about restore process
- Validates version belongs to file
- Returns success notification
File: /home/user/openspp-modules-v2/spp_dms/views/dms_file_version_views.xml
Components:
- Tree view: List all versions with key metadata
- Form view: Detailed version information with restore button
- Action: Navigate to version list
File: /home/user/openspp-modules-v2/spp_dms/views/dms_file_views.xml
Enhancements:
- Header buttons: Enable/Disable versioning
- Stat button: Version count with quick access
- Notebook tab: Version history (visible when versioned)
- Inline version tree with restore action
File: /home/user/openspp-modules-v2/spp_dms/security/ir.model.access.csv
Access Rights:
| Model | Admin (Manager) | User |
|---|---|---|
spp.dms.file.version |
CRUD (1,1,1,1) | Read-only (1,0,0,0) |
spp.dms.restore.version.wizard |
CRUD (1,1,1,1) | CRUD (1,1,1,1) |
Rationale:
- Admins have full control over versions
- Users can read versions and restore them (via wizard)
- Version creation is handled automatically by the system
File: /home/user/openspp-modules-v2/spp_dms/tests/test_dms_file_version.py
Test Coverage (18 test cases):
test_01_enable_versioning: Basic enable functionalitytest_02_enable_versioning_twice_should_fail: Duplicate enable protectiontest_03_auto_create_version_on_content_change: Auto-versioningtest_04_manual_version_creation: Manual version with commenttest_05_version_without_versioning_enabled_should_fail: State validationtest_06_restore_version: Full restore workflowtest_07_restore_nonexistent_version_should_fail: Error handlingtest_08_restore_wrong_file_version_should_fail: Cross-file protectiontest_09_disable_versioning: Disable preserves historytest_10_disable_versioning_without_enabled_should_fail: State validationtest_11_only_one_current_version_constraint: Constraint enforcementtest_12_version_number_uniqueness: SQL constrainttest_13_version_checksum_calculation: Data integritytest_14_version_name_get: Display nametest_15_action_view_versions: Action structuretest_16_version_metadata: Metadata capturetest_17_restore_version_wizard: Wizard functionalitytest_18_no_auto_version_when_disabled: Conditional auto-versioning
- Open a file in form view
- Click "Enable Versioning" button in header
- System creates initial version (version 1)
- Version History tab becomes visible
- When versioning is enabled
- Any content change triggers auto-version
- New version created with comment "Auto-saved"
- Previous versions marked as non-current
file_record._create_new_version(comment="Before major changes")Method 1: From Version History Tab
- Open file form view
- Go to "Version History" tab
- Click "Restore" button on desired version
- System creates backup, restores content, creates new version
Method 2: From Version Form
- Open version record
- Click "Restore This Version" button (hidden for current version)
- Wizard opens with version details
- Click "Restore Version"
Method 3: Programmatically
file_record.action_restore_version(version_id)- Open file form view
- Click "Disable Versioning" button
- Confirm action
- Versioning stops, history preserved
- Sequential integers starting at 1
- Auto-incremented on each version creation
- Unique per file (SQL constraint)
- Binary content stored as Odoo attachment
- Full snapshot (not delta-based)
- Checksum for integrity verification
- Only one version can be
is_current=Trueper file - Automatically managed by
_create_new_version() - Enforced by Python constraint
- Create backup version of current state (auto-saved)
- Update file content with selected version content
- Create new version for restored content (marked as current)
Example: Restoring version 2 when on version 5
- Before: Versions 1, 2, 3, 4, 5 (current)
- After: Versions 1, 2, 3, 4, 5, 6 (backup), 7 (current, restored from v2)
- Each version stores full file content (not delta)
- Large files with many versions consume storage
- Consider periodic version pruning for large deployments
- Checksums use SHA512 for strong integrity
-- spp_dms_file_version table
CREATE TABLE spp_dms_file_version (
id SERIAL PRIMARY KEY,
file_id INTEGER REFERENCES spp_dms_file ON DELETE CASCADE,
version_number INTEGER NOT NULL,
content BYTEA, -- Stored as attachment
checksum VARCHAR,
size FLOAT,
mimetype VARCHAR,
created_by_id INTEGER REFERENCES res_users,
created_date TIMESTAMP,
comment TEXT,
is_current BOOLEAN,
UNIQUE(file_id, version_number)
);
-- Indexes
CREATE INDEX idx_file_version_file_id ON spp_dms_file_version(file_id);
CREATE INDEX idx_file_version_is_current ON spp_dms_file_version(is_current);
CREATE INDEX idx_file_version_checksum ON spp_dms_file_version(checksum);
CREATE INDEX idx_file_version_created_by ON spp_dms_file_version(created_by_id);
CREATE INDEX idx_file_version_created_date ON spp_dms_file_version(created_date);All version operations are logged at INFO level:
_logger.info("Created version %d for file: file_id=%s, checksum=%s", ...)
_logger.info("Enabled versioning for file: file_id=%s", ...)
_logger.info("Disabled versioning for file: file_id=%s", ...)
_logger.info("Restored file from version: file_id=%s, restored_version=%d, new_version=%d", ...)Failed auto-versioning logged at WARNING level (non-blocking).
- Version Comparison: Diff view between versions
- Version Pruning: Automatic cleanup of old versions
- Delta Storage: Store only changes for efficiency
- Version Tags: Named versions (e.g., "v1.0", "Release")
- Version Notes: Rich text comments with formatting
- Branching: Multiple version branches
- Access Control: Per-version permissions
- Version Search: Search content across versions
- Existing files start with
is_versioned=False - No automatic migration of existing files
- Admins must manually enable versioning per file
- No data loss risk (versioning is opt-in)
- v1.4.0 (2025-12-14): Initial versioning implementation
- spp.dms.file.version model
- Auto-versioning on content change
- Restore wizard
- Comprehensive test coverage (18 tests)
- Full UI integration
spp_dms/models/dms_file_version.pyspp_dms/wizard/__init__.pyspp_dms/wizard/restore_version_wizard.pyspp_dms/wizard/restore_version_wizard_views.xmlspp_dms/views/dms_file_version_views.xmlspp_dms/tests/test_dms_file_version.py
spp_dms/__init__.py(added wizard import)spp_dms/models/__init__.py(added dms_file_version import)spp_dms/models/dms_file.py(added versioning fields and methods)spp_dms/views/dms_file_views.xml(added versioning UI)spp_dms/security/ir.model.access.csv(added version model access)spp_dms/tests/__init__.py(added version test import)spp_dms/__manifest__.py(updated version to 1.4.0, added data files)
- ✅ Naming:
spp.dms.file.versionfollows conventions - ✅ Security: Three-tier access (admin CRUD, user read)
- ✅ Logging: Uses
_logger, no PII in logs - ✅ Error Handling: Specific exceptions (UserError, ValidationError)
- ✅ Testing: 18 test cases with @tagged decorator
- ✅ Documentation: Docstrings on all methods
- ✅ Odoo 19: Uses Command API patterns, proper constraints
- ✅ Performance: No
cr.commit()in loops - ✅ Code Quality: No
print(), no bareexcept: