bemade_document_versions: successfully uploading a new revision via the Kanban view.
This commit is contained in:
parent
f0f92cf483
commit
a987afd5d3
13 changed files with 221 additions and 66 deletions
|
|
@ -1,2 +1,3 @@
|
||||||
from . import models
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,11 @@
|
||||||
'website': 'https://www.bemade.org',
|
'website': 'https://www.bemade.org',
|
||||||
'license': 'OPL-1',
|
'license': 'OPL-1',
|
||||||
'depends': ['documents', 'mail'],
|
'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,
|
'installable': True,
|
||||||
'auto_install': False,
|
'auto_install': False,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<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="name">Default Document Revision Sequence</field>
|
||||||
<field name="code">document.revision</field>
|
<field name="code">document.revision</field>
|
||||||
<field name="implementation">standard</field>
|
<field name="implementation">standard</field>
|
||||||
<field name="prefix">DOC/</field>
|
<field name="prefix">DOC/</field>
|
||||||
<field name="padding">2</field>
|
<field name="padding">2</field>
|
||||||
</record>
|
</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>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
from . import document_revision
|
|
||||||
from . import document
|
from . import document
|
||||||
from . import document_sequence
|
from . import document_revision
|
||||||
|
from . import document_revision_sequence
|
||||||
|
from . import workflow
|
||||||
|
|
|
||||||
|
|
@ -3,68 +3,68 @@ from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class Document(models.Model):
|
class Document(models.Model):
|
||||||
_inherit = ['documents.document']
|
_inherit = 'documents.document'
|
||||||
|
|
||||||
current_revision_id = fields.Many2one('documents.revision', 'Current Revision')
|
current_revision_id = fields.Many2one('documents.revision', 'Current Revision')
|
||||||
revision_ids = fields.One2many('documents.revision', 'document_id')
|
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', )
|
'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 set_up_revisions(self, initial_revision_name: str = None,
|
||||||
def constrain_revisions(self):
|
sequence_prefix: str = None,
|
||||||
for rec in self:
|
sequence_suffix: str = None, sequence_padding: int = None):
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
Helper method to set up revisions, no matter how we got into tracking them.
|
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.ensure_one()
|
||||||
self.track_revisions = True # may already be true, but no matter
|
self.revision_sequence = self.revision_sequence or self._create_rev_sequence(
|
||||||
self.revision_sequence = self.revision_sequence or self.env.ref(
|
sequence_prefix, sequence_suffix, sequence_padding)
|
||||||
'bemade_document_versions.document_revision_sequence_default')
|
self.revision_ids = self.revision_ids or [Command.set(
|
||||||
self.revision_ids = self.revision_ids or Command.set(
|
[self._create_first_revision(initial_revision_name).id])]
|
||||||
[self._create_first_revision()])
|
|
||||||
|
|
||||||
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
|
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
|
:return: a single, new documents.revision record
|
||||||
"""
|
"""
|
||||||
self.ensure_one()
|
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())
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@ from odoo import models, fields, _, api
|
||||||
class DocumentRevision(models.Model):
|
class DocumentRevision(models.Model):
|
||||||
_name = "documents.revision"
|
_name = "documents.revision"
|
||||||
_description = "Document 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)
|
document_id = fields.Many2one('documents.document', required=True)
|
||||||
attachment_id = fields.Many2one('ir.attachment', required=True)
|
attachment_id = fields.Many2one('ir.attachment', required=True)
|
||||||
previous_revision_id = fields.Many2one('documents.revision', 'Previous Revision')
|
previous_revision_id = fields.Many2one('documents.revision', 'Previous Revision')
|
||||||
|
|
@ -12,6 +16,16 @@ class DocumentRevision(models.Model):
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
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)
|
res = super().create(vals_list)
|
||||||
for rec in res:
|
for rec in res:
|
||||||
# When we create a new revision, we need to replace the attachment linked
|
# 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
|
# back to the previous revisions
|
||||||
rec.previous_revision_id = rec.document_id.current_revision_id
|
rec.previous_revision_id = rec.document_id.current_revision_id
|
||||||
rec.document_id.current_revision_id = rec
|
rec.document_id.current_revision_id = rec
|
||||||
|
return res
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class DocumentRevisionSequence(models.Model):
|
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'
|
_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')
|
||||||
|
|
|
||||||
28
bemade_document_versions/models/workflow.py
Normal file
28
bemade_document_versions/models/workflow.py
Normal 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',
|
||||||
|
}
|
||||||
5
bemade_document_versions/security/ir.model.access.csv
Normal file
5
bemade_document_versions/security/ir.model.access.csv
Normal 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
|
||||||
|
12
bemade_document_versions/views/document_views.xml
Normal file
12
bemade_document_versions/views/document_views.xml
Normal 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>
|
||||||
1
bemade_document_versions/wizard/__init__.py
Normal file
1
bemade_document_versions/wizard/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import document_revision_wizard
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from odoo import models, fields, _
|
from odoo import models, fields, _, api
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,13 +7,62 @@ class DocumentRevisionWizard(models.TransientModel):
|
||||||
_description = 'Allows the creation of new document revisions'
|
_description = 'Allows the creation of new document revisions'
|
||||||
|
|
||||||
document_id = fields.Many2one('documents.document', 'Document')
|
document_id = fields.Many2one('documents.document', 'Document')
|
||||||
|
document_name = fields.Char()
|
||||||
file = fields.Binary('File to upload')
|
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):
|
def default_get(self, fields_list):
|
||||||
ctx = self._context
|
ctx = self._context
|
||||||
if 'active_ids' in ctx and len(ctx.get('active_ids')) > 1:
|
vals = {}
|
||||||
raise UserError(_('You can only create revisions for one document at a time.'))
|
if 'document_id' not in ctx:
|
||||||
if 'active_id' not in ctx:
|
raise UserError(
|
||||||
raise UserError(_('You must select a document for which you are creating a revision.'))
|
_('You must select a document for which you are creating a revision.'))
|
||||||
self.document_id = self.env['documents.document'].browse(ctx.get('active_id'))
|
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,
|
||||||
|
})
|
||||||
|
|
|
||||||
32
bemade_document_versions/wizard/document_revision_wizard.xml
Normal file
32
bemade_document_versions/wizard/document_revision_wizard.xml
Normal 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>
|
||||||
Loading…
Reference in a new issue