diff --git a/bemade_document_versions/__init__.py b/bemade_document_versions/__init__.py
index 899bcc9..a22f984 100644
--- a/bemade_document_versions/__init__.py
+++ b/bemade_document_versions/__init__.py
@@ -1,2 +1,3 @@
from . import models
+from . import wizard
diff --git a/bemade_document_versions/__manifest__.py b/bemade_document_versions/__manifest__.py
index 20d23ae..80c37f6 100644
--- a/bemade_document_versions/__manifest__.py
+++ b/bemade_document_versions/__manifest__.py
@@ -25,7 +25,11 @@
'website': 'https://www.bemade.org',
'license': 'OPL-1',
'depends': ['documents', 'mail'],
- 'data': [],
+ 'data': ['security/ir.model.access.csv',
+ 'data/document_revision_data.xml',
+ 'views/document_views.xml',
+ 'wizard/document_revision_wizard.xml',
+ ],
'installable': True,
'auto_install': False,
}
diff --git a/bemade_document_versions/data/document_revision_data.xml b/bemade_document_versions/data/document_revision_data.xml
index 1de5b39..611fb26 100644
--- a/bemade_document_versions/data/document_revision_data.xml
+++ b/bemade_document_versions/data/document_revision_data.xml
@@ -1,12 +1,18 @@
-
+
Default Document Revision Sequence
document.revision
standard
DOC/
2
+
+ 0
+ Revise
+
+ documents.revision
+
\ No newline at end of file
diff --git a/bemade_document_versions/models/__init__.py b/bemade_document_versions/models/__init__.py
index 69e0c5b..5648a48 100644
--- a/bemade_document_versions/models/__init__.py
+++ b/bemade_document_versions/models/__init__.py
@@ -1,3 +1,4 @@
-from . import document_revision
from . import document
-from . import document_sequence
+from . import document_revision
+from . import document_revision_sequence
+from . import workflow
diff --git a/bemade_document_versions/models/document.py b/bemade_document_versions/models/document.py
index 0e6684c..8ee2940 100644
--- a/bemade_document_versions/models/document.py
+++ b/bemade_document_versions/models/document.py
@@ -3,68 +3,68 @@ from odoo.exceptions import ValidationError
class Document(models.Model):
- _inherit = ['documents.document']
+ _inherit = 'documents.document'
current_revision_id = fields.Many2one('documents.revision', 'Current Revision')
revision_ids = fields.One2many('documents.revision', 'document_id')
- revision_sequence = fields.Many2one('documents.revision.sequence', 'document_id',
+ revision_sequence = fields.Many2one('ir.sequence',
'Revision Sequence', )
- track_revisions = fields.Boolean(default=False)
- # set copy=False for number_next
- number_next = fields.Integer(string='Next Number', required=True, default=1,
- copy=False, help="Next number of this sequence")
- @api.constrains('revision_ids', 'revision_sequence', 'track_revisions')
- def constrain_revisions(self):
- for rec in self:
- revision_fields = [rec.revision_ids, rec.revision_sequence,
- rec.track_revisions]
- if any(revision_fields) and not all(revision_fields):
- raise ValidationError(_('A revision sequence must be selected to track'
- ' revisions.'))
-
- def write(self, vals):
- super().write(vals)
- if self.track_revisions and 'track_revisions' in vals \
- and not vals['track_revisions']:
- raise ValidationError(_('Revision tracking cannot be disabled after it has'
- 'been turned on for a document.'))
- self._check_revision_fields()
-
- @api.model_create_multi
- def create(self, vals):
- res = super().create(vals)
- for rec in res:
- rec._check_revision_fields()
- return res
-
- def _check_revision_fields(self):
- """ Helper method to check that all three fields `track_revisions`,
- `revisions_sequence` and `revision_ids` are properly set if any one of them is set.
- :return: None
- """
- self.ensure_one()
- revision_fields = [self.revision_ids, self.revision_sequence,
- self.track_revisions]
- if any(revision_fields) and not all(revision_fields):
- self._set_up_revisions()
-
- def _set_up_revisions(self):
+ def set_up_revisions(self, initial_revision_name: str = None,
+ sequence_prefix: str = None,
+ sequence_suffix: str = None, sequence_padding: int = None):
"""
Helper method to set up revisions, no matter how we got into tracking them.
- :return: None
+ :param initial_revision_name str: The name to give to the initial revision. By
+ default, this will be the next name from the sequence.
+ :param sequence_prefix: The prefix to use for the new revision sequence.
+ :param sequence_suffix: The suffix to use for the new revision sequence.
+ :return None:
"""
self.ensure_one()
- self.track_revisions = True # may already be true, but no matter
- self.revision_sequence = self.revision_sequence or self.env.ref(
- 'bemade_document_versions.document_revision_sequence_default')
- self.revision_ids = self.revision_ids or Command.set(
- [self._create_first_revision()])
+ self.revision_sequence = self.revision_sequence or self._create_rev_sequence(
+ sequence_prefix, sequence_suffix, sequence_padding)
+ self.revision_ids = self.revision_ids or [Command.set(
+ [self._create_first_revision(initial_revision_name).id])]
- def _create_first_revision(self):
+ def _create_rev_sequence(self, sequence_prefix: str = None,
+ sequence_suffix: str = None, sequence_padding: int = None):
+ """
+ Helper method to create a new sequence for a document that we are just starting
+ to track revisions for.
+
+ :param sequence_prefix: The prefix to use for the sequence.
+ :param sequence_suffix: The suffix to use for the sequence.
+ :param sequence_padding: The padding to use for the sequence.
+ :return: The created ir.sequence record.
+ """
+ return self.env['ir.sequence'].create({
+ 'document_id': self.id,
+ 'name': f'sequence_doc{self.id}',
+ 'prefix': sequence_prefix,
+ 'suffix': sequence_suffix,
+ 'padding': sequence_padding,
+ 'implementation': 'standard',
+ })
+
+ def _create_first_revision(self, name: str = None):
"""
Creates a new documents.revision record to associate to this Document
+
+ :param name: Optional name to use for the newly created revision.
:return: a single, new documents.revision record
"""
self.ensure_one()
- return self.env['documents.revision'].create({'document_id': self.id, })
+ name = name or self.revision_sequence.get_next_char(0)
+ return self.env['documents.revision'].create({
+ 'document_id': self.id,
+ 'name': name,
+ 'attachment_id': self.attachment_id.id,
+ })
+
+ def get_next_revision_name(self):
+ """
+ :return str: The name of the next revision in the sequence or None if revision
+ tracking is not yet set up.
+ """
+ return self.revision_sequence.get_next_char(self.revision_sequence._next())
diff --git a/bemade_document_versions/models/document_revision.py b/bemade_document_versions/models/document_revision.py
index 70f333f..240d82f 100644
--- a/bemade_document_versions/models/document_revision.py
+++ b/bemade_document_versions/models/document_revision.py
@@ -4,7 +4,11 @@ from odoo import models, fields, _, api
class DocumentRevision(models.Model):
_name = "documents.revision"
_description = "Document Revision"
+ _sql_constraints = [
+ ('name_document_id_unique', 'unique (name,document_id)',
+ 'The revision name must be unique for each document.')]
+ name = fields.Char()
document_id = fields.Many2one('documents.document', required=True)
attachment_id = fields.Many2one('ir.attachment', required=True)
previous_revision_id = fields.Many2one('documents.revision', 'Previous Revision')
@@ -12,6 +16,16 @@ class DocumentRevision(models.Model):
@api.model_create_multi
def create(self, vals_list):
+ """
+ Creates one or more new document revisions. Each revision created replaces the
+ ir.attachment tied to the document with the one provided in this revision. It
+ also supersedes the previous revision, updating the document's
+ current_revision_id and carrying a link to the previous revision in
+ previous_revision_id.
+
+ :param vals_list: Dictionary or list of dictionaries
+ :return:
+ """
res = super().create(vals_list)
for rec in res:
# When we create a new revision, we need to replace the attachment linked
@@ -21,3 +35,4 @@ class DocumentRevision(models.Model):
# back to the previous revisions
rec.previous_revision_id = rec.document_id.current_revision_id
rec.document_id.current_revision_id = rec
+ return res
diff --git a/bemade_document_versions/models/document_revision_sequence.py b/bemade_document_versions/models/document_revision_sequence.py
index 167d645..dee825d 100644
--- a/bemade_document_versions/models/document_revision_sequence.py
+++ b/bemade_document_versions/models/document_revision_sequence.py
@@ -1,11 +1,12 @@
from odoo import models, fields, api, _
+from odoo.exceptions import UserError
class DocumentRevisionSequence(models.Model):
- """ Creates an independent sequence for each Document when the document is set to
- track
- """
- _name = 'documents.revision.sequence'
_inherit = 'ir.sequence'
+ # Would prefer to inherit and make a new model, but the way ir.sequence is
+ # implemented has a lot of hard coded references to "ir.sequence" for all the
+ # database operations.
- document_id = fields.Many2one('documents.document', 'Document', ondelete='cascade')
\ No newline at end of file
+ document_id = fields.Many2one('documents.document', 'Document',
+ ondelete='cascade')
diff --git a/bemade_document_versions/models/workflow.py b/bemade_document_versions/models/workflow.py
new file mode 100644
index 0000000..7a3ced9
--- /dev/null
+++ b/bemade_document_versions/models/workflow.py
@@ -0,0 +1,28 @@
+from odoo import models, fields, _
+from odoo.exceptions import UserError
+
+
+class WorkflowActionRuleRevision(models.Model):
+ _inherit = ['documents.workflow.rule']
+
+ create_model = fields.Selection(selection_add=[('documents.revision', "Revision")])
+
+ def create_record(self, documents=None):
+ rv = super().create_record(documents=documents)
+ if self.create_model == 'documents.revision':
+ if len(documents) != 1:
+ raise UserError(_('Document revisions must be added for one and '
+ 'only one document at a time.'))
+ ctx = {'document_id': documents[0].id}
+ ctx.update(self._context)
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'documents.revision.wizard',
+ 'name': 'New Revision',
+ 'target': 'new',
+ 'context': ctx,
+ 'views': [(self.env.ref(
+ 'bemade_document_versions.document_revision_wizard_view_form').id,
+ 'form')],
+ 'view_mode': 'form',
+ }
diff --git a/bemade_document_versions/security/ir.model.access.csv b/bemade_document_versions/security/ir.model.access.csv
new file mode 100644
index 0000000..a9ae138
--- /dev/null
+++ b/bemade_document_versions/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_document_revision_user,Document Revision Access,model_documents_revision,documents.group_documents_user,1,1,1,1
+access_document_revision_manager,Document Revision Manager Access,model_documents_revision,documents.group_documents_manager,1,1,1,1
+access_document_revision_wizard_user,Document Revision Wizard Access,model_documents_revision_wizard,documents.group_documents_user,1,1,1,1
+access_document_revision_wizard_manager,Document Revision Wizard Manager Access,model_documents_revision_wizard,documents.group_documents_manager,1,1,1,1
diff --git a/bemade_document_versions/views/document_views.xml b/bemade_document_versions/views/document_views.xml
new file mode 100644
index 0000000..d8d4049
--- /dev/null
+++ b/bemade_document_versions/views/document_views.xml
@@ -0,0 +1,12 @@
+
+
+
+ Upload Document Revision
+ documents.revision.wizard
+
+ action
+ list,form,kanban
+ form
+ new
+
+
\ No newline at end of file
diff --git a/bemade_document_versions/wizard/__init__.py b/bemade_document_versions/wizard/__init__.py
new file mode 100644
index 0000000..e8b027a
--- /dev/null
+++ b/bemade_document_versions/wizard/__init__.py
@@ -0,0 +1 @@
+from . import document_revision_wizard
diff --git a/bemade_document_versions/wizard/document_revision_wizard.py b/bemade_document_versions/wizard/document_revision_wizard.py
index 13a4995..e117594 100644
--- a/bemade_document_versions/wizard/document_revision_wizard.py
+++ b/bemade_document_versions/wizard/document_revision_wizard.py
@@ -1,4 +1,4 @@
-from odoo import models, fields, _
+from odoo import models, fields, _, api
from odoo.exceptions import UserError
@@ -7,13 +7,62 @@ class DocumentRevisionWizard(models.TransientModel):
_description = 'Allows the creation of new document revisions'
document_id = fields.Many2one('documents.document', 'Document')
+ document_name = fields.Char()
file = fields.Binary('File to upload')
- revision_name = fields.Char()
+ revision_name = fields.Char(required=True)
+ revision_sequence_prefix = fields.Char(string='Sequence Prefix')
+ revision_sequence_suffix = fields.Char(string='Sequence Suffix')
+ revision_sequence_padding = fields.Integer(string='Sequence Padding',
+ help="Number of digits to show in the"
+ " sequence number. A value of 2 will"
+ " generate sequence numbers like 01,"
+ " 02, 03, etc.")
def default_get(self, fields_list):
ctx = self._context
- if 'active_ids' in ctx and len(ctx.get('active_ids')) > 1:
- raise UserError(_('You can only create revisions for one document at a time.'))
- if 'active_id' not in ctx:
- raise UserError(_('You must select a document for which you are creating a revision.'))
- self.document_id = self.env['documents.document'].browse(ctx.get('active_id'))
+ vals = {}
+ if 'document_id' not in ctx:
+ raise UserError(
+ _('You must select a document for which you are creating a revision.'))
+ document = self.env['documents.document'].browse(ctx.get('document_id'))
+ vals['document_name'] = document.name
+ vals['document_id'] = document.id
+ sequence = document.revision_sequence or self.env.ref(
+ 'bemade_document_versions.document_revision_sequence_default')
+ if document.revision_sequence:
+ vals['revision_name'] = self.document_id.get_next_revision_name()
+ else:
+ vals['revision_name'] = sequence.get_next_char(0)
+ vals['revision_sequence_prefix'] = sequence.prefix
+ vals['revision_sequence_suffix'] = sequence.suffix
+ vals['revision_sequence_padding'] = sequence.padding
+ return vals
+
+ def action_upload_revision(self):
+ for wizard in self:
+ if not wizard.file:
+ raise UserError(_('You must upload a file.'))
+ if not wizard.revision_name:
+ raise UserError(_('Revision name cannot be empty.'))
+ prev_attachment = wizard.document_id.attachment_id
+ attachment = self.env['ir.attachment'].with_context(
+ {'no_document': True}).create({
+ 'name': prev_attachment.name,
+ 'datas': wizard.file,
+ 'res_model': prev_attachment.res_model,
+ 'res_id': prev_attachment.res_id,
+ 'company_id': prev_attachment.company_id.id,
+ 'public': prev_attachment.public,
+ })
+ if not wizard.document_id.revision_sequence:
+ wizard.document_id.set_up_revisions(wizard.revision_name,
+ wizard.revision_sequence_prefix,
+ wizard.revision_sequence_suffix,
+ wizard.revision_sequence_padding)
+ else:
+ self.env['documents.revision'].create({
+ 'document_id': wizard.document_id.id,
+ 'attachment_id': attachment.id,
+ 'name': wizard.revision_name,
+ 'attachment_id': attachment.id,
+ })
diff --git a/bemade_document_versions/wizard/document_revision_wizard.xml b/bemade_document_versions/wizard/document_revision_wizard.xml
new file mode 100644
index 0000000..4021061
--- /dev/null
+++ b/bemade_document_versions/wizard/document_revision_wizard.xml
@@ -0,0 +1,32 @@
+
+
+
+ bemade_document_versions.document.revision.wizard.form
+ documents.revision.wizard
+
+
+
+
+
\ No newline at end of file