Allow copying attachments and notes to created records

issue9154
review266971002
This commit is contained in:
Cédric Krier
2020-04-13 17:25:02 +02:00
parent ad4a2bdf75
commit 0081dc431d
10 changed files with 202 additions and 14 deletions
+1
View File
@@ -1,3 +1,4 @@
* Allow copying attachments and notes to created records
* Add link button on form
* Support explicit delete and remove for saving and on_change xxx2Many
* Add export_data_domain to ModelStorage
+10 -4
View File
@@ -3,15 +3,14 @@
from sql import Null
from sql.operators import Concat
from trytond.i18n import lazy_gettext
from ..model import ModelView, ModelSQL, fields
from ..transaction import Transaction
from ..pyson import Eval
from .resource import ResourceMixin
from .resource import ResourceMixin, resource_copy
from ..config import config
__all__ = [
'Attachment',
]
__all__ = ['AttachmentCopyMixin']
def firstline(description):
@@ -102,3 +101,10 @@ class Attachment(ResourceMixin, ModelSQL, ModelView):
@fields.depends('description')
def on_change_with_summary(self, name=None):
return firstline(self.description or '')
class AttachmentCopyMixin(
resource_copy(
'ir.attachment', 'attachments',
lazy_gettext('ir.msg_attachments'))):
pass
+9
View File
@@ -302,5 +302,14 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_timedelta_s">
<field name="text">s</field>
</record>
<record model="ir.message" id="msg_resource_copy_help">
<field name="text">The resources to which this record must be copied.</field>
</record>
<record model="ir.message" id="msg_attachments">
<field name="text">Attachments</field>
</record>
<record model="ir.message" id="msg_notes">
<field name="text">Notes</field>
</record>
</data>
</tryton>
+8 -2
View File
@@ -5,14 +5,15 @@ from textwrap import TextWrapper
from sql import Null
from sql.conditionals import Case
from trytond.i18n import lazy_gettext
from ..model import ModelView, ModelSQL, ModelStorage, fields
from ..pool import Pool
from ..transaction import Transaction
from ..tools import grouped_slice, reduce_ids
from ..pyson import Eval
from .resource import ResourceMixin
from .resource import ResourceMixin, resource_copy
__all__ = ['Note', 'NoteRead']
__all__ = ['NoteCopyMixin']
class Note(ResourceMixin, ModelSQL, ModelView):
@@ -115,3 +116,8 @@ class NoteRead(ModelSQL):
ondelete='CASCADE')
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
class NoteCopyMixin(
resource_copy('ir.note', 'notes', lazy_gettext('ir.msg_notes'))):
pass
+77 -3
View File
@@ -2,18 +2,28 @@
# this repository contains the full copyright notices and license terms.
from sql.conditionals import Coalesce
from ..model import ModelSQL, ModelView, fields
from trytond.i18n import lazy_gettext
from ..model import ModelStorage, ModelView, fields
from ..pool import Pool
from ..transaction import Transaction
from ..pyson import Eval
__all__ = ['ResourceMixin']
__all__ = ['ResourceMixin', 'resource_copy']
class ResourceMixin(ModelSQL, ModelView):
class ResourceMixin(ModelStorage, ModelView):
resource = fields.Reference('Resource', selection='get_models',
required=True, select=True)
copy_to_resources = fields.MultiSelection(
'get_copy_to_resources', "Copy to Resources",
states={
'invisible': ~Eval('copy_to_resources_visible'),
},
depends=['copy_to_resources_visible'])
copy_to_resources_visible = fields.Function(
fields.Boolean("Copy to Resources Visible"),
'on_change_with_copy_to_resources_visible')
last_user = fields.Function(fields.Char('Last User',
states={
'invisible': ~Eval('last_user'),
@@ -43,6 +53,24 @@ class ResourceMixin(ModelSQL, ModelView):
access = ModelAccess.get_access([m.model for m in models])
return [(m.model, m.name) for m in models if access[m.model]['read']]
@fields.depends('resource')
def get_copy_to_resources(self):
pool = Pool()
Model = pool.get('ir.model')
resources = []
if isinstance(self.resource, ResourceCopyMixin):
models = self.resource.get_resources_to_copy(self.__name__)
if models:
models = Model.search([
('model', 'in', models),
])
resources.extend((m.model, m.name) for m in models)
return resources
@fields.depends(methods=['get_copy_to_resources'])
def on_change_with_copy_to_resources_visible(self, name=None):
return bool(self.get_copy_to_resources())
def get_last_user(self, name):
return (self.write_uid.rec_name if self.write_uid
else self.create_uid.rec_name)
@@ -103,3 +131,49 @@ class ResourceMixin(ModelSQL, ModelView):
records = super(ResourceMixin, cls).create(vlist)
cls.check_access([r.id for r in records], mode='create')
return records
class ResourceCopyMixin(ModelStorage):
@classmethod
def get_resources_to_copy(cls, name):
return set()
def resource_copy(resource, name, string):
class _ResourceCopyMixin(ResourceCopyMixin):
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
else:
default = default.copy()
default.setdefault(name, None)
return super().copy(records, default=default)
def copy_resources_to(self, target):
pool = Pool()
Resource = pool.get(resource)
try:
super().copy_resources_to(target)
except AttributeError:
pass
to_copy = []
for record in getattr(self, name):
if (record.copy_to_resources
and target.__name__ in record.copy_to_resources):
to_copy.append(record)
if to_copy:
return Resource.copy(to_copy, default={
'resource': str(target),
'copy_to_resources': None,
})
setattr(_ResourceCopyMixin, name, fields.One2Many(
resource, 'resource', string,
help=lazy_gettext('ir.msg_resource_copy_help')))
return _ResourceCopyMixin
+2
View File
@@ -16,6 +16,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="type"/>
<label name="link"/>
<field name="link" widget="url"/>
<label name="copy_to_resources"/>
<field name="copy_to_resources" yexpand="0"/>
</group>
<field name="data" widget="image" readonly="1"/>
<notebook>
+7 -5
View File
@@ -1,14 +1,16 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="6">
<form>
<label name="resource"/>
<field name="resource" colspan="5"/>
<field name="resource"/>
<label name="unread"/>
<field name="unread"/>
<label name="last_user"/>
<field name="last_user"/>
<label name="last_modification"/>
<field name="last_modification"/>
<label name="unread"/>
<field name="unread"/>
<field name="message" colspan="6"/>
<label name="copy_to_resources"/>
<field name="copy_to_resources" yexpand="0"/>
<field name="message" colspan="4"/>
</form>
+2
View File
@@ -40,6 +40,7 @@ def register():
from . import modelview
from . import mptt
from . import multivalue
from . import resource
from . import rule
from . import tree
from . import trigger
@@ -78,6 +79,7 @@ def register():
modelview.register('tests')
mptt.register('tests')
multivalue.register('tests')
resource.register('tests')
rule.register('tests')
tree.register('tests')
trigger.register('tests')
+27
View File
@@ -0,0 +1,27 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL
from trytond.pool import Pool
from trytond.ir.note import NoteCopyMixin
class TestResource(ModelSQL, NoteCopyMixin):
"Test Resource"
__name__ = 'test.resource'
@classmethod
def get_resources_to_copy(cls, name):
return {'test.resource.other'}
class TestResourceOther(ModelSQL):
"Test Resource Other"
__name__ = 'test.resource.other'
def register(module):
Pool.register(
TestResource,
TestResourceOther,
module=module, type_='model')
+59
View File
@@ -0,0 +1,59 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import unittest
from trytond.pool import Pool
from trytond.tests.test_tryton import activate_module, with_transaction
class ResourceTestCase(unittest.TestCase):
"Test Resource"
@classmethod
def setUpClass(cls):
activate_module('tests')
@with_transaction()
def test_resources_copied(self):
"Test resources are copied"
pool = Pool()
Resource = pool.get('test.resource')
Other = pool.get('test.resource.other')
Note = pool.get('ir.note')
record = Resource()
record.save()
note = Note(resource=record, copy_to_resources=[Other.__name__])
note.save()
other = Other()
other.save()
copies = record.copy_resources_to(other)
other_notes = Note.search([('resource', '=', str(other))])
self.assertTrue(other_notes)
self.assertEqual(len(other_notes), 1)
self.assertEqual(other_notes, copies)
@with_transaction()
def test_resources_not_copied(self):
"Test resources are not copied"
pool = Pool()
Resource = pool.get('test.resource')
Other = pool.get('test.resource.other')
Note = pool.get('ir.note')
record = Resource()
record.save()
note = Note(resource=record)
note.save()
other = Other()
other.save()
copies = record.copy_resources_to(other)
other_notes = Note.search([('resource', '=', str(other))])
self.assertFalse(other_notes)
self.assertFalse(copies)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(ResourceTestCase)