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)

Reply via email to