Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openobject-server/trunk-default-filter-xmo into 
lp:openobject-server.

Requested reviews:
  Olivier Dony (OpenERP) (odo-openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-default-filter-xmo/+merge/106807

* Adds ``is_default`` attribute to ``ir.filters``
* Returns ``is_default`` from ``get_filters``
* Only one filter should be ``is_default`` per ``user_id``: on 
``create_or_update``, if setting ``is_default`` to true
  - if the filter has a ``user_id``, set all other filters to 
``is_default=False``
  - if the filter does not have a ``user_id`` and there is already a filter 
with no ``user_id`` and ``is_default`` set (which is not the one being 
updated), raise an error
* A few tests for these behaviors.
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-default-filter-xmo/+merge/106807
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-server/trunk-default-filter-xmo.
=== modified file 'openerp/addons/base/ir/ir_filters.py'
--- openerp/addons/base/ir/ir_filters.py	2012-05-16 16:31:22 +0000
+++ openerp/addons/base/ir/ir_filters.py	2012-05-22 13:13:24 +0000
@@ -19,6 +19,7 @@
 #
 ##############################################################################
 
+from openerp import exceptions
 from osv import osv, fields
 from tools.translate import _
 
@@ -41,13 +42,47 @@
     def get_filters(self, cr, uid, model):
         """Obtain the list of filters available for the user on the given model.
 
-        :return: list of :meth:`~osv.read`-like dicts containing the ``name``,
-            ``domain``, ``user_id`` (m2o tuple) and ``context`` of the matching ``ir.filters``.
+        :return: list of :meth:`~osv.read`-like dicts containing the
+            ``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple) and
+            ``context`` of the matching ``ir.filters``.
         """
         # available filters: private filters (user_id=uid) and public filters (uid=NULL) 
-        act_ids = self.search(cr, uid, [('model_id','=',model),('user_id','in',[uid, False])])
-        my_acts = self.read(cr, uid, act_ids, ['name', 'domain', 'context', 'user_id'])
-        return my_acts
+        filter_ids = self.search(cr, uid,
+            [('model_id','=',model),('user_id','in',[uid, False])])
+        my_filters = self.read(cr, uid, filter_ids,
+            ['name', 'is_default', 'domain', 'context', 'user_id'])
+        return my_filters
+
+    def _check_global_default(self, cr, uid, vals, matching_filters, context=None):
+        """ _check_global_default(cursor, UID, dict, list(dict), dict) -> None
+
+        Checks if there is a global default for the model_id requested.
+
+        If there is, and the default is different than the record being written
+        (-> we're not updating the current global default), raise an error
+        to avoid users unknowingly overwriting existing global defaults (they
+        have to explicitly remove the current default before setting a new one)
+
+        This method should only be called if ``vals`` is trying to set
+        ``is_default``
+
+        :raises openerp.exceptions.Warning: if there is an existing default and
+                                            we're not updating it
+        """
+        existing_default = self.search(cr, uid, [
+            ('model_id', '=', vals['model_id']),
+            ('user_id', '=', False),
+            ('is_default', '=', True)], context=context)
+
+        if not existing_default: return
+        if matching_filters and \
+           (matching_filters[0]['id'] == existing_default[0]):
+            return
+
+        raise exceptions.Warning(
+            _("There is already a global filter set as default for %(model)s, delete or change it before setting a new default") % {
+                'model': vals['model_id']
+            })
 
     def create_or_replace(self, cr, uid, vals, context=None):
         lower_name = vals['name'].lower()
@@ -57,11 +92,25 @@
                                 # f.user_id is False and vals.user_id is False or missing,
                                 # or f.user_id.id == vals.user_id
                                 if (f['user_id'] and f['user_id'][0]) == vals.get('user_id', False)]
+
+        if vals.get('is_default'):
+            if vals.get('user_id'):
+                act_ids = self.search(cr, uid, [
+                        ('model_id', '=', vals['model_id']),
+                        ('user_id', '=', vals['user_id']),
+                        ('is_default', '=', True),
+                    ], context=context)
+                self.write(cr, uid, act_ids, {'is_default': False}, context=context)
+            else:
+                self._check_global_default(
+                    cr, uid, vals, matching_filters, context=None)
+
         # When a filter exists for the same (name, model, user) triple, we simply
         # replace its definition.
         if matching_filters:
             self.write(cr, uid, matching_filters[0]['id'], vals, context)
             return matching_filters[0]['id']
+
         return self.create(cr, uid, vals, context)
 
     _sql_constraints = [
@@ -87,11 +136,13 @@
         'domain': fields.text('Domain', required=True),
         'context': fields.text('Context', required=True),
         'model_id': fields.selection(_list_all_models, 'Model', required=True),
+        'is_default': fields.boolean('Default filter')
     }
     _defaults = {
         'domain': '[]',
         'context':'{}',
         'user_id': lambda self,cr,uid,context=None: uid,
+        'is_default': False
     }
 
 ir_filters()

=== modified file 'openerp/tests/__init__.py'
--- openerp/tests/__init__.py	2012-03-01 13:46:08 +0000
+++ openerp/tests/__init__.py	2012-05-22 13:13:24 +0000
@@ -11,10 +11,12 @@
 import test_expression
 import test_ir_sequence
 import test_orm
+from . import test_ir_filters
 
 fast_suite = [
     test_ir_sequence,
-    ]
+    test_ir_filters
+]
 
 checks = [
     test_expression,

=== modified file 'openerp/tests/common.py'
--- openerp/tests/common.py	2012-03-01 13:46:08 +0000
+++ openerp/tests/common.py	2012-05-22 13:13:24 +0000
@@ -14,7 +14,7 @@
 HOST = '127.0.0.1'
 
 ADMIN_USER = 'admin'
-ADMIN_USER_ID = 1
+ADMIN_USER_ID = openerp.SUPERUSER_ID
 ADMIN_PASSWORD = 'admin'
 
 def start_openerp():

=== added file 'openerp/tests/test_ir_filters.py'
--- openerp/tests/test_ir_filters.py	1970-01-01 00:00:00 +0000
+++ openerp/tests/test_ir_filters.py	2012-05-22 13:13:24 +0000
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+import functools
+
+import openerp
+from openerp import exceptions
+from . import common
+
+class Fixtures(object):
+    def __init__(self, *args):
+        self.fixtures = args
+
+    def __call__(self, fn):
+        @functools.wraps(fn)
+        def wrapper(case):
+            for model, vars in self.fixtures:
+                case.registry(model).create(
+                    case.cr, common.ADMIN_USER_ID, vars, {})
+
+            fn(case)
+        return wrapper
+def fixtures(*args):
+    return Fixtures(*args)
+
+def noid(d):
+    """ Removes `id` key from a dict so we don't have to keep these things
+    around when trying to match
+    """
+    if 'id' in d: del d['id']
+    return d
+
+class TestGetFilters(common.TransactionCase):
+    USER_ID = 3
+    USER = (3, u'Demo User')
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='c', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='d', user_id=USER_ID, model_id='ir.filters')),
+    )
+    def test_own_filters(self):
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='b', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+            dict(name='d', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='c', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='d', user_id=False, model_id='ir.filters')),
+    )
+    def test_global_filters(self):
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='b', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='d', is_default=False, user_id=False, domain='[]', context='{}'),
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', user_id=common.ADMIN_USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='c', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='d', user_id=common.ADMIN_USER_ID, model_id='ir.filters')),
+    )
+    def test_no_third_party_filters(self):
+        filters = self.registry('ir.filters').get_filters(
+            self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', is_default=False, user_id=False, domain='[]', context='{}'),
+            dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}'),
+        ])
+
+class TestOwnDefaults(common.TransactionCase):
+    USER_ID = 3
+    USER = (3, u'Demo User')
+
+    def test_new_no_filter(self):
+        """
+        When creating a @is_default filter with no existing filter, that new
+        filter gets the default flag
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'a',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=True,
+                 domain='[]', context='{}')
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', user_id=USER_ID, model_id='ir.filters')),
+    )
+    def test_new_filter_not_default(self):
+        """
+        When creating a @is_default filter with existing non-default filters,
+        the new filter gets the flag
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', is_default=True, user_id=USER_ID, model_id='ir.filters')),
+    )
+    def test_new_filter_existing_default(self):
+        """
+        When creating a @is_default filter where an existing filter is already
+        @is_default, the flag should be *moved* from the old to the new filter
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=USER_ID, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', is_default=True, user_id=USER_ID, model_id='ir.filters')),
+    )
+    def test_update_filter_set_default(self):
+        """
+        When updating an existing filter to @is_default, if an other filter
+        already has the flag the flag should be moved
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'a',
+            'model_id': 'ir.filters',
+            'user_id': self.USER_ID,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=self.USER, is_default=True, domain='[]', context='{}'),
+            dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}'),
+        ])
+
+class TestGlobalDefaults(common.TransactionCase):
+    USER_ID = 3
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', user_id=False, model_id='ir.filters')),
+    )
+    def test_new_filter_not_default(self):
+        """
+        When creating a @is_default filter with existing non-default filters,
+        the new filter gets the flag
+        """
+        Filters = self.registry('ir.filters')
+        Filters.create_or_replace(self.cr, self.USER_ID, {
+            'name': 'c',
+            'model_id': 'ir.filters',
+            'user_id': False,
+            'is_default': True,
+        })
+        filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
+
+        self.assertItemsEqual(map(noid, filters), [
+            dict(name='a', user_id=False, is_default=False, domain='[]', context='{}'),
+            dict(name='b', user_id=False, is_default=False, domain='[]', context='{}'),
+            dict(name='c', user_id=False, is_default=True, domain='[]', context='{}'),
+        ])
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', is_default=True, user_id=False, model_id='ir.filters')),
+    )
+    def test_new_filter_existing_default(self):
+        """
+        When creating a @is_default filter where an existing filter is already
+        @is_default, an error should be generated
+        """
+        Filters = self.registry('ir.filters')
+        with self.assertRaises(exceptions.Warning):
+            Filters.create_or_replace(self.cr, self.USER_ID, {
+                'name': 'c',
+                'model_id': 'ir.filters',
+                'user_id': False,
+                'is_default': True,
+            })
+
+    @fixtures(
+        ('ir.filters', dict(name='a', user_id=False, model_id='ir.filters')),
+        ('ir.filters', dict(name='b', is_default=True, user_id=False, model_id='ir.filters')),
+    )
+    def test_update_filter_set_default(self):
+        """
+        When updating an existing filter to @is_default, if an other filter
+        already has the flag an error should be generated
+        """
+        Filters = self.registry('ir.filters')
+
+        with self.assertRaises(exceptions.Warning):
+            Filters.create_or_replace(self.cr, self.USER_ID, {
+                'name': 'a',
+                'model_id': 'ir.filters',
+                'user_id': False,
+                'is_default': True,
+            })

=== modified file 'openerp/tests/test_ir_sequence.py'
--- openerp/tests/test_ir_sequence.py	2012-03-14 14:25:52 +0000
+++ openerp/tests/test_ir_sequence.py	2012-05-22 13:13:24 +0000
@@ -22,18 +22,59 @@
 def cursor():
     return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
 
-class test_ir_sequence_standard(unittest2.TestCase):
+class SequenceCase(unittest2.TestCase):
+    """
+    Base case for stateful testing of sequence objects (until we have fixtures?)
+
+    * Provides easy-to-call methods for creating new ir.sequence and
+      ir.sequence.type (``make_seq`` and ``make_typ``)
+    * Cleanup of all sequences and sequence types created via the two functions
+      above
+    """
+    typ_ids = None
+    seq_ids = None
+
+    @classmethod
+    def setUpClass(cls):
+        cls.typ_ids = []
+        cls.seq_ids = []
+
+    @classmethod
+    def tearDownClass(cls):
+        cr = cursor()
+
+        registry('ir.sequence.type').unlink(cr, ADMIN_USER_ID, cls.typ_ids)
+        registry('ir.sequence').unlink(cr, ADMIN_USER_ID, cls.seq_ids)
+
+        cr.commit()
+        cr.close()
+
+    @classmethod
+    def make_seq(cls, cr, vars):
+        seq = registry('ir.sequence').create(cr, ADMIN_USER_ID, vars, {})
+        cls.seq_ids.append(seq)
+        return seq
+
+    @classmethod
+    def make_typ(cls, cr, vars):
+        typ = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, vars, {})
+        cls.typ_ids.append(typ)
+        return typ
+
+class test_ir_sequence_standard(SequenceCase):
     """ A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
 
     def test_ir_sequence_create(self):
         """ Try to create a sequence object. """
         cr = cursor()
+
         d = dict(code='test_sequence_type', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
         d = dict(code='test_sequence_type', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
+
         cr.commit()
         cr.close()
 
@@ -66,18 +107,19 @@
         cr0.close()
         cr1.close()
 
-class test_ir_sequence_no_gap(unittest2.TestCase):
+class test_ir_sequence_no_gap(SequenceCase):
     """ Copy of the previous tests for a 'No gap' sequence. """
 
     def test_ir_sequence_create_no_gap(self):
         """ Try to create a sequence object. """
         cr = cursor()
         d = dict(code='test_sequence_type_2', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
+
         d = dict(code='test_sequence_type_2', name='Test sequence',
             implementation='no_gap')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
         cr.commit()
         cr.close()
@@ -105,24 +147,24 @@
         cr0.close()
         cr1.close()
 
-class test_ir_sequence_change_implementation(unittest2.TestCase):
+class test_ir_sequence_change_implementation(SequenceCase):
     """ Create sequence objects and change their ``implementation`` field. """
 
     def test_ir_sequence_1_create(self):
         """ Try to create a sequence object. """
         cr = cursor()
         d = dict(code='test_sequence_type_3', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
         d = dict(code='test_sequence_type_3', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
         d = dict(code='test_sequence_type_4', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
         d = dict(code='test_sequence_type_4', name='Test sequence',
             implementation='no_gap')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
         cr.commit()
         cr.close()
@@ -146,17 +188,17 @@
         cr.commit()
         cr.close()
 
-class test_ir_sequence_generate(unittest2.TestCase):
+class test_ir_sequence_generate(SequenceCase):
     """ Create sequence objects and generate some values. """
 
     def test_ir_sequence_create(self):
         """ Try to create a sequence object. """
         cr = cursor()
         d = dict(code='test_sequence_type_5', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
         d = dict(code='test_sequence_type_5', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
         cr.commit()
         cr.close()
@@ -171,10 +213,10 @@
         """ Try to create a sequence object. """
         cr = cursor()
         d = dict(code='test_sequence_type_6', name='Test sequence type')
-        c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_typ(cr, d)
         assert c
         d = dict(code='test_sequence_type_6', name='Test sequence')
-        c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+        c = self.make_seq(cr, d)
         assert c
         cr.commit()
         cr.close()

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to