bemade_document_versions: successfully uploading a new revision via the Kanban view.

This commit is contained in:
Marc Durepos 2023-10-05 15:51:36 -04:00
parent f0f92cf483
commit a987afd5d3
13 changed files with 221 additions and 66 deletions

View file

@ -1,2 +1,3 @@
from . import models
from . import wizard

View file

@ -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,
}

View file

@ -1,12 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="documents.revision.sequence" id="document_revision_sequence_default">
<record model="ir.sequence" id="document_revision_sequence_default">
<field name="name">Default Document Revision Sequence</field>
<field name="code">document.revision</field>
<field name="implementation">standard</field>
<field name="prefix">DOC/</field>
<field name="padding">2</field>
</record>
<record id="document_revision_workflow_rule" model="documents.workflow.rule">
<field name="sequence">0</field>
<field name="name">Revise</field>
<field name="domain_folder_id" ref="documents.documents_internal_folder"/>
<field name="create_model">documents.revision</field>
</record>
</data>
</odoo>

View file

@ -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

View file

@ -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())

View file

@ -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

View file

@ -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')
document_id = fields.Many2one('documents.document', 'Document',
ondelete='cascade')

View file

@ -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',
}

View file

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_document_revision_user Document Revision Access model_documents_revision documents.group_documents_user 1 1 1 1
3 access_document_revision_manager Document Revision Manager Access model_documents_revision documents.group_documents_manager 1 1 1 1
4 access_document_revision_wizard_user Document Revision Wizard Access model_documents_revision_wizard documents.group_documents_user 1 1 1 1
5 access_document_revision_wizard_manager Document Revision Wizard Manager Access model_documents_revision_wizard documents.group_documents_manager 1 1 1 1

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_upload_revision" model="ir.actions.act_window">
<field name="name">Upload Document Revision</field>
<field name="res_model">documents.revision.wizard</field>
<field name="binding_model_id" ref="documents.model_documents_document"/>
<field name="binding_type">action</field>
<field name="binding_view_types">list,form,kanban</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View file

@ -0,0 +1 @@
from . import document_revision_wizard

View file

@ -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,
})

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="document_revision_wizard_view_form" model="ir.ui.view">
<field name="name">bemade_document_versions.document.revision.wizard.form</field>
<field name="model">documents.revision.wizard</field>
<field name="arch" type="xml">
<form>
<header>
<field name="document_id" invisible="1"/>
</header>
<sheet>
<group>
<group string="File">
<field name="file"/>
<field name="revision_name"/>
</group>
<group string="Sequence Numbering Setup">
<field name="revision_sequence_prefix"/>
<field name="revision_sequence_suffix"/>
<field name="revision_sequence_padding"/>
</group>
</group>
</sheet>
<footer>
<button string="Upload" name="action_upload_revision" type="object"
class="btn-primary"/>
<button special="cancel" string="Cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
</odoo>