changeset aeb7307bf72c in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=aeb7307bf72c
description:
Allow copying attachments and notes to created records
issue9154
review266971002
diffstat:
CHANGELOG | 1 +
trytond/ir/attachment.py | 14 ++++-
trytond/ir/message.xml | 9 ++++
trytond/ir/note.py | 10 +++-
trytond/ir/resource.py | 80 +++++++++++++++++++++++++++++++++++-
trytond/ir/view/attachment_form.xml | 2 +
trytond/ir/view/note_form.xml | 12 +++--
trytond/tests/__init__.py | 2 +
trytond/tests/resource.py | 27 ++++++++++++
trytond/tests/test_resource.py | 59 +++++++++++++++++++++++++++
10 files changed, 202 insertions(+), 14 deletions(-)
diffs (349 lines):
diff -r 45787f01dc3e -r aeb7307bf72c CHANGELOG
--- a/CHANGELOG Mon Apr 13 12:21:55 2020 +0200
+++ b/CHANGELOG Mon Apr 13 17:25:02 2020 +0200
@@ -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
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/attachment.py
--- a/trytond/ir/attachment.py Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/attachment.py Mon Apr 13 17:25:02 2020 +0200
@@ -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 @@
@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
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/message.xml
--- a/trytond/ir/message.xml Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/message.xml Mon Apr 13 17:25:02 2020 +0200
@@ -302,5 +302,14 @@
<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>
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/note.py
--- a/trytond/ir/note.py Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/note.py Mon Apr 13 17:25:02 2020 +0200
@@ -5,14 +5,15 @@
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 @@
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
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/resource.py
--- a/trytond/ir/resource.py Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/resource.py Mon Apr 13 17:25:02 2020 +0200
@@ -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 @@
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 @@
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
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/view/attachment_form.xml
--- a/trytond/ir/view/attachment_form.xml Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/view/attachment_form.xml Mon Apr 13 17:25:02 2020 +0200
@@ -16,6 +16,8 @@
<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>
diff -r 45787f01dc3e -r aeb7307bf72c trytond/ir/view/note_form.xml
--- a/trytond/ir/view/note_form.xml Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/ir/view/note_form.xml Mon Apr 13 17:25:02 2020 +0200
@@ -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>
diff -r 45787f01dc3e -r aeb7307bf72c trytond/tests/__init__.py
--- a/trytond/tests/__init__.py Mon Apr 13 12:21:55 2020 +0200
+++ b/trytond/tests/__init__.py Mon Apr 13 17:25:02 2020 +0200
@@ -40,6 +40,7 @@
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 @@
modelview.register('tests')
mptt.register('tests')
multivalue.register('tests')
+ resource.register('tests')
rule.register('tests')
tree.register('tests')
trigger.register('tests')
diff -r 45787f01dc3e -r aeb7307bf72c trytond/tests/resource.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/tests/resource.py Mon Apr 13 17:25:02 2020 +0200
@@ -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')
diff -r 45787f01dc3e -r aeb7307bf72c trytond/tests/test_resource.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/tests/test_resource.py Mon Apr 13 17:25:02 2020 +0200
@@ -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)