Allow copying attachments and notes to created records
issue9154 review266971002
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user